Components can communicate changes with actions. These actions can be sent back to the parent or be handled in the component. Let's take a look at a few recipes that show this.
In this recipe, we'll create a student list that we will then manipulate.
student-list
component:$ ember g component student-list
This will generate the student-list
component and the necessary files.
student-list.js
file in the app/components
folder. We'll need to create a few actions
and a new array:// app/components/student-list.js import Ember from 'ember'; export default Ember.Component.extend({ init() { this._super(...arguments); this.setup(); }, actions: { remove(){ this.get('listOfStudents').popObject(); }, reset(){ this.setup(); } }, setup(){ let st = this.set('listOfStudents',[]); st.clear(); st.pushObject('Erik'); st.pushObject('Bob'); st.pushObject('Suze'); } });
The first part of this component is the init
method. This will fire as soon as the component is initialized. This is a private method that sets up the component. As we are overriding this framework method, we must call super
so that the component is created correctly. The setup
method is then called.
The setup
method creates a new listOfStudents
array, clears it, and creates three new string objects by popping them onto the array. As we are creating the listOfStudents
array in the init
method, it will be local to this component instance. It's good practice to declare objects or properties in the init
or didInsertElement
methods. Otherwise, if the object or array is declared as a property of the component, the component will no longer have its own independent state. We'll be discussing the didInsertElement
hook later in the book.
There are two actions
listed, remove
and reset
. Both will tie back into actions in the component's template that we'll use later. The remove
action removes or pops off one object from the top of the array. The reset
method calls setup
, and this returns the component to its original state.
each
helper to list the contents of the array in the student-list.hbs
file in the app/templates/components
folder:// app/templates/components/student-list.hbs <button {{action 'remove'}}>Remove</button> <button {{action 'reset'}}>Reset</button><br> {{#each listOfStudents as |student|}} {{student}}<br> {{/each}}
The {{action}}
helper will fire when the remove
and reset
buttons are pressed. The first argument to the action
helper is always the action name. The each
helper lists the contents of the listOfStudents
Ember array.
student-list
component to the application template:// app/templates/application.hbs <h2 id="title">Welcome to Ember</h2> {{student-list }} {{outlet}}
This will display the contents of the student-list
component.
ember server
, your output will look as follows:Pressing the Remove button will remove each item one by one. The Reset
button will reset the array back.
Ember provides you with a way to send actions from a parent to child component. We can use closure actions to make this possible.
application
route and a student-list
component:$ ember g route application $ ember g controller application $ ember g component student-list
This will generate the application.js
file in the app/routes
folder, the application
controller, and the student-list
component.
listOfStudents
array:// app/routes/application.js import Ember from 'ember'; export default Ember.Route.extend({ listOfStudents: [], beforeModel(){ this.reset(); }, model(){ return this.get('listOfStudents'); }, reset(){ let st = this.get('listOfStudents'); st.clear(); st.pushObject('Tiffany'); st.pushObject('Zack'); st.pushObject('George'); }, actions: { removeRoute(){ this.get('listOfStudents').popObject(); }, resetRoute(){ this.reset(); } } });
This might look familiar to you from the previous example. Here, we are creating a new array called listOfStudents
. The beforeModel
hook will run before the model is set up. In this beforeModel
, the reset
method is called. This adds the default data to the array.
The model hook returns the listOfStudents
array. In addition, we added two actions, remove
and reset
, to remove an item or reset the items back to the original array, respectively.
This was essentially the same code that we had in the component earlier but we moved it to the route instead.
application.hbs
file and add the component that we just created:// app/templates/application.hbs <h2 id="title">Welcome to Ember</h2> {{student-list onRemove=(action 'removeController' ) onReset=(action 'resetController')}} {{#each model as |student|}} {{student}}<br> {{/each}} {{outlet}}
This template will show a list of students from our model and our student-list
component. The onRemove
and onReset
properties are set to actions that are basically functions in our parent controller that we can pass to the component. This is called a closure action. All closure actions must have parentheses surrounding the (action 'removeController')
action.
// app/controllers/application.js import Ember from 'ember'; export default Ember.Controller.extend({ actions:{ removeController(){ this.send('removeRoute'); }, resetController(){ this.send('resetRoute'); } } });
These two actions use this.send
to send the action to the removeRoute
and resetRoute
actions that we defined earlier. You can use the send
method to trigger actions from parent routes or controllers.
// app/templates/components/student-list.hbs <button {{action 'removeComponent'}}>Remove</button> <button {{action 'resetComponent'}}>Reset</button>
This component displays two buttons that are linked to actions that are defined in the component.
// app/components/student-list.js import Ember from 'ember'; export default Ember.Component.extend({ actions: { removeComponent(){ this.get('onRemove')(); }, resetComponent(){ his.attrs.onReset(); } } });
These actions will be triggered from the component template. At this point, they'll trigger the function that was passed to it from the closure actions, onRemove
and onReset
.
To invoke these methods, you can do it using either this.get
or this.attrs
. The this.get
method and the name of the property, in this case onRemove
, will invoke the method passed to it.
In the other case, you can use this.attrs
to access the attributes on the property to invoke the function that was passed on, this.attrs.onReset()
.
The action will flow this way: application template -> component -> controller -> route. The end result is the route triggering the action to remove or reset the list.
ember server
and you should see a list that you can now delete or reset:Actions can be handled in a few ways in a component. Actions can be added to different HTML tags and be handled within components. Actions can also be sent to parent components or controllers using closure actions or send.
Closure actions make the passing of actions much easier. We can pass down actions to components that they can then invoke. This helps separate logic between different parent routes, controllers, and components.