jeudi 21 mai 2015

Understanding isDirty

A simple situation with unexpected results

Before I get to my question, some context. I have an ember-data project

DEBUG: -------------------------------
DEBUG: Ember             : 1.11.1
DEBUG: Ember Data        : 1.0.0-beta.16.1
DEBUG: jQuery            : 1.11.2
DEBUG: Ember Simple Auth : 0.7.3
DEBUG: -------------------------------

An Ember-data model

I have a simple model containing some string attributes and some number attributes. This is my app\models\simplemodel.js

import DS from "ember-data";

export default DS.Model.extend({
  code: DS.attr('string'),
  description: DS.attr('string'),
  a_number: DS.attr('number'),
  another_number: DS.attr('number'),
  related_stuff: DS.hasMany('someothermodel', {async: true})
});

A handlebars template with bound <input type="number"> elements

I have a template in which I set the number parameters with a bound <input type="number"> element.

<header>
  <h2>{{model.code}}</h2>
  <h2>{{textarea value=model.description placeholder="description"}}</h2>
</header>
<section>
  <p>
      <label>A number:&nbsp;</label>{{input type="number" value=model.a_number}}
      <label>Another number:&nbsp;</label>{{input type="number" value=model.another_number}}
    {{#if model.isDirty}}
      <button {{action 'save'}}>Save</button>
    {{/if}}
  </p>
</section>

The problem/Unexpected behaviour

When I change the value of the <input type="number"> elements the save button appears as expected. However, when I reset the value back to the original value the save button remains. The model isDirty is still true even though the values match the original values.

Possible explanation

Now, I understand that the <input> element will store its content as a string rather than a number. Presumably, when I change the value of the bound input it sets the bound model attribute to its value. It could be that the original value was say 1 but the by using the <input> element I am changing the value to "1". However, the model explicitly declares this attribute to be a DS.attr('number'). Indeed, when I save the model it dutifully sends a number in the payload to the server.

{"simplemodel":{"code":"34735337888888","description":"Some description","a_number":4,"another_number":5}}

This is expected, ember-data deals with communication with the server and has cast the DS.attr('number') attributes to numbers. So I guess the actual value of the model attribute before it is sent to the server is still a string and hence the model isDirty.

The question

Is my explanation correct and is there a way to avoid this problem?

Possible solutions

I can see two obvious ways to handle this.

  1. Make a version of the input element that transparently casts strings to numbers - I have tried this with no luck - probably because I don't understand how ember works.
  2. Have the model cast values to the appropriate type automatically.

My instinct would be to go with 1 as 2 feels like it would be more likely to produce unexpected behaviour.

My attempt

I tried to produce a component to use for all input elements bound to numbers.

import Ember from 'ember';

export default Ember.Component.extend({
    attributeBindings: ['value', 'type'],
    tagName: 'input',
    type: 'number',
    number: null,

    value: function() {
      var number = this.get('number');
      return number;
    }.property('number'),

    change: function() {
      var value = this.$().val();
      this.set('number', parseFloat(value));
    }
});

When I used this, the isDirty flag was not set at all even when the value of my component was changed. I don't understand this anyway, it was adapted from something I've been using for date fields. Do I need to do something else here?




Aucun commentaire:

Enregistrer un commentaire