3

私は Ember を初めて使用し、Google のような検索インターフェイスを Solr API に実装しようとしています。つまり、入力時に結果がページ上で自動的に更新されるようにしたいと考えています。

私が最初に考えたのは、SearchInput と SearchResults の 2 つの子ビューを持つ、再利用可能な TextSearch CollectionView を作成することでした。

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 promise が解決されるのを待つ必要があります。

    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. ビュー、レンダー、およびコントローラーの違いを認識していませんでした (十分に文書化されているわけではありませんが、Google 検索でわかります)。しかし、これらは「部分的」ヘルパーとどのように関係しているのでしょうか? わからない。

  3. 「&json.wrf=?」を追加して Solr URL の末尾に追加すると、XSS セキュリティのためにブラウザーで呼び出しがブロックされるのを回避できます。

  4. ビューからモデルを参照する方法について、私は非常に混乱しました。これが正しい方法です (この例の場合):

    <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}} ヘルパーを使用してください。

将来の読者のために、提案されたソリューションのコメントに対する1つのマイナーな修正:

    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 を使用します。'action' 属性でイベント ハンドラーをラップする上記の変更は、RC6 以降に導入されたようです。Ember の最新バージョンを使用している場合 (必要に応じて)、(イベント) コードは (デフォルトの Ember ライブラリを使用して) JSBin で動作しません。ただし、アクション ラッパーを使用しない場合、コードはブラウザーで動作しません。うーん... github.com/emberjs/ember.js/releases を参照してください

以下の JSBin の例では、イベント ハンドラーから「action:」タグを削除したため、アプリが実行されます。

また、いくつかの小さなデザイン変更を加えました。

  • InputController を SearchResultsController から分離するために、代わりに検索イベント ハンドラを InputController からそれを含む TextSearchController に移動しました。
  • また、ページ読み込み時の最初の検索を削除したため、ユーザーは白紙の状態から始めます。
  • 最後に、TextSearch オブジェクトを削除し、代わりに solr 統合を TextSearchController に移動しました。

最終版はhttp://jsbin.com/ezExeCI/1/にあります。

あなたの助けに感謝します、ジェレミー。あなたなしではできなかった。必ず先払いにします。

どうもありがとう!

4

1 に答える 1

1

あなたは jsbin にかなり近づいており、修正に取り組むには 2 つの異なる方法があります。

  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プロパティの両方が に存在する必要があり、Ember によって使用されることApp.IndexControllerはないため、 and は必要App.SearchInputControllerありません。App.SearchResultsController

    これは、それを示す jsbin です: http://jsbin.com/EMexuXU/1/edit

modelまた、とはではなく でafterModel呼び出されるコールバックであることも注目に値します。RouteController

最後に、クロスドメインの制限により、「Continue to use views」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 に追加することで、クロスドメイン制限の制約を受けない jsonp を送信でき&json.wrf=?ます。jQuery は jsonp 応答を自動的に処理します。「ビューを使用しない」jsbin には、jsonp の修正と、機能させるために微調整しなければならなかったその他のいくつかのランダムなビットが含まれています。( http://jsbin.com/aNowEZO/1/edit )

于 2013-09-10T06:12:00.867 に答える