2

This question is related to Ember Octane How to Get Error Messages to be Displayed?

Question: What is the correct way to clear form errors and how do I do it? I want this to run everytime the user comes to the form. The form errors are generated in the Controller JS file. The use case is as follows:

  1. User navigates to form
  2. User provides erroneous input, resulting in errors to be displayed
  3. User navigates away from the form and does something else
  4. User comes back to the form and the existing errors re-display (I do not want this to happen)

In Ember Classic, I am able to clear form errors within the component JS file using the following code snippet:

import { A } from '@ember/array';

...

init() {
    this._super(... arguments);
    this.set('errors', A([]));
},

However, in Ember Octane, I get the following ESLint error:

Don't use this._super in ES classes ember/no-ember-super-in-es-classes

I tried changing the code snippet to:

import { A } from '@ember/array';

...

init() {
    super(... arguments);
    this.set('errors', A([]));
}

Unfortunately, I get the following error:

super() is only valid inside a class constructor of a subclass. Maybe a typo in the method name ('constructor') or not extending another class?

Code

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 @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 @changePasswordModel={{this.model}} @changePassword={{action 'changePassword'}} @errors={{this.errors}} />

Component JS:

import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';

export default class ChangePasswordForm extends Component {

    constructor() {
        super(...arguments);
        this.errors  = []
    }

    @tracked oldPassword;
    @tracked newPassword;
    @tracked confirmPassword;
    @tracked errors;    

    @action
    changePassword(ev) {

        ev.preventDefault();

        this.args.changePassword({
            oldPassword: this.oldPassword,
            newPassword: this.newPassword,
            confirmPassword: this.confirmPassword
        });
    }
}

Controller 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(() => {

                this.transitionToRoute('clients.change-password-success');
            })
            .catch((ex) => {

                this.set('errors', ex.payload.errors);
            });
        }
    }
}

I have posted an Ember-Twiddle:

https://ember-twiddle.com/364eaf05a2e1072994b61f255032eb62?openFiles=templates.application%5C.hbs%2C

J Weezy
  • 3,507
  • 3
  • 32
  • 88
  • try with https://github.com/emberjs/ember-render-modifiers and add that to a `did-insert` so that code runs every time the element is inserted on the dom – xploshioOn Apr 13 '20 at 08:53

1 Answers1

2

classic ember was

 init() {
    this._super(...arguments);
  }

ember Octane uses classe constructor

constructor() {
    super(...arguments);
  }

Ember.js Octane vs Classic Cheat Sheet

look at this examle i made : example

i have edited your twiddle file, i added a clearErrors action to the controller,

 @action
  clearErrors(){
     this.set('errors',[]);
  }

then a passed it as argument to the component ,

<Clients::ChangePasswordForm @changePasswordModel={{this.model}} @changePassword={{action 'changePassword'}}
@clearErrors={{action 'clearErrors'}} 
@errors={{this.errors}} />

then on each init of component i call clearErrors ,

    constructor() {
        super(...arguments);
        this.args.clearErrors();
    }
Slimane amiar
  • 934
  • 12
  • 27
  • That does not clear out the form's errors. How do I clear out the form's errors? – J Weezy Apr 04 '20 at 00:22
  • look at the example that i added – Slimane amiar Apr 05 '20 at 08:00
  • I see where you are going with this. My apologies, I was not clear on the use case in my question - I have updated it. Instead of clearing the errors array in each `@action`, I want the constructor to run it, if that is possible. – J Weezy Apr 05 '20 at 18:04
  • Upvote for the Twiddle that you posted. I like the use case of allowing the user to clear errors. That could be modified to clear the user's form inputs. – J Weezy Apr 05 '20 at 18:17
  • actually the constructor is initialising the message each time you change route, look again at the example i have edited , if it not your case , in some way you are navigating from the form without chnaging the route , then you have to call the clear action on that event – Slimane amiar Apr 06 '20 at 06:46
  • I have updated my question with the code. The component only gets called on the first time I click on it's link. When I submit and get an error I then click on another link. When I click back on the link, the error still shows. I can clear the errors by refreshing the page. – J Weezy Apr 07 '20 at 05:22
  • @insltemit That worked - thank you. Unfortunately, the bounty has already expired so I cannot award it. FYI, I have another question posted on Google's reCaptcha. If you could look at it I would greatly appreciate it https://stackoverflow.com/questions/61242639/ember-octane-ember-g-recaptcha-recaptcharesponse-comes-back-as-undefined – J Weezy Apr 18 '20 at 21:14