访问异步加载和呈现数据的DOM元素(Ember)

时间:2014-07-23 09:53:23

标签: jquery ember.js render

在我的ember应用程序中,我使用异步加载的数据(使用ember数据和rest-adapter)与把手一起显示。加载和渲染数据后,我想操纵这些数据的呈现方式(计算它们所在的位置)。这也需要在视口大小调整上完成。

目前,我正试图让这项工作采用中提出的方法 How to catch whether array was inserted in handlebar in Ember?Run jquery at the end of Ember.CollectionView rendering。 我甚至尝试使用schedule而不是scheduleOnce导致只调用一次display update方法,但仍然没有呈现异步加载的数据,并且在加载和呈现数据后方法不会再次调用。

export default Ember.View.extend({
templateName: 'calendar',
didInsertElement: function() {
    this._super();
    this.$('#calender_button_new').click(function(){
        document.location.href= "index.html#/add-appointment/select-employee";
    });
    this.$('#calender_button_home').click(function(){
        document.location.href= "index.html#/home";
    });
},

updateCalendar: function() {
    console.log(this.$('.appointment').length);
    var view = this;
    this.$('.appointment').each(function(index, item){
        var id = $(item).attr('data-appointment');
        view.controller.store.find('appointment', id).then(function(appointment){
            var beginning_base = Math.floor(appointment.get('beginning'));
            var end_base = Math.floor(appointment.get('end'));
            // get cell where appointment starts, each cell can be identified by the attributes data-time and data-employee
            var beginning_block = $('td[data-employee="' + appointment.get('doneBy').get('id') + '"][data-time="' + beginning_base + '"]');
            var end_block = $('td[data-employee="' + appointment.get('doneBy').get('id') + '"][data-time="' + end_base + '"]');
            // calculate exact position in the cell when the appointment starts, one cell covers an hour but the appointment might not start at full hour
            var beginning_offset = beginning_block.outerHeight()*(appointment.get('beginning')-beginning_base);
            var end_offset = end_block.outerHeight()*(appointment.get('end')-end_base);
            $(item).css({top:beginning_block.offset().top+beginning_offset-59, 
                        left:beginning_block.offset().left, 
                        width: beginning_block.outerWidth(), 
                        height:end_block.offset().top-beginning_block.offset().top+end_offset, 
                        background:'red'});
        });
    });
},

init: function() {
    this._super();
    $(window).bind('resize', $.proxy(this.updateCalendar, this));
},

willDestroy: function() {
    this._super();
    $(window).unbind('resize');
}

});

通过这种方法,它不应该是必须要注意实现的承诺(?)。另一方面,这是我能想到为什么这不起作用的唯一原因。 (注意:在手动调整窗口大小时,一切正常。只是初始渲染给我带来麻烦。) 我实际上要听哪个事件让它起作用?

修改

也许我应该提到我使用ArrayController,视图是数组元素的容器。 因此结构是:

 -Calendar (ArrayController, View with afterRender event)
 -Day (Item of Calendar, does not have a view - uses calendar view/template to be displayed)

calendar.hbs-template的相关部分是:

