This question is related to Ember Octane Upgrade How to pass values from component to controller
I was struggling to receive and assign values from an HBS form into a component and then pass it to the controller. The working answer showed that I had to create an @action
function for each form field. For example:
@action
changeNewPassword(ev) {
this.newPassword = ev.target.value;
}
But, I do not understand where or how those functions are called and so I do not understand why they work. Does anyone know how these functions are called?
Template Component HBS
<div class="middle-box text-center loginscreen animated fadeInDown">
<div>
<h3>Change Password</h3>
<form class="m-t" role="form" {{on "submit" this.changePassword}}>
{{#each this.errors as |error|}}
<div class="error-alert">{{error.detail}}</div>
{{/each}}
<div class="form-group">
<Input @type="password" class="form-control" placeholder="Old Password" @value={{this.oldPassword}} required="true" />
</div>
<div class="form-group">
<Input @type="password" class="form-control" placeholder="New Password" @value={{this.newPassword}} required="true" />
</div>
<div class="form-group">
<Input @type="password" class="form-control" placeholder="Confirm Password" @value={{this.confirmPassword}} required="true" />
</div>
<div>
<button type="submit" class="btn btn-primary block full-width m-b">Submit</button>
</div>
</form>
</div>
</div>
Template HBS
<Clients::ChangePasswordForm @chgpwd={{this.model}} @changePassword={{action 'changePassword'}} @errors={{this.errors}} />
Template Component JS
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
export default class ChangePasswordForm extends Component {
@tracked oldPassword;
@tracked newPassword;
@tracked confirmPassword;
@tracked errors = [];
@action
changeOldPassword(ev) {
this.oldPassword = ev.target.value;
}
@action
changeNewPassword(ev) {
this.newPassword = ev.target.value;
}
@action
changeConfirmPassword(ev) {
this.confirmPassword = ev.target.value;
}
@action
changePassword(ev) {
ev.preventDefault();
this.args.changePassword({
oldPassword: this.oldPassword,
newPassword: this.newPassword,
confirmPassword: this.confirmPassword
});
}
}
In Ember Octane, you want to use the on
modifier for setting up actions.
The line
<form class="m-t" role="form" {{on "submit" this.changePassword}}>
effectively sets up an event listener for the submit
event of this form element which will invoke the changePassword
function on the component's class (because the this
in this.changePassword
means that the function is local to the component)
That invokes this action:
@action
changePassword(ev) {
ev.preventDefault();
this.args.changePassword({
oldPassword: this.oldPassword,
newPassword: this.newPassword,
confirmPassword: this.confirmPassword
});
}
This changePassword
action is in turn invoking the changePassword
function that was passed to the component under the named argument @changePassword
<Clients::ChangePasswordForm @chgpwd={{this.model}} @changePassword={{action 'changePassword'}} @errors={{this.errors}} />
Now, in your Template Component JS
you have three other actions
changeOldPassword
changeNewPassword
changeConfirmPassword
which, as far as I can tell from the code you posted, are never used. They look like code you would use to set up a 1-way bound input, but you are using the built-in Input
which is the Ember input built-in component (and uses two-way binding between the input value and the @value
). The very important distinction to note is the capital I
on Input
. All angle bracket components use title-casing (each separate word starts with a capital letter).
Had you instead done something like:
<input type="password" class="form-control" placeholder="New Password" value={{this.newPassword}} {{on 'input' this.changeNewPassword}} required="true">
Then you would have bound the this.changeNewPassword
function to the input
event of the <input>
element (which is the native html <input>
. With the changeNewPassword
action as you've defined:
@action
changeNewPassword(ev) {
this.newPassword = ev.target.value;
}
You would have kept the this.newPassword
value in sync with the input via one way binding.
There are two ways I see you are using actions in your examples.
{{on}}
:<form class="m-t" role="form" {{on "submit" this.changePassword}}>
This case is more straightforward. When you do this
in a component template, you are referring to the component's class, so, this.changePassword
is called by the template when the submit DOM event happens in the form
element.
You can see more information in the {{on}}
API docs .
{{action}}
<Clients::ChangePasswordForm @chgpwd={{this.model}} @changePassword={{action 'changePassword'}} @errors={{this.errors}} />
In this case, whenever @changePassword
is triggered inside Clients::ChangePasswordForm
, Ember will search for a changePassword
either in the actions hash (classic syntax) or a decorated method ( @action
) in the class of the component that is using Clients::ChangePasswordForm
.
You can see more information in the {{action}}
API docs .
Hope this helps clarify the action mechanisms.
For extra homework, you might want to check the upgrade guides on actions .
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.