Using ember-changeset with ember-redux

Better inputs without mutating state

Dustin Farris
The Ember Way
3 min readDec 9, 2016

--

ember-changeset enables you to stage changes against your Redux state without mutating anything! That’s pretty darn awesome. Guess what: it validates those changes, too!

The problem with binding

When you set up your stateToComputed in redux, you have to be mindful of Ember’s 2-way binding.

For example, if you have a global “project” in your application, and you want your user to be able to edit the name of the project. You might have a simple “project” reducer that:

  1. Returns the current state of a global “project”; and
  2. Receives an action called “UPDATE_PROJECT” that replaces the project with new data

That reducer would look like:

Then, you might be tempted to set up an EditProject component like this:

The thinking behind the above component is that you want the user to edit the project property of the component, so you think that:

  1. The user modifies the project via an input element
  2. When the user clicks “Submit”, you dispatch the modified project
  3. The project reducer replaces the old project with the new one

The problem with this logic is Ember binds that project property all the way back to Redux state. See the action (mut project.name) in the oninput handler? Well, if this is your current Redux state:

{
project: {
name: 'Chores'
}
}

The second you start typing “My New Proje”, you are triggering oninput which begins mutating project.name which Ember has bound all the way back up to Redux! Without any dispatch or anything, your state is now:

{
project: {
name: 'My New Proje'
}
}

That’s not right! That’s not the redux way! There must be something better!

Something better

ember-redux is all about Data Down Actions Up. It works great for managing state, and it’s dead simple to use. ember-changeset all about DDAU, too, and it’s the sharpest tool in the box for managing changes.

Install ember-changeset in the usual way:

ember install ember-changeset

Now you have a powerful helper, changeset, that you can use in your layouts. This helper takes a content object, project, in our case, and gives you a new copied (sort of) object that you can mutate. Any mutations you make are saved on this new object in an intelligent way that you can do all sorts of neat things with.

For our purposes, saving an edit is pretty much all we need at this point.

How can we can use the changeset helper to rewrite the edit project component? We will:

  1. Create a form component that takes a changeset and a submit action
  2. Adjust the EditProject component layout to use the new form component
  3. Update the action to send the change object from the changeset

Here’s the EditProjectForm component:

And the new EditProject component:

As soon as the user hits “Submit”, we can grab the change object and send that to our reducer instead. The change object will be just that, an object who’s key/values have deviated from the original.

Since the change object may not have all of the key/values of the original, this won’t be a simple replacement. Luckily, there'sObject.assign, which combines the properties of multiple objects, with objects on the right overriding objects on the left. We’ll use Object.assign to finish things off in our reducer:

Wow! When you use changesets, you can throw anything at them and not worry about accidentally messing with your Redux state. All without any deepCopying, buffers, etc., that you might reach for otherwise.

ember-changeset makes working with immutable objects a delight!

Bonus

You get a lot of other neat things with ember-changeset, too! Like real-time input validations, rollbacks, snapshots, and more! Check out ember-changet’s README for more info.

Chime in

Got a better way to wrangle Ember’s 2-way binding? Tell us! We’re hanging out in #e-redux . 🙃

--

--