{{#each}}
    {{#each appointment in appointments}}
         <div class="appointment" {{bind-attr data-appointment="appointment.id"}}>{{appointment.title}}</div>
    {{/each}}
{{/each}}

日历模型只是请求日期(CalendarRoute的一部分)

export default Ember.Route.extend({
model: function() {
    return this.store.find('Day');
},
});

日模型本身:

export default DS.Model.extend({
date: DS.attr('string'),
appointments: DS.hasMany('Appointment', {
    async: true,
    inverse: null
}),
employees: DS.hasMany('Employee', {
    async: true,
    inverse: null
}),
opening: DS.attr('number'),
closing: DS.attr('number')
});

约会模式

export default DS.Model.extend({
title:  DS.attr('string'),
comment: DS.attr('string'),
doneBy: DS.belongsTo('Employee'),
beginning: DS.attr('number'),
end: DS.attr('number')
});

日模型由控制器支持,但不包含任何相关内容(仅根据日期模型中的开始和结束时间创建时间段的计算字段)。

编辑2

我更新了所有源代码。如果您想知道这是怎么回事,请访问http://arshaw.com/fullcalendar/(周视图)。而不是在不同的日子里,我让员工在y轴上。计划没有拖放,因此我只使用整整几个小时(而不是30分钟)。

2 个答案:

答案 0 :(得分:1)

经过一番研究后,我终于在几天前想出了如何解决这个问题。在这个来源(http://yoranbrondsema.com/loading-animations-asynchronous-models-ember-js/)中,它完全是我所寻找的,但用例略有不同。即使他们的用例的解决方案可能不是最好的(如他们的评论中提到的,如果可能的话,更好地使用加载路由)。但是,我可以将代码用于我的设置。关键部分是我的数据中的hasMany-relation与异步加载。所以我所要做的就是在hasMany-relation解析之后运行我的jQuery代码,然后在下一次渲染之后调用我的代码:

<强> CalendarRoute

setupController: function(controller, day) {
    this._super(controller, day);
    // Pre-load the comments
    // The 'get' call will result in an AJAX call to get
    // the comments and returns a promise
    Ember.RSVP.makePromise = function(maybePromise) {
        // Test if it's a promise
        if (maybePromise.then) {
            // Then return it
            return maybePromise;
        } else {
            // Wrap it in a Promise that resolves directly
            return Ember.RSVP.resolve(maybePromise);
        }
    };
    var appointments = day.get('appointments');

    // Wait until the promise has been resolved
    appointments.then(function() { 
        // Wait until all templates have finished rendering
        Ember.run.scheduleOnce('afterRender', this, function() {
            // Here goes my jQuery code
        }
    }
}

答案 1 :(得分:0)

在Ember中观察数组的长度就像观察property.[]一样简单。考虑到JS正在写DOM,你永远不应该像你想要的那样观看DOM!

第1部分:绑定位置

我认为一个好的,Emberish方法是对arrayController中的每个项使用itemController,并将style属性绑定到div.appointment或任何想要重新定位的元素。 style属性本身将是基于返回数据的计算属性。这是一个简化的例子:

让我们假设元素的位置以某种方式相对于月份。

App.CalendarController = Em.ArrayController.extend({
  itemController: 'day'
});

App.DayController = Em.ObjectController.extend({
  positioning: function() { 
    var top = 'top: ' + this.get('month') / 2 + 'px;';
    var left = 'left: ' + this.get('month') * 5 + 'px;';

    return top + left;
  }.property('month'),
});

你的模板:

{{#each controller}}
  <div {{bind-attr style=positioning}}>{{title}}</div>
{{/each}}

或者,您可以将Em.ColectionView与itemViewClass一起使用。

第2部分:重新计算调整大小

对于窗口调整大小,利用百分比定位可能意味着您不必注意调整大小,您可以使用位置和负百分比边距相对于其宽度移动元素(即创建相对于窗口的位置移动和/或元素宽度与css)。但是,这不是JS解决方案。

相反,基于Ember的解决方案是使用以下方法触发事件,对其进行去抖(因此您不会进行过多调用)并重新计算属性 - 请注意:以下尚未适应您决定管理视图/控制器的任何方式,但理论是正确和适用的。同样,这是一个简化的例子:

App.IndexView = Em.View.extend({
  positionTrigger: false,

  watchForResize: function() {
    $(window).bind('resize', $.proxy(this.resize, this));
  }.on('didInsertElement'),

  resize: function() {
    // No repeat calls within 200ms
    Em.run.debounce(this, this.toggleProperty, 'positionTrigger', 200);
  },

  positioning: function() {
    // Whatever you want to do to the position here
    var top = 'top: ' + this.get('month') / 2 + 'px;';
    var left = 'left: ' + this.get('month') * 5 + 'px;';

    return top + left; // Updates style binding in template
  }.property('positionTrigger', 'month'),
});

请注意:组合上述两种方法的方式可能有所不同(例如,在集合中使用itemViewClass或在itemController中完成所有操作),但您将能够实现您想要的效果如何通过组合第一部分和第二部分的方法来实现。

根据用例的不同,您可能只想让positionTrigger属性上的观察者执行其他操作。