Unbound not working as expected with components


#1

https://ember-twiddle.com/8a6aaf6ba703e84e0bafee6efb5bcf0b?openFiles=templates.application.hbs%2C

See twiddle above. I am using unbound to attempt to freeze a model. unbound seems to work as I’d expect and freezes the value when output simply on the page like so.

<!-- Does not update, which is what I'd expect -->
<p>{{unbound model.name}}</p>
<!-- Does not update, which is what I'd expect -->
<p>
  {{#with (unbound model) as |user|}}
    {{user.name}}
  {{/with}}
</p>

It does not work as I’d expect when the result of unbound is used with the bs-form component.

This isn’t really an issue for me since I’m using ember-changeset, but I stumbled across this behavior and was curious as to why this is happening. Why do the values in Form 2 and Form 3 in the twiddle update even though I’m using unbound?

It seems odd to me that I would be able to see the result of unbound ever updating.

Apologies if I’m just missing something obvious here.


#2

I think this is because you have used the input text field. This probably makes the value to be mutable. I tried replacing

{{form.element controlType="text" placeholder="Name" property="name" }}

with simply {{user.name}} and the value remained unbound.


#3

Thanks @Akash_D_Souza.

That theory makes sense. Though, I tried to use those unbound properties with inputs like you postulated and saw they remained unbound (which is what I’d expect).

{{input value=user.name}}
<input type="text" value={{user.name}} />

So it seems like it’s not simply that the unbound property is used in an input that’s making it update. It seems like ember-bootstrap is doing something unpredictable that circumvents the unbound.

I feel like I should do some digging into ember-bootstrap to try and figure out what sorcery it’s using that somehow manages to make the unbound property appear to be bound again.

I think the fact that an unbound variable can ever become mutable again is the part that really throws me.

The unbound helper only accepts a single argument, and it return an unbound value.

The {{unbound}} helper disconnects the one-way binding of a property, essentially freezing its value at the moment of rendering

https://www.emberjs.com/api/ember/2.14/classes/Ember.Templates.helpers/methods/unbound?anchor=unbound

Based on the docs, I shouldn’t think there’s any time when an unbound would automatically update again.


#4

For anyone who finds this and is interested, I did some more digging and found that it’s not really an ember-bootstrap issue, but an issue in how unbound seems to behave in certain scenarios. Or, maybe an issue in how properties get passed between components.

https://ember-twiddle.com/aa6e294cbfc2cd8cb44a74892372ea0a?openFiles=templates.application.hbs%2Ctemplates.components.some-component.hbs

It’s not really clear to me from the docs that this would be the behavior, but it is.

This works like I’d expect

{{#with (unbound model) as |unboundModel|}}
   <!-- this is properly unbounded and does not update -->
   {{input value=unboundModel.name}}
{{/with}}

This does not work like I’d expect

// The component
import Ember from 'ember';

export default Ember.Component.extend({
  output: Ember.computed('input', function () {
    return this.get('input');
  })
});
// Using an unbound property with the component
{{#with (unbound model) as |unboundModel|}}
  <!-- unboundModel is unbound -->
  {{#some-component input=unboundModel as |output|}}
    <!-- output.name is bound to model and will update -->
    {{output.name}}
  {{/some-component}}
{{/with}}

It appears that when the unbound property is assigned as a parameter to the component, the component’s instance/reference to that property is somehow bound to model.

I think the reason that behavior is confusing to me is that (in my mind at least) the unboundModel should be disconnected from updates upstream, and so should anything downstream from unboundModel.

… essentially freezing its value at the moment of rendering …

I don’t know enough about how properties are passed around under the hood to understand why it works like it does. There’s probably a line of reasoning where this behavior makes sense, but as someone coming from the angle of “this thing is unbound, so if I use this thing in a component, properties derived from it shouldn’t update” it was a little weird to figure out.