3

我是 Ember 的新手,正在尝试为 Solr API 实现类似 Google 的搜索界面。即我希望结果在您键入时在页面上自动更新。

我的第一个想法是创建一个可重用的 TextSearch CollectionView,它有两个子视图 SearchInput 和 SearchResults。

输入 SearchInput 将触发一个事件,该事件将被 TextSearch CollectionView 捕获,这将更新 SearchResult 子元素。

我得到了生成的事件,但 TextSearch ContainerView 无法使用操作、函数或事件管理器拦截事件。但是,出于某种原因,我可以拦截 IndexController 中的事件,但我需要在可重用的 TextSearch CollectionView 中处理它(我认为)。

最后,我无法更新 SearchResults 中的视图(当我尝试在 IndexController 中处理事件时),并且 SearchResult 模型在页面加载时没有触发。

我在 Ember 很新,所以我确定我在这里做了一些愚蠢的事情。达到这一点还有很长的路要走。

任何建议将不胜感激!

粘贴箱:http : //jsbin.com/ezomOkO/3/edit

HTML:

<script type="text/x-handlebars">
  <h2>Welcome to Ember.js</h2>
  {{outlet}}
</script>

<script type="text/x-handlebars" data-template-name="index">
  {{view App.TextSearchView }}
</script>

<script type="text/x-handlebars" data-template-name="textSearch">
  {{view App.SearchInputView }}
  {{view App.SearchResultsView }}
</script>

<script type="text/x-handlebars" data-template-name="searchInput">
  {{input type="text" value=query size="50"}}
</script>  

<script type="text/x-handlebars" data-template-name="searchResults" >
  <ul>
    {{#each concept}}
      <li>{{title}}</li>
    {{else}}
      Sorry, nobody is here. 
    {{/each}}
  </ul>

Javascript:

App = Ember.Application.create();

App.ApplicationController = Ember.Controller.extend({
  appName: 'Snomed Search'
});

App.Router.map(function() {
  this.route("index", {path: "/"});
});

// INDEX
App.IndexRoute = Ember.Route.extend({
});
App.IndexController = Ember.Controller.extend({
});

//TEXT SEARCH
App.TextSearchView = Ember.View.extend({
  actions:{
    search: function(search) {
      this.get('controllers.searchResults').set('model', App.TextSearch.find(search));
      return false;
    }
  },
  needs: "searchResults",
  templateName: 'textSearch'
});

// SEARCH INPUT
App.SearchInputController  = Ember.Controller.extend({  
  query: 'Family'
});

App.SearchInputView = Ember.View.extend({
  templateName: 'searchInput',
  keyUp: function(evt) {
    this.get('controller').send('search', this.get('controller.query'));
  }
});

// SEARCH RESULTS
App.SearchResultsController  = Ember.Controller.extend({
  model: function(){
    return App.TextSearch.find('Family');
  },
  afterModel: function(posts, transitions){
    alert('model');
  },
  isPublic: true
});

App.SearchResultsView = Ember.View.extend({
  templateName: 'searchResults'
});


App.SearchResults = Ember.Object.extend({
  total: 0,
  start: 0,
  concepts: Ember.A()
});

App.Concept = Ember.Object.extend({
  id: null,
  title: null,
  active: null,
  effectiveTime: null
});

App.TextSearch = Ember.Object.extend({});

App.TextSearch.reopenClass({
  find: function(searchString){
    return Ember.Deferred.promise(function(p) {
      p.resolve($.getJSON("http://solr.sparklingideas.co.uk/solr/concept/select?q=title:" + searchString + "&wt=json&indent=true")
        .then(function(solr) {
            var returned = App.SearchResults.create();
            returned.total = solr.response.numFound;
            returned.start = solr.response.start;
            solr.response.docs.forEach(function (doc) {
              var concept = App.Concept.create();
              concept.id = doc.id;
              concept.title = doc.title;
              concept.active = doc.active;
              concept.effectiveTime= doc.effectiveTime;
              returned.concepts.push(concept);
            });
            return returned;
        }) //then
      );//resolve
    });//deferred promise
  }//find
});//reopen


更新

非常感谢您的帮助,杰里米,这就是我需要的所有信息。杰出的。

以下是一些经验教训:

  1. 在使用结果之前,您必须等待 JSON 承诺解决,如下所示:

    search: function(search) {
      var results = App.TextSearch.find(search);
      var _this = this;
      // results is a jquery promise, wait for it to resolve
      // Ember can't resolve it automatically
      results.then(function(results){
        _this.get('controllers.searchResults').set('model', results);
      });
      return false;
    }
    
  2. 我不知道视图、渲染和控制器之间的区别(它的文档并不全,但谷歌搜索会告诉你)。但是这些与“部分”助手有什么关系呢?没有把握。

  3. 通过添加 '&json.wrf=?' 在 Solr URL 的末尾,您可以避免由于 XSS 安全性而在浏览器中阻止调用。

  4. 我对如何从视图中引用模型的 ember 文档感到非常困惑。这是正确的方法(对于这个例子):

    <script type="text/x-handlebars" data-template-name="searchResults" >
      Total : {{model.total}}
      <ul>
        {{#each model.concepts}}
          <li>{{title}}</li>
        {{else}}
          Sorry, nobody is here. 
        {{/each}}
      </ul>
    </script>
    
  5. 我的事件没有被视图控制器捕获,因为通过在模板中使用 {{view}} 助手,没有控制器(!)。改用 {{render}} 助手。

对于未来的读者,只需对提议的解决方案中的评论进行一个小的更正:

    App.SearchResultsController  = Ember.Controller.extend({
      // This controller could be removed.
      // Ember will auto generate one for you.
    });

实际上,删除此控制器会导致 Javascript 错误:

Assertion failed: <App.TextSearchController:ember242> needs controller:searchResults but it does not exist 

因为 SearchInputController 的需求属性。不过,在最后一个示例中,我最终从输入控制器中删除了对 searchResults 的依赖。

我相信 Ember 现在需要将事件处理程序包装在 'actions: {}' 参数中,如下所示:

    actions:{
      search: function(search) {
        var results = App.TextSearchController.find(search);
        var _this = this;
        // results is a jquery promise, wait for it to resolve
        // Ember can't resolve it automatically
        results.then(function(results){
          _this.get('controllers.searchResults').set('model', results);
        });
        return false;
      }
    }


请注意:

JSBin 使用了几个月前的 EmbeJS RC6。上面对在 'action' 属性中包装事件处理程序的更改似乎是在 RC6 之后引入的。如果您使用的是最新版本的 Ember(如您所愿),那么您的(事件)代码将无法在 JSBin 中工作(使用默认的 Ember 库)。但是,如果您不使用操作包装器,那么您的代码将无法在您的浏览器中运行。嗯……见github.com/emberjs/ember.js/releases

因此,在下面的 JSBin 示例中,我从事件处理程序中删除了“action:”标签,因此应用程序将运行。

我还做了一些小的设计更改:

  • 我将搜索事件处理程序从 InputController 移动到包含 TextSearchController,以便将 InputController 与 SearchResultsController 分离。
  • 我还删除了页面加载时的初始搜索,因此用户从空白开始。
  • 最后,我删除了 TextSearch 对象,并将 solr 集成移至 TextSearchController。

最终版本可以在这里找到:http: //jsbin.com/ezExeCI/1/

杰里米,非常感谢您的帮助。没有你我做不到。我一定会付清的。

非常感谢!

4

1 回答 1

1

您在 jsbin 中相当接近,并且有两种不同的方法可以解决此问题。

  1. 不要直接使用视图,render而是使用。通常,您不想直接实例化视图。当你这样做时,你最终不会为每个视图使用不同的控制器。相反,您的每个视图最终都连接到开始渲染的主控制器。在这种情况下App.IndexController。比直接实例化视图更好的{{view App.TextSearchView}}是使用{{render "textSearch"}}. 使用render意味着您获得支持每个不同视图/模板的控制器。如果你把search方法放在App.SearchInputController上面,它就会被调用。

    这是一个render基于 jsbin 的:http: //jsbin.com/aNowEZO/1/edit

  2. 继续使用视图。如果您真的想继续直接实例化视图,请注意上下文中的控制器是您的App.IndexController. 在这种情况下,您的search方法和您的query属性都需要存在于 上App.IndexController,而您不需要App.SearchInputControllerApp.SearchResultsController因为它们从未被 Ember 使用过。

    这是一个 jsbin 显示:http: //jsbin.com/EMexuXU/1/edit

另外值得注意的是modelandafterModel是在 a 上调​​用的回调Route,而不是 a Controller

最后,您会看到由于跨域限制,搜索实际上在“继续使用视图”jsbin 中不起作用。您将看到此错误:XMLHttpRequest cannot load http://solr.sparklingideas.co.uk/solr/concept/select?q=title:Familys&wt=json&indent=true. Origin http://run.jsbin.com is not allowed by Access-Control-Allow-Origin. 要解决此问题,您需要从“solr.sparklingideas.co.uk”提供您的应用程序,或者您需要让您的 Solr 服务器发送适当的 CORS 标头。

&json.wrf=?好在 Solr 可以通过添加到 URL来发送不受跨域限制的 jsonp 。jQuery 会自动为您处理 jsonp 响应。“不要使用视图”jsbin 包含 jsonp 修复和一些其他随机位,我必须对其进行调整才能使事情正常运行。(http://jsbin.com/aNowEZO/1/edit

于 2013-09-10T06:12:00.867 回答