如何使用Ember为Solr服务构建类似Google的搜索界面

时间:2013-09-10 03:57:33

标签: ember.js

我是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. 添加'&amp; json.wrf =?'在Solr URL的末尾,您可以避免因XSS安全性而在浏览器中阻止呼叫。

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

    <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}}助手。

  6. 对于未来的读者,建议的解决方案中的评论只是一个小的修正:

        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的needs属性。但是,在最后一个例子中,我最终从输入控制器中删除了对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。在“动作”属性中包装事件处理程序的上述更改似乎是在RC6之后引入的。如果您使用的是最新版本的Ember(如您所愿),那么您的(事件)代码将无法在JSBin中工作(使用默认的Ember库)。但是,如果您不使用动作包装器,那么您的代码将无法在浏览器中运行。嗯...见github.com/emberjs/ember.js/releases

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

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

    • 我将搜索事件处理程序从InputController移动到 包含TextSearchController,以便解耦 来自SearchResultsController的InputController。
    • 我也删除了 初始搜索页面加载,因此用户以空白平板开始。
    • 最后,我删除了TextSearch对象,并移动了solr 而是集成到TextSearchController。

    最终版本可在此处找到: http://jsbin.com/ezExeCI/1/

    非常感谢你的帮助,杰里米。没有你,我无法做到。我会确保支付它。

    非常感谢!

1 个答案:

答案 0 :(得分: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

  3. 另外值得注意的是,modelafterModel是在Route上调用的回调,而不是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头文件。

    幸运的是,Solr可以通过向URL添加&json.wrf=?来发送不受跨域限制约束的jsonp。 jQuery会自动为您处理jsonp响应。 “不要使用视图”jsbin包含jsonp修复和一些其他随机位,我不得不调整以使工作正常。 (http://jsbin.com/aNowEZO/1/edit