Problem

问题

You’d like to redraw your views every few seconds/minutes e.g. to update relative timestamps (like on twitter.com).

有时视图需要每隔几秒或者几分钟重绘一次。例如更新相对时间(如同twitter.com那样)。

Solution

解决方案

Have a clock object with a pulse attribute in your application which increments using a timed interval. You want to let view(s) bind values to be refreshed when the pulse attribute increments.

应用中有一个时钟对象,该对象有一pulse属性定时递增。希望将视图与pulse绑定,并在其增加时得到刷新。

The clock object can be used to create new instances for binding to new views generated within the application, like a list of comments.

时钟对象可以创建用于绑定到应用生成的新视图的对象,比如评论列表。

Discussion

讨论

Cookbook: Continuous Redrawing of Views

Cookbook: 持续重绘视图

ClockService object

ClockService对象

This ClockService is an example of an object that may come from a library. And, is injected into the application via an initializer.

这里ClockService只是用于作为一个例子,它可能来自于一个第三方库。并且通过初始化注入到应用中。

During initialization the tick method is called which uses Ember.run.later with a time of 250 milliseconds as the interval. A property is set at the end of the interval. Since the tick method observes the incremented property another interval is triggered each time the property increases.

在初始化过程中,tick方法通过Ember.run.later每250毫秒被调用一次。当到时间时,会设置一个属性。由于tick方法观察了一个递增属性,并且每次属性增加时另外一个周期也同时被触发。

  1. var ClockService = Ember.Object.extend({
  2. pulse: Ember.computed.oneWay('_seconds').readOnly(),
  3. tick: function () {
  4. var clock = this;
  5. Ember.run.later(function () {
  6. var seconds = clock.get('_seconds');
  7. if (typeof seconds === 'number') {
  8. clock.set('_seconds', seconds + (1/4));
  9. }
  10. }, 250);
  11. }.observes('_seconds').on('init'),
  12. _seconds: 0,
  13. });

Binding to the pulse attribute

绑定至pulse属性

In this recipe, an application initializer is used to inject an instance of the ClockService object, setting a controller’s clock property to this instance.

在本技巧中,应用在初始化时注入了一个ClockService的实例,并且将一个控制器的clock属性设置为该实例。

  1. Ember.Application.initializer({
  2. name: 'clockServiceInitializer',
  3. initialize: function(container, application) {
  4. container.register('clock:service', ClockService);
  5. application.inject('controller:interval', 'clock', 'clock:service');
  6. }
  7. });

The controller can set any computed properties based on the pulse property of the injected clock instance.

该控制器可以基于注入的clock实例的pulse属性来设置任意属性。

In this case the seconds property is bound to the pulse property of the controller’s clock. The property clock.pulse was injected during initialization.

本例中seconds属性绑定到控制器的clock对象的pulse属性。属性clock.pulse`在初始化时被注入。

The controller has (session) data to display seconds to visitors, as well as a handful of properties used as conditions in the Handlebars template.

控制器有(会话)数据来显示seconds给访问者,而且少数用于Handlebars模板的条件属性。

  1. App.IntervalController = Ember.ObjectController.extend({
  2. secondsBinding: 'clock.pulse',
  3. fullSecond: function () {
  4. return (this.get('seconds') % 1 === 0);
  5. }.property('seconds'),
  6. quarterSecond: function () {
  7. return (this.get('seconds') % 1 === 1/4);
  8. }.property('seconds'),
  9. halfSecond: function () {
  10. return (this.get('seconds') % 1 === 1/2);
  11. }.property('seconds'),
  12. threeQuarterSecond: function () {
  13. return (this.get('seconds') % 1 === 3/4);
  14. }.property('seconds')
  15. });

A controller for a list of comments, each comment will have a new clock instance when added to the list. The comment item controller sets up the seconds binding, used by the template to show the time since the comment was created.

一个评论列表的控制器,每条评论在被添加到列表时,会得到一个新的时钟实例。评论条目控制器设置seconds绑定,用于在模板中显示评论创建了多长时间。

  1. App.CommentItemController = Ember.ObjectController.extend({
  2. seconds: Ember.computed.oneWay('clock.pulse').readOnly()
  3. })
  4. App.CommentsController = Ember.ArrayController.extend({
  5. needs: ['interval'],
  6. itemController: 'commentItem',
  7. actions: {
  8. add: function () {
  9. this.addObject(Em.Object.create({
  10. comment: $('#comment').val(),
  11. clock: ClockService.create()
  12. }));
  13. }
  14. }
  15. });

Handlebars template which displays the pulse

显示pulse的Handlebars模板

The seconds value is computed from the pulse attribute. And the controller has a few properties to select a component to render, fullSecond, quarterSecond, halfSecond, threeQuarterSecond.

seconds的值是从pulse属性计算得来的。控制器有些属性是用来选择一个控件来渲染的,fullSecondquarterSecondhalfSecondthreeQuarterSecond

  1. {{#if fullSecond}}
  2. {{nyan-start}}
  3. {{/if}}
  4. {{#if quarterSecond}}
  5. {{nyan-middle}}
  6. {{/if}}
  7. {{#if halfSecond}}
  8. {{nyan-end}}
  9. {{/if}}
  10. {{#if threeQuarterSecond}}
  11. {{nyan-middle}}
  12. {{/if}}
  13. <h3>You&apos;ve nyaned for {{digital_clock seconds}} (h:m:s)</h3>
  14. {{render 'comments'}}

A template for a list of comments

评论列表的一个模板:

  1. <input type="text" id="comment" />
  2. <button {{action add}}>Add Comment</button>
  3. <ul>
  4. {{#each}}
  5. <li>{{comment}} ({{digital_clock clock.pulse}})</li>
  6. {{/each}}
  7. </ul>

Handlebars helper to format the clock display (h:m:s)

格式化时钟显示(h:m:s)的Handlebars助手

This helper is used in the template like so {{digital_clock seconds}}, seconds is the property of the controller that will be displayed (h:m:s).

本助手在模板中这样使用:{{digital_clock seconds}}seconds是控制器要显示的一个属性(h:m:s)。

  1. Ember.Handlebars.registerBoundHelper('digital_clock', function(seconds) {
  2. var h = Math.floor(seconds / 3600);
  3. var m = Math.floor((seconds % 3600) / 60);
  4. var s = Math.floor(seconds % 60);
  5. var addZero = function (number) {
  6. return (number < 10) ? '0' + number : '' + number;
  7. };
  8. var formatHMS = function(h, m, s) {
  9. if (h > 0) {
  10. return '%@:%@:%@'.fmt(h, addZero(m), addZero(s));
  11. }
  12. return '%@:%@'.fmt(m, addZero(s));
  13. };
  14. return new Ember.Handlebars.SafeString(formatHMS(h, m, s));
  15. });

Note

注意

To explore the concept further, try adding a timestamp and updating the clock’s pulse by comparing the current time. This would be needed to update the pulse property when a user puts his/her computer to sleep then reopens their browser after waking.

为了深入探究这个概率,可以尝试添加一个时间戳,并通过与当前时间比较来更新时钟的pulse。在用户将计算机设置为睡眠后,再唤醒后重新打开浏览器时也应该更新pulse属性。

Links

链接

The source code:

源代码:

Further reading:

更多相关内容: