Scalable Vector Ember

November 24th, 2014

Fresh from emberGarten in Toronto I bring you the latest on SVG in Ember.js, metal-views, and HTMLBars:

In Ember.js 1.8, we’ve added full support for SVG. This means you can author templates of SVG and make SVG-element components. Demo here, and code here. Zero graphic design skills were involved in the making of this demo, but you get the point!

SVG is slowly rising in usage as IE8 finally drops from the list of commonly supported browsers. To fully support SVG in Ember templates, 1.8 tackled a few tough challenges:

  • Switching namespaces and element creation methods. Ember transparently swaps into the SVG namespace, and even swaps back out to HTML for foreignObject tags.
  • Track contextual elements. In 1.8, each executed template is a string which must be converted into DOM with innerHTML. To properly support components and nested templates inside an SVG inline document, we’ve created a system to track the contextual (parent) element of a given template. This also helps us implement fixes for broken DOM APIs, and handle complex edge cases in the spec such as optionally omitted start tags (like tbody).

But the big story of metal views, and HTMLBars, is the slow migration of Ember from a string based rendering pipeline to a DOM based one.

  • pre-1.8 Ember templates returned strings, and the rendering pipeline assembled strings. This is akin to how a server-side framework would generate HTML.
  • In 1.8-1.9, Ember templates are strings individually converted to DOM, then folded into a DOM based rendering tree.
  • In 1.10+, Ember will introduce the DOM based HTMLBars templating engine. This completes our migration from string composition to DOM.

The nominal goals of this transition are to introduce new syntaxes (<li class="{{someClass}}">) and improve performance (less strings means less GC pressure).

DOM based rendering opens up an additional and less discussed frontier- That of interoperability with other DOM based rendering engines such as those in Angular, React, Mithril, and Polymer. None of these tools output strings, all expect to be attached to a DOM node and yield to a DOM node. By kicking HTML strings to the curb and embracing the DOM, we’ve created the possibility of using the best rendering strategy for a given part of an app.

SVG support is nothing more than a proving ground for how this kind of integration can work. Our next steps are to ship HTMLBars, then work on making Ember’s conventional path a competitively performing one for all common cases.

My Ember.js consulting firm 201 Created has helped 15+ companies get up to speed. We’re eager to work on SVG projects, and available for hire in January.

Ember’s path to a 2.0 release has been announced in an RFC. Our ambition is not simply to have 2.0 be a great framework, but for every 1.x codebase to make the transition forward with us.

There is a lot of code to be written. Contributing code to Ember.js, especially if you are new to open source development, is a rewarding and skills-building experience. The project maintains a high bar for technical contribution, and fixing a bug often involves a healthy dose of new knowledge. Additionally, there is a mature process for adding new features and bug fixes that should serve as an inspiration to your development process elsewhere.

If you have the time and will, adding code to Ember.js is an opportunity to collaborate with wonderful and extremely smart people.

the time” is what keeps most developers from contributing to open source. There are businesses to build, families to raise, and always another Tolkien movie coming out.

But you don’t need to contribute code to help Ember.js reach its 2.0 goals. Here are five great ways to be a part of the process.

Continue reading →

Fresh via EmberFest:

Ember has an amazing testing story built upon the fundamentals of encapsulation and OO programming central to the framework. Despite this, every web application will be dependent upon and need to interact with native and network APIs that are hostile: Unreliable, slow, under-documented, and not to be trusted. The challenge in testing Ember applications lies with how to handle these external entities.

In this talk, we distinguish the domain of an application from its implementation dependencies. The dependency is contained such that it avoids leaking into application code, unit tests, and acceptance tests.

As a addendum exercise, lets consider how to wrap animation frames in a service that can be mocked.

First, consider a component that schedules the animation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// in ember-cli, app/components/animate-in.js
App.AnimateInComponent = Ember.Component.extend({
  
  animateIn: function(){
    var element = this.$();
    var position = -100;
    element.css({
      'margin-left': ''+position+'px'
    });
    function animate(){
      position++;
      element.css('margin-left', ''+position+'px');  
      if (position !== 0) {
        window.requestAnimationFrame(animate);
      }
    }
    window.requestAnimationFrame(animate);
  }.on('didInsertElement', 'click')
  
});

Though functional, this component is leaking knowledge about a browser API (requestAnimationFrame) and is difficult to disable or isolate in tests. If in the animation a component property was modified, it might also require the addition of an explicit runloop.

Lets create a service to contain the request animation API and make it easier to mock in tests. A service in Ember.js has two parts, the service itself:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// in ember-cli, app/services/animation.js
App.AnimationService = Ember.Object.extend({
  init: function(){
    this.frames = [];
  },
  scheduleFrame: function(frame){
    this.frames.push(frame);
    Ember.run.scheduleOnce('afterRender', this, this.scheduleAnimationFrame);
  },
  scheduleAnimationFrame: function(){
    window.requestAnimationFrame(Ember.run.bind(this, this._animateFrame));
  },
  _animateFrame: function(){
    var framesLength = this.frames.length;
    while (framesLength--) {
      var frame = this.frames.shift();
      frame();
    }
    if (this.frames.length) {
      Ember.run.scheduleOnce('afterRender', this, this.scheduleAnimationFrame);
    }
  }
});

And an initializer. In this example, I’m using the globals style of Ember. In ember-cli this would look different (again, see the slides):

1
2
3
// in ember-cli, app/initializers/animation.js
App.register('service:animation', App.AnimationService);
App.inject('component', 'animation', 'service:animation');

And now the component itself can be refactored to use this service:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// in ember-cli, app/components/animate-in.js
App.AnimateInComponent = Ember.Component.extend({
  
  animateIn: function(){
    var element = this.$();
    var animation = this.animation;
    var position = -100;
    element.css({
      'margin-left': ''+position+'px'
    });
    function animate(){
      position++;
      element.css('margin-left', ''+position+'px');  
      if (position !== 0) {
        animation.scheduleFrame(animate);
      }
    }
    animation.scheduleFrame(animate);
  }.on('didInsertElement', 'click')
  
});

A unit test for this component could now easily assert that animation begins, without needing to reference the native requestAnimationFrame itself.

1
2
3
4
5
6
7
8
9
10
11
12
13
// in ember-cli, tests/components/animate-in-test.js
moduleForComponent('component:animate-in');

test('schedules an animation', function(){
  expect(1);
  var component = this.subject({
    scheduleFrame: function(){
      ok(true, 'animation was scheduled!');
    }
  });

  this.append();
});

Additionally, other components can now push frames to our service, conserving how many animation frames the browser needs to manage. This is a great example of how striving to move our technology dependency into a module apart from our domain code can improve the architecture of an application.

Try out the example codebase in this JSBin.

If you joined us in Barcelona, thank you for being part of a great Ember conference. Let me know if you have any thoughts or comments, and hope to see you again!


← All Articles