Upgrade from Ember <3.15
to >=3.15
. How do I pass form values from a controller into a component?
I cannot begin to explain the number of diagnostic combinations attempted and their corresponding errors received. So, I figure it best to ask how it should be done correctly? Is Glimmer involved?
A simple example: pass a change password from old
password to both a new
and confirm
password via a component to a controller. In the Component, I keep getting onsubmit() is not a function
error.
Code example:
User Input Form
ChangePasswordForm.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 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=oldPassword required="true"}} </div> <div class="form-group"> {{input type="password" class="form-control" placeholder="New Password" value=newPassword required="true"}} </div> <div class="form-group"> {{input type="password" class="form-control" placeholder="Confirm Password" value=confirmPassword required="true"}} </div> <div> <button type="submit" class="btn btn-primary block full-width m-b">Submit</button> </div> </form> </div> </div>
Template Component
ChangePassword.hbs
<Clients::ChangePasswordForm @chgpwd={{this.model}} {{on "submit" this.changePassword}} @errors={{this.errors}} />
Component
ChangePasswordForm.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 changePassword(ev) { // Prevent the form's default action. ev.preventDefault(); this.oldPassword = ev.oldPassword; this.newPassword = ev.newPassword; this.confirmPassword = ev.confirmPassword; // Call the form's onsubmit method and pass in the component's values. this.onsubmit({ oldPassword: this.oldPassword, newPassword: this.newPassword, confirmPassword: this.confirmPassword }); } }
Controller
ChangePassword.js
import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; export default class ChangePassword extends Controller { @service ajax @service session @action changePassword(attrs) { if(attrs.newPassword == attrs.oldPassword) { this.set('errors', [{ detail: "The old password and new password are the same. The password was not changed.", status: 1003, title: 'Change Password Failed' }]); } else if(attrs.newPassword != attrs.confirmPassword) { this.set('errors', [{ detail: "The new password and confirm password must be the same value. The password was not changed.", status: 1003, title: 'Change Password Failed' }]); } else { let token = this.get('session.data.authenticated.token'); this.ajax.request(this.store.adapterFor('application').get('host') + "/clients/change-password", { method: 'POST', data: JSON.stringify({ data: { attributes: { "old-password" : attrs.oldPassword, "new-password" : attrs.newPassword, "confirm-password" : attrs.confirmPassword }, type: 'change-passwords' } }), headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/vnd.api+json', 'Accept': 'application/vnd.api+json' } }) .then(() => { // Transistion to the change-password-success route. this.transitionToRoute('clients.change-password-success'); }) .catch((ex) => { // Set the errors property to the errors held in the ex.payload.errors. This will allow the errors to be shown in the UI. this.set('errors', ex.payload.errors); }); } } }
Model
ChangePassword.js
import Route from '@ember/routing/route'; import AbcAuthenticatedRouteMixin from '../../mixins/abc-authenticated-route-mixin'; export default Route.extend(AbcAuthenticatedRouteMixin, { //export default class ChangePasswordRoute extends Route(AbcAuthenticatedRouteMixin, { model() { return { oldPassword: '', newPassword: '', confirmPassword: '' }; }, })
Advertisement
Answer
There is no onsubmit
method in @glimmer/component
, so you cannot call this.onsubmit
inside an action in the component.
First, you need to pass the action created in your controller to your component. This can be done like this:
<ChangePasswordForm @chgpwd={{this.model}} @changePassword={{action 'changePassword'}} />
Remember, you cannot pass data up any more in a glimmer component, you need to use an action since everything is one way binding.
Second, you need to call this action inside your glimmer component:
this.args.changePassword({ oldPassword: this.oldPassword, newPassword: this.newPassword, confirmPassword: this.confirmPassword });
I’ve created an Ember Twiddle for you to show this example working.