Actions¶
We have now seen how to handle data exchange with the user at the basic level. However, we also want the application to be able to react to when the user instigates certain actions, such as clicking on a button or submitting a form. To handle any such kind of interaction we use the {{on}}
helper. Update the <form>
tag in the apps/templates/login.hbs
to look like this:
The {{on}}
helper is very powerful and supports a number of other parameters, but in its most basic form it has two parameters, which is when an action is triggered and the name of the action to call. The action will be called when the element emits the “submit” event. In the case of a <form>
we want the action to fire when the user submits the form, thus we need to specify the property with the value “submit”, so that the action is called when the user submits the form. We will cover helpers in more detail in an upcoming tutorial.
The next step is to actually handle the action, which we will do in the app/components/login-page.js
:
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
export default class LoginPageComponent extends Component {
@tracked email = '';
@tracked password = '';
@tracked loginInvalid = false;
@action
login(event) {
event.preventDefault();
if(this.email === 'test@example.com' && this.password === 'password') {
this.loginInvalid = false;
} else {
this.loginInvalid = true;
}
}
};
All actions have to be specified with the action
decorator and the name of the action function must be the same as the name of the action used in the {{on}}
helper. The new action handler function demonstrates a few more Ember patterns. First, in the action handle this
always refers to the current context. Second, to access any property on the component, we use the tracked property. This is necessary as Ember needs to know when we modify a property, so that it can update the UI. The rest of the code is relatively straight-forward. We get e-mail and password, check them and then set a new property loginInvalid
to either true
or false
. Note, that we have a parameter that is called event
, even though we did not pass it in the function when we called it in the hbs
-file. The on
-helper is passing this parameter automatically for us. We use this parameter to prevent the default behavior. If we did not do so, this behaviour would have caused the page to reload without setting the values of our classes.
Templating Control: {{if}}
¶
We can now use the new loginInvalid
to only show the error message if the login failed. Update the two error message <span>
elements in app/components/login-page.hbs
to look like this:
{{#if this.loginInvalid}}
<span class="invalid-feedback">No user exists with the e-mail address {{this.email}} or the password is incorrect.</span>
{{/if}}
Previously the helpers we used (such as {{on}}
) did not have any content. For helpers that do have a content, the opening tag is specified with a #
and the closing with a /
. In the case of the {{#if}}
statement, the next part of the opening tag is to specify the property that is tested for true
or false
. What is important here is that we cannot specify any comparisons directly in the template. We can only specify the property to check for its truthiness. If we want to do more complex checks, these need to be performed in the controller and then the result set to a single property, which is then used in the template.
Templating: Binding Attributes¶
We have now seen how to bind properties to input fields, make the template dynamic, and react to actions. The next thing we want to do is to bind a property to the attribute of one or more HTML elements, so that we can modify the HTML element attribute value. We will use this to further highlight any errors in the form. Again, we can make use of Ember’s `{{if}}
-helper, but we use it inline this time.
Update the two <label>
tags to look like this:
<label class={{if loginInvalid this.classLabel}}>
With the property classLabel
now bound to the class
properties of two <label>
elements, we can control the CSS classes from the controller. Update the app/components/login-page.js
to this:
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
export default class LoginPageComponent extends Component {
@tracked email = '';
@tracked password = '';
@tracked loginInvalid = 'false';
classLabel = 'text-danger';
classInput = 'is-invalid';
classSpan = 'invalid-feedback';
@action
login(event) {
event.preventDefault();
if(this.email === 'test@example.com' && this.password === 'password') {
this.loginInvalid = false;
} else {
this.loginInvalid = true;
}
}
};
As you can see, the only difference is the addition of the variable classInput
that receives a value during the definition. The function only checks the user input and sets the value for classLabel
. If you now test the login functionality, you will see that when you provide invalid input, the <label>
font colour also changes. This works since we track the value of loginInvalid
. We use this variable for reusability.
Of cause, we can to update the Input
and span
elements as well.
<Input type="email" @value={{this.email}} class={{if loginInvalid this.classInput}}/>
{{#if this.loginInvalid}}
<span class='{{if this.loginInvalid this.classSpan}} '>No user exists with this e-mail address {{this.email}} or the password is incorrect.</span>
{{/if}}
<Input type="password" @value={{this.password}} class={{if loginInvalid this.classInput}} />
{{#if this.loginInvalid}}
<span class='{{if this.loginInvalid this.classSpan}} '>No user exists with this e-mail address {{this.email}} or the password is incorrect.</span>
{{/if}}
Template Control: Action Events¶
If you type an invalid email address into the input field and then submit it, the code shows the error message. However, if you then correct the email address, the error message updates with the new email address value. To the user this implies that the changed email address is still invalid. It is thus better if we hide the error message when the user starts typing. To do this, we need to create a new action and then bind that to a specific event in the <Input>
. Update the two <Input>
tags in the app/components/login-page.hbs
to this:
To attach an action to a specific event we specify the event name. In JavaScript the event is actually called keyPress
, but in Ember everything is lowercase, so it becomes keypress
. In this way we can tell Ember to listen for the keyPress
event and when it happens, call the typing
action.
To handle the new action update the app/components/login-page.js
by adding this below the login
action:
@action
typing() {
this.loginInvalid = false;
}
If you test this out now, you will see that when you start typing the error message vanishes again, providing a better user experience.