Like a Route in Ember, components also have a series of public functions that fire during the life of the component.

To get started let's go back to the app that we created to look at routes and generate a component. From the root of our example app from the route episode (lifecycle-example) we can generate a new component and add it to the index template of the app.

ember g component example-component
vim app/templates/index.hbs
  {{example-component}}

An Ember component is made of two parts: a JavaScript file and a template. We will look more at the template when we create an app next week. This week we are going to look at the lifecycle hooks of the Ember component. There is a good writeup in the Ember guides about the lifecycle hooks. I recommend you go over there an look through them. I will recreate some of that here and try and try to supplement it with a few additions.

First, let's look at the hooks as they execute. We'll add the methods in the component's JavaScript file with some console logs to see them fire. Most of the methods are named really well so describing their behavior can feel somewhat redundant, but perhaps it will be helpful so, let's to go over them anyway.

import Ember from 'ember';

export default Ember.Component.extend({
  // On initial render
  init(){
    // When you override the init method on a component, you need to call this._super(...arguments); so Ember can
    // do all of its internal setup. After calling super, you can do anything that you need to do to setup your
    // component.
    this._super(...arguments);
    console.log('----------------- In Component -----------------');
    console.log('Component: 1 - init - on initial render');
  },
  // on re-render
  didUpdateAttrs(){
    this._super(...arguments);
    // didUpdateAttrs is run right after init, when the attributes of the component have changed... but only sometimes.
    // This hook doesn't fire when component.rerender, component.set changes attrs. It only fires on external
    // changes that trigger a re-render. This makes this hook ideal for resetting presentation state. I have used this hook in
    // a type-ahead search component to reset the value of the search field after the search is submitted. We will
    // implement something like this in an upcoming episode.
    console.log('Component: 2 - didUpdateAttrs - on re-render');
  },
  // on initial render and re-render
  didReceiveAttrs(){
    // This fires when the attrs are received by the component. I have used this hook to pull out
    // the first item in an array to feature it in the display. It fires on initial render and re-render,
    // but like it's update sibling didUpdateAttrs, it doesn't fire on internal attribute changes.
    console.log('Component: 3 - didReceiveAttrs - on initial render and re-render');
  },
  // on re-render
  willUpdate(){
    // This hook fires every time a component is re-rendering for any reason. Confession time:
    // I have never used this hook. I know there are good reasons for using it, I just haven't
    // encountered them in real life. If you have any good use cases I would love to hear them.
    console.log('Component: 4 - willUpdate - on re-render'); },
  // on initial render and re-render
  willRender(){
    // This hook fires before the component is rendered and before any re-renders
    console.log('Component: 5 - willRender - on initial render and re-render');
  },
  // On initial render
  didInsertElement(){
    // The didInsertElement hook is one of the more dangerous hooks available to Ember developers.
    // You can use this hook to get into all kinds of trouble. This fires after your component has
    // been inserted into the DOM. If you were so inclined, you could integrate third party (non-Ember)
    // stuff here, or if you need to look at the rendered DOM you can do it here.
    console.log('Component: 6 - didInsertElement - on initial render');
  },
  // on re-render
  didUpdate(){
    // The didUpdate hook is similar to the didInsertElement hook only it runs on re-renders after the
    // changes have been inserted into the DOM.
    console.log('Component: 7 - didUpdate - on re-render');
  },
  // on initial render and re-render
  didRender(){
    // didRender run after didInsertElement and didUpdate on both initial render and subsequent re-renders.
    console.log('Component: 8 - didRender - on initial render and re-render');
  },

  // Destroyish stuff
  willDestroyElement(){
    // If you set up any kind of event listeners in one of the methods above,
    // you could remove them here. This hook is just for any tear down type
    // things that you need to do.
    console.log('Component: Destroy - willDestroyElement - on destroy');
  },
  willClearRender(){
    // If a component is going to re-render this hook gets called before it is torn down.
    console.log('Component: willClearRender - on destroy');
  }
});

Now, let's open up our component's template and add a few lines. In app/templates/components/example-component.hbs:

<h2>Example Component</h2>
{{input value=exampleProp}}

This line with the input is using Ember's input helper and binding the value to a property on the component called exampleProp. This is here so that we can change a property on the component and see how the hooks that fire as a result.

Now, let's start our ember server and take a look at what we have.

ember server
open http://localhost:4200

Now, open your dev tools (cmd + option + i) and look at your console and refresh the page. If we look at our console logs we will see that init, didReceiveAttrs, willRender, didInsertElement, and didRender all fired. Now, still looking at our console, type a character in the input field. Now, we will see that willUpdate, willRender, didUpdate and didRender fired. In our example, because of how we constructed our example, the didUpdateAttrs will not fire.

Let's add one more thing. Let's create a controller for the index route. in app/controllers/index.js add an init method:

import Ember from 'ember';

export default Ember.Controller.extend({
  init(){
    console.log('--------- Controller Init ----------');
  }
});

Now, let's look at our site. We can see that our controller and component's init methods fire. If we navigate to the about route, and back to the index route we will see that our component's init method runs again but our controller's init method does not. This is because in Ember controllers are singletons, while components are ephemeral. As a result, any state you store on your component is ephemeral as well. If you navigate away from your component, or your component is destroyed, all internal state will be lost. In most cases, if the state needs to be preserved, it should be stored somewhere other than the component.

That is it for today. See you tomorrow when we wrap this week with some links and an exercise.