EmberJS + EmberData:如何从服务器监听模型加载

时间:2015-04-18 11:25:39

标签: ember.js ember-data

环境:

Ember.VERSION = 1.7.1

DS.VERSION = 1.0.0-beta.14.1

问题: 我试图在最初的应用程序加载时最终呈现视图时运行一些代码,问题是模型尚未加载,因为客户端从服务器获取它。

所以发生的流程(我猜)是这样的:

  1. Ember渲染视图,请求模型
  2. Model返回一个promise,但无论如何都会呈现视图
  3. AJAX完成后,模型会更新,视图会重新呈现
  4. 我想在第3步完成后运行一些代码(工具提示的bootstrap init),但我真的不确定如何。

    只是要指出,当我离开并返回到同一路径从服务器获取模型后,一切正常(因为模型从商店返回而不是从服务器返回)

    到目前为止我尝试了什么:

    How can catch event after template is rendered at EmberJS?

    App.ApartmentsView = Ember.View.extend({
       didInsertElement: function () {
           $('[data-toggle="tooltip"]').tooltip();
       }
    });
    

    这没有用,因为此时模型长度为0

    听取模特改变:

    App.ApartmentsRoute = Ember.Route.extend({
       model: function () {
           return this.store.filter('apartment').then(function (model) {
               console.log(model);
               return model;
           });
       }
    });
    

    此时,model.content.length = 0此时我猜这个商店只是创建了模型,并且只在稍后更新它。

    我也尝试过监听控制器中的arrayContentDidChange属性,但此时模型还没有渲染,我可以使用超时,但我认为这是一个糟糕的解决方案。

    UPDATE1: 这是模板:

    <script type="text/x-handlebars" data-template-name="apartments" id="apartments">
    .
    .
    {{#each }}
       <tr>
          <td>{{rating}}</td>
          <td>{{price}}</td>
          <td>{{street}} {{building}} {{city}}</td>
          <td class="text-right hidden-print">
          <button type="button" class="btn btn-default" aria-label="Left Align" data-toggle="tooltip" data-placement="top" title="Navigate" {{ action 'navigate' }}>
             <span class="glyphicon glyphicon-transfer" aria-hidden="true"></span>
          </button>
          <button type="button" class="btn btn-default" aria-label="Left Align" data-toggle="tooltip" data-placement="top" title="Center Apartment" {{ action 'centerApartment' }}>
             <span class="glyphicon glyphicon-screenshot" aria-hidden="true"></span>
          </button>
          <button type="button" class="btn btn-default" aria-label="Left Align" data-toggle="tooltip" data-placement="top" title="NOT WORKING" {{ action 'setTime' }}>
             <span class="glyphicon glyphicon-time" aria-hidden="true"></span>
          </button>
          <button type="button" class="btn btn-default" aria-label="Left Align" data-toggle="tooltip" data-placement="top" title="NOT WORKING" {{ action 'share' }}>
             <span class="glyphicon glyphicon-share-alt" aria-hidden="true"></span>
          </button>
          {{#link-to 'apartment' this classNames="btn btn-default"}}<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span>{{/link-to}}
          <div class="btn btn-default" data-toggle="tooltip" data-placement="top" title="Delete Apartment" {{action 'deleteApartment' }}>X</div>
          </td>
        </tr>
    {{/each}}
    .
    .
    
    </script>
    

    有什么建议吗?

3 个答案:

答案 0 :(得分:2)

您对生命周期的描述不正确。它应该是:

  1. Ember进入路线。

  2. 调用路由的model挂钩。

  3. 解决了model挂钩的返回值。

  4. 继续进行转换,包括呈现模板并调用其didInsertElement挂钩。

  5. 问题是你的模型钩子正在从过滤器返回一个promise,但是在那时,正如你所发现的那样,商店里还没有记录,因此没有任何过滤。我在您的问题中遗漏的是您何时以及如何期望从服务器加载公寓。 filter不会为你做那件事。出于我们的目的,我们假设您有一个例程,返回一个名为getApartments的promise,它确保从服务器加载必要的模型。也许它就像

    一样简单
    getApartments: function() {
        return this.store.find('apartments');
    }
    

    然后用这样的东西替换你的model钩子:

    model: function () {
      return this.getApartments() .
        then(function(model) {
          return this.store.filter('apartment', filterfunc);
        })
      ;
    }
    

    现在Ember会等到getApartments的承诺得到解决,然后使用过滤后的结果作为模型,然后渲染,这样你想要的元素就可以在didInsertElement钩子中进行操作了

    但是,如果稍后将其他公寓添加到商店,这仍然会失败,因为didInsertElement已在添加元素时运行。要解决这个问题,我建议将您的逻辑移动到一个组件中。无论如何,这可能是一种很好的方法来计算代码。

      

    部件/显示-公寓/ template.hbs

    <tr>
      <td>{{content.rating}}</td>
      ...
      <button type="button" class="btn btn-default" aria-label="Left Align" data-toggle="tooltip" data-placement="top" title="Navigate" {{ action 'navigate' content}}>
      ...
    </tr>
    

    然后,在组件的JS中,初始化didInsertElement中的工具提示:

      

    部件/显示-公寓/ component.js

    export default Ember.Component.extend({
    
      didInsertElement: function() {
        $(this).find('[data-toggle="tooltip"]').tooltip();
      }
    
    });
    

    然后在顶级模板中,使用新组件显示每个公寓:

    {{#each apartment in model}}
      {{show-apartment content=apartment navigate=navigate}}
    {{/each}}
    

    现在,每次新公寓进入商店时,过滤后的模型都会反映出来,{{#each}}将生成新条目,新组件将被创建和呈现,didInsertElement运行该组件的逻辑以初始化该特定工具提示。

    顺便说一句,你的model挂钩调用store.filter没有过滤功能。你想用它做什么?

    使用isLoaded是一种黑客和代码气味,在承诺的土地上是不必要的。

答案 1 :(得分:0)

如果您希望在加载模型后才能渲染某些内容,那么我猜您可以在模板中检查isLoaded键。像这样的东西

  

template.hbs

{{#if model.isLoaded}}
     All the HTML stuff that will be shown only if model is loaded.
{{/if}}

答案 2 :(得分:0)

正如@torazaburo已提到的,问题出在过滤器功能中。简单

model : function(){
  return this.store.filter('apartment', {}, function(item){
    return true;
  });
}

会对你有用。显然,这类似于this.store.find('apartment')

实际上这是一个简单的黑客(包括空对象作为查询),以便在过滤之前从服务器获取数据。

但是,为了确保工具提示在新添加的元素上初始化,@torazaburo提到了更好的方法

DEMO