Series: Editing and Validating Forms with ember-changeset

Displaying Errors Inline with ember-changeset-validations

Published on Aug 24, 2016

Validation errors are great, but they're even better when they can be displayed by the relevant input field.

This video shows how to create the validated-input component, which includes inline error display, using ember-changeset-validations.

UPDATE: The update method on one-way-input renders the onblur validateProperty method superfluous. That's why it "works" even when using nonexistent variable property instead of propertyName, as pointed out below by denzo. Sorry for the confusion.


Links

Code

//templates/component/validated-input.hbs
<div class="edit-{{propertyName}}">
  {{label}}
  {{one-way-input value=(get changeset propertyName)
                  update=(action (mut (get changeset propertyName)))
                  onblur=(action 'validateProperty' changeset property)}}

  {{#if (get changeset.error propertyName)}}
    {{#each (get (get changeset.error propertyName) 'validation') as |error|}}
      <br>
      {{error}}
    {{/each}}
  {{/if}}
</div>
//components/validated-input.js
import Ember from 'ember';

export default Ember.Component.extend({
  actions: {
    validateProperty(changeset, property){
      return changeset.validate(property)
    }
  }
});
//templates/monsters/monster/edit.hbs
{{validated-input label="Name: " propertyName="name" changeset=changeset}}
{{validated-input label="Level: " propertyName="level" changeset=changeset}}

Transcript

In the last video, we showed how to create validations and then show the errors. Now, we want to show those errors, not in one big list at the bottom, but right by where the data is being inputted, so the ‘Name can’t be blank’ validation error we want to put right below name, and the two about the level we would want to put right below the level. This is going to end up as a lot more code per input field, so way more than just one line because we’re going to have to put in a set of errors for each of those lines. So we’ll want to have a component that encapsulates all this that we can reuse. So we’ll generate a component called validated-input. When we use validated-input we’re going to be passing in two arguments. One is the label, and we’ll just be taking this label that we’ve given it already. And then we’ll be taking in the propertyName. And this one will be name. So this line will be replacing this big line plus a lot of other stuff. But first let’s just worry about replacing what we already have here before we talk about adding these errors.

Alright. So we’ll go ahead and take this and put it in our validated-input handlebars. So we’ll have to make some changes here. We’ll start with an easy one. This will be the propertyName here. Now we’ll add the label. And this one’s pretty easy. We just put it there ahead of our input field. And the next thing to add is changing this value property. So for this particular one that we took it from, it was the name property on changeset. And another way of writing that is like this. But it’s not always going to be the name property, the property we passed in via propertyName. So that’s how we make sure to get the correct value. And we’re going to be doing the same thing here. Now we can look and see that here our ‘Name’ is still working. It’s still being handled by changeset and it’ll still save. So our validated-input component perfectly replicates what we had before. Now let’s do this for the three other inputs. And sure enough, everything is showing up just like it was before. So we’d just saved a lot of code with this one component and we haven’t even started adding our main goal for today yet.

That goal of course is for the errors to appear right next to where the input is rather than down below in one big group. So we’re going to have to learn a little bit about the error singular object on Ember changeset. So the error singular has instead of a property called key, it actually in the hash has a key with the key, so you can find what errors there are for each individual property. Let’s go ahead and steal this little block of templating and we’re going to make it dynamic. So we’ll put this of course below the one-way input, and our first order of business is to make this dynamic, the if statement. So instead of that, we’ll be using the get helper, and instead of the firstName, we’ll be taking the propertyName. Alright, so we’ve got that if helper under control. Let’s go ahead and test it out. So we’ll have that paragraph tag and we’ll just say ‘Hey’ if there’s an error. Now let’s go ahead and add errors to these, and sure enough, stuff is showing up. Now let’s get the actual errors displaying instead of just ‘Hey’. So what we’ll do is use nested gets, because first we’ll be getting the property, so we’ll have a get here, but then we’ll also need to get the validation of that. So we’ll wrap this get in a sub-expression, and then get the validation off of that. And of course we’ll remove the ‘Hey’. Now, there we go. That is pretty awesome. There are some styling issues. I mean quite frankly this doesn’t look very good, but we’ll leave that as an exercise to the reader since you probably didn’t set yourself up with this ridiculous card format.

So this is the gist of what we want. There are three issues with it though, and we’ll tackle them in order of increasing difficulty. First, this stuff is still down at the bottom, and that is really, really easy to get rid of. We’ll just remove some code. Awesome. So that problem is solved. And the second is that these errors, if there are two showing up, are not formatted very well. That’s because when we just display the validation without looping through them individually, it basically takes an array and it joins them with a comma. So we’ll need to do an each statement here. So we will each over and we’ll make this a sub-expression so it’ll fit inside the each, and now we’ll have each individual error to work with. And we’ll go ahead and make each error have its own paragraph. That might not be the best idea long term but you’ll get the idea. There we go. So they’re both showing up and they’re on different lines. And yeah, I’m thinking this paragraph thing is a really bad idea. Let’s go ahead and just add a break tag before each one. Ah, that fits in much better with the rest of our styling. It’s not saying it’s good. The rest of our styling is pretty bad, but it fits in.

So that solves the second problem. What’s the third? Well, it’s that let’s say we go to a monster that already has some data that now no longer fits in the validations. This would happen for example when you’re adding a new monster or when you add new strictures to the data. So you go to change it and you go here like ah, I want to change this a little, I want to go here, and then you tab through it and you don’t change it. And nothing is showing up, so you think it’s fine, but then when you hit ‘Save’, it’s like oh, there are errors that it didn’t tell you about and now they finally show up. So what we want to do is show the errors onblur. We use the onblur event and then when we’re blurring we’ll use an action, and we’ll call validateProperty. That’s an action we’ll still have to create. And we’ll call it with the changeset and the property. So this will check the validations for that property on the changeset. And of course we’ll have to create this in our validated-input component. So we’re taking in the changeset and the property and then we’re just returning , calling changeset.validate for that specific property. This is something that comes with Ember changeset validations. Now let’s test that out. When we blur out of it, it shows us that error. So there we go. There is our nice validated-input component. I hope you’ll join me in the next video where we discuss more possible validations, as well as how to create your own validations.

Editing and Validating Forms with ember-changeset

Subscribe to our mailing list