Intro

Take a first look at a non-declarative component

Transcript

What is declarative rendering? It's one of those terms we hear thrown around all the time, and if you’re working with Ember, chances are you rely on it every day.

Typically when folks talk about declarative rendering in Ember, they say something like, "I know Handlebars is declarative, because it takes care of re-rendering my app whenever its data changes".

That's partially true. But it's not the full picture.

Declarative rendering is an approach for writing UIs, and its something we can violate even when we're using Ember.

For it to work its magic, our code must satisfy three separate conditions, and whenever we break one of those conditions, our apps start to have subtle and unexpected UI bugs, the kind that we're all familiar with:

  • tooltips not being dismissed when we expect
  • components not responding correctly to new server data
  • certain pages of our apps breaking based on the path the user took to get there

You can even find these bugs on sites like Twitter and Facebook. And while there's always quick fixes we can use to paper over these issues, the root cause so often turns out to be that part of our app was rendered in a non-declarative way.

By the end of this series, you'll understand the principles behind declarative rendering, and be able to explain it to others in a simple, step-by-step way that anyone can understand.

We’ll unpack these principles by working with a carousel component. Let’s take a look at it now.

Here's our image-carousel component. It's only functionality right now is these two arrows, and we can click to move the slides back and forth.

Let's come over to the code.

Here's the component’s template. It iterates over an array of photos and renders each one. And we also have the two buttons here that control the navigation.

<div class='carousel-viewport' style='transform: translate(115px)'>

  {{#each photos as |photo index|}}
    <a class='carousel-slide'>
      <div class="carousel-image-wrapper">
        <div style='background-image: url({{photo}})' class="carousel-image"></div>
      </div>
    </a>
  {{/each}}

</div>

<div class="carousel-controls">
  <a {{action 'moveLeft'}} class='carousel-control' href="#">
    {{fa-icon 'angle-left'}}
  </a>
  <a {{action 'moveRight'}} class='carousel-control' href="#">
    {{fa-icon 'angle-right'}}
  </a>
</div>

If we follow this moveRight action into the component, we see that it’s doing a few things.

import Component from '@ember/component';

export default Component.extend({
  classNames: 'carousel',

  photos: null, // passed in

  actions: {
      moveLeft() {
        let container = this.$('.carousel-viewport');
        let current = +container.css('transform').replace(/[^0-9\-.,]/g, '').split(',')[4];
        let shifted = current + 1056;

        container.css('transform', `translate(${shifted}px)`);
      },

      moveRight() {
        let container = this.$('.carousel-viewport');
        let current = +container.css('transform').replace(/[^0-9\-.,]/g, '').split(',')[4];
        let shifted = current - 1056;

        container.css('transform', `translate(${shifted}px)`);
      }
  }

});

First, it grabs the carousel element from the DOM. Then it gets the current value of the transform CSS property, shifts it by the width of one slide, and then updates the transform with that new shifted property.

moveLeft is doing the same thing, except its moving the carousel back by one slide rather than forward.

And that’s what gives us this behavior - every time we click the arrow, the carousel’s transform property is being updated, and the carousel slides.

Now - you might already be thinking that this component doesn't follow standard Ember practices, and that's true. But if we make the violations obvious, it will prevent us from getting distracted as we work through all aspects of declarative rendering. And as you'll see by the end of this series, the non-declarative parts of our own apps are not always so easy to spot.

So - before moving on, take a moment to assess this component. What do you think about it? Do you think it will work like other Ember developers expect it to? What if we wanted to add some new features to it - would we have to change or modify the code we already have?

In the next video, we’ll unpack the first principle of declarative rendering.

Questions?

Send us a tweet:

Or ask us in Inside EmberMap, our private Slack workspace for subscribers.