You may have heard the motto "Data down, actions up". This is a pattern in favour of two-way bindings that's strongly encouraged with the move to Ember 2.0 with an emphasis on the actions up part. It's what EmberUp is named after.

The general principle is that you regain control of data flow in your app by splitting out the getting and setting functionality of properties that you pass to components. That is that you set readonly attributes on a component and trigger actions when that data needs to be changed.

E.g. instead of

{{my-time-input value=job.startTime}}

we write something like

{{my-time-input value=readOnlyStartTime

Where readOnlyStartTime is readonly and updateStartTime is an action that gets triggered when the input is changed. We could define define the action and readonly property like this:

readOnlyStartTime: Ember.computed.readOnly('job.startTime'),

actions: {  
  updateStartTime(value) {
    this.set('job.startTime', value);

Although this is perfectly valid, it is definitely verbose. If you have a large form this can quickly escalate to plenty of boilerplate code.

Enter mut and readonly helpers

Ember provides some helpers for wrapping up this behaviour on your properties when they are passed to the component.

In particular the mut helper will provide an update method that sets the date. This is is actually what happens by default behind the scenes, but will need to be set explicitly on Ember 2.0 with GlimmerComponents so it's good to get into the habit of using it now.

The readonly helper wraps the property in a way that disables set on the property, this forces the property to be set using an action. This way if you are using a component that still supports setting in the old way your properties won't be unexpectedly updated.

Finally the action helper will wrap up the mut object to update it when it receives a value.

Our example now works without boilerplate code and we interact with the properties directly:

{{my-time-input value=(readonly job.startTime)
                on-time-change=(action (mut job.startTime))}}

Thanks to @mmun for reading this over.

Tags: Helpers, Upgrading