`mut` keyword with angle-bracket component attributes

Hi all,

Given:

<some-comp test={{mut someProp}} />

Then using this.get('test') inside the component returns an object containing (among others) a value property, and an update method.

Will this be the case going forward or will these be wrapped in a computed property with a getter and setter?

In your example (with angle bracket components), test should not be available directly off of this in the component. If this.get('test') is working, that is likely a bug and we will fix it before 1.13.0 final. You should be accessing it from this.attrs.test instead.


Yes, using {{mut}} returns a new “mutable object” that has a .value and .update property. That is definitely intentional.

You can use the new API’s for interacting with attrs: this.getAttr('test') or this.setAttr('test', 'new value').

1 Like

Thanks that was an error, i definitely meant this.get('attrs.test').

And thanks again, that’s what I was looking for. I see that .setAttr throws an exception when trying to set non-mutable attribute.

I noticed that when I didn’t use {{mut}} it was simply a value property without .value. and .update, and was wondering the best way to tell from inside the component whether or not the attribute was given an explicit two-way binding or one-way binding. Should we be writing components with the mindset that an attribute could or could not be mutable, or should we rather just make users aware that an attribute should always be mutable?

An example use-case for this last point would be someone using an attribute similar to how one would use a value method argument, where you could assign the argument different values for use as a local variable without affecting the variable used when calling the method.

I think that the best pattern for a component is to send an action with the new value. The calling site then has the ability to either create an action (normally) or create mutating action in the template. I realize this doesn’t make a bunch of sense, so I made a demo JSBin.

The crux of the demo is:

<labeled-input label="Name:" value={{name}} update={{action (mut name)}} />

Let me break update={{action (mut name)}} down a bit:

  1. Make a mutable object for name.
  2. The action helper identifies that it was passed a mutable object, and makes a dynamic action that is the equivalent of:
updateName: function(value) {
  this.set('name', value);
}

Down the line, if you need to do more to deal with this update action, just implement the action above and augment with your new logic. From the labeled-input component’s perspective nothing has changed, it is still sending an action up.

3 Likes

That’s a great explanation of using mut with action, and moving from mutable attributes to “data down, actions up”.

Thanks!

– Edit

How will this tie in with ember-data? I have a situation where I would need to pass a model to a component and perhaps generate some additional input fields based on certain criteria, so I would not be able to pass each field individually to the component as an attribute. Now obviously the model itself is not mutable if I used like:

<some-component model={{someModel}} />

But the properties on the model are still mutable. So I could have an input like:

{{input value=attrs.model.property}}

And this would allow me to update the property on the model.

@uberclops I’m curious if you ever nailed down the pattern here. For such a long time two-way bindings were the recommendation and using an input with a EmberData model was just this simple. Upon first glance, the {{mut}} pattern feels slightly jarring to the rest of the ecosystem.

Unfortunately not, but I’m waiting on the recommended best practices from the Ember team when all of this becomes standard.

I don’t really see a way to get around it unless you force all objects to be passed by value (thus deep copying them), which generally isn’t a practice people follow. This is just the nature of passing by reference. At the end of the day you could change the objects properties, but you wouldn’t be able to change the object reference itself. Personally I still just pass the model itself to the component because I would use some meta-programming to add properties dynamically to the model, which I would need to generate inputs for.

I’d imagine if you really didn’t want the object properties being changed you would need to proxy the properties yourself to make sure they’re read-only.