我是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
非常感谢你的帮助,杰里米,这是我需要的所有信息。辉煌。
以下是一些经验教训:
在使用结果之前,您必须等待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;
}
我没有意识到视图,渲染和控制器之间的区别(它并没有完全记录,但谷歌搜索会告诉你)。但这些与“部分”助手有何关系?不确定。
添加'&amp; json.wrf =?'在Solr URL的末尾,您可以避免因XSS安全性而在浏览器中阻止呼叫。
关于如何从视图中引用模型,我对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>
我的事件没有被视图控制器捕获,因为在模板中使用{{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的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:'标记,因此应用程序将运行。
我还做了一些小的设计变更:
最终版本可在此处找到: http://jsbin.com/ezExeCI/1/
非常感谢你的帮助,杰里米。没有你,我无法做到。我会确保支付它。
非常感谢!
答案 0 :(得分:1)
你的jsbin相当接近,有两种方法可以解决这个问题。
请勿直接使用视图,而是使用render
。通常,您不希望直接实例化视图。执行此操作时,您不会为每个视图添加不同的控制器。相反,您的每个视图最终都会连接到渲染开始的主控制器。在这种情况下App.IndexController
。比使用{{view App.TextSearchView}}
直接实例化视图更好的方法是使用{{render "textSearch"}}
。使用render
意味着您可以获得支持每个不同视图/模板的控制器。如果您将search
方法放在App.SearchInputController
上,那么它就会被调用。
以下是基于render
的jsbin:http://jsbin.com/aNowEZO/1/edit
继续使用观看次数。如果您真的想继续直接实例化视图,请注意上下文中的控制器是您的App.IndexController
。在这种情况下,您的search
方法和query
属性都需要存在App.IndexController
,而您不需要App.SearchInputController
和App.SearchResultsController
,因为它们永远不会被Ember使用。
这是一个jsbin,显示:http://jsbin.com/EMexuXU/1/edit
另外值得注意的是,model
和afterModel
是在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)