25

Please see the solution to another question provided by ud3323: http://jsfiddle.net/ud3323/ykL69/. This solution highlights the changed value using the red color. I have an additional requirement: if the value has increased highlight in green color, if it has decreased highlight in red color. For this, I need to know the old and the new values in my observer. Is there any way to do this?

P.S. Embers docs do not say anything about what's available in the observer function. All I can tell from the example is that since the observer is defined in the itemRowView, "this" points to the itemRowView.

Naresh
  • 23,937
  • 33
  • 132
  • 204

4 Answers4

17

beforeObserver has been deprecated in Ember 1.10.

Instead, use a manual cache:

doSomething: Ember.observer( 'foo', function() {
  var foo = this.get('foo');
  var oldFoo = this.get('_oldFoo');

  if (foo === oldFoo) { return; }

  // Do stuff here

  this.set('_oldFoo', foo);
})
Andrey Mikhaylov - lolmaus
  • 23,107
  • 6
  • 84
  • 133
  • how does this observer know when to get fired when it does not have any properties it subscribes to ? I tried something similar with .observes('foo') and the foo value was already changed inside the observer function. – J. Barca Sep 09 '15 at 16:43
  • Thx, I've updated my answer with the dependency key. Yes, the dependent value is new in the observer. You compare it to the previous value and if it's different than you know that the value has changed. – Andrey Mikhaylov - lolmaus Sep 09 '15 at 16:45
  • OK, I get it, thanks for clarification and update. In my case, I was actually using the old value to set the new value if the new value was undefined so using the setter was best because then I didn't need to stash the value. But I see this works great in many cases. – J. Barca Sep 09 '15 at 20:01
12

Take a look at beforeObservers. A beforeObserver fires before a property changes. So if you get the property in a beforeObserver, you will have the the pre-change value. You set up this type of observer using Ember.addBeforeObserver or via the observesBefore function prototype extension.

That would give you what you need to achieve your goal. I've created the following JSBin based on your fiddle to demonstrate this here: http://emberjs.jsbin.com/qewudu/2/edit

UPDATED on 2014-10-07 to reflect behavior in Ember 1.7.x.

UPDATE on 2015-02: beforeObserver has been deprecated. See other answers to this question.

Andrey Mikhaylov - lolmaus
  • 23,107
  • 6
  • 84
  • 133
Luke Melia
  • 8,389
  • 33
  • 41
2

Use willInsertElement to store the initial value, and upon change of the value, compare the two:

Quotes.itemRowView = Ember.View.extend({
    tagName: 'tr',

    initialValue: null,

    willInsertElement: function(){
        var value = this.get('content').value;
        this.set('initialValue', value); 
    },

    valueDidChange: function() {
        // only run if updating a value already in the DOM
        if(this.get('state') === 'inDOM') {
            var new_value = this.get('content').value;
            // decreased or increased?
            var color =  (new_value > this.get('initialValue') ) ?
                'green' : 'red' ;
            // store the new value
            this.set('initialValue', new_value);
            // only update the value element color
            Ember.$(this.get('element')).find('.quote-value').css('color', color);
        }
    }.observes('content.value')
});

Take a look at the jsfiddle http://jsfiddle.net/ykL69/15/

Panagiotis Panagi
  • 9,927
  • 7
  • 55
  • 103
  • Thanks Zack. That works very well. However, if my understanding is correct, there is one bug in this code. It seems that willInsertElement() is called only once (when the element is inserted initially). Thus to keep the colors coming up correctly, at the end of valueDidChange() you would have to reinitialize the initialValue to the new value - thus making sure that the next change is compared against this value. While this approach works overall, the view is now becoming a place to buffer values. Instead if the observer could provide the old and the new value, the code could be much simpler. – Naresh Feb 11 '12 at 00:49
  • Corrected my answer. It looks like that beforeObserver, as @Luke says, can achieve the same result. – Panagiotis Panagi Feb 11 '12 at 05:36
  • View `state` is an internal property and shouldn't be referenced directly from any of your code. Also, instead of using `Ember.$(this.get('element')).find('.quote-value)`, you can just do `this.$('.quote-value')`. – Peter Wagenet May 01 '12 at 03:48
  • Just a note: shouldn't "Quotes.itemRowView" be "Quotes.ItemRowView" since its extending rather than creating from Ember.View? http://www.emberist.com/2012/04/09/naming-conventions.html – jacobq Oct 04 '12 at 17:15
  • @iX3 The name will not matter. But yes, the convention is to use uppercase for classes and lower cases for instances. – Panagiotis Panagi Oct 04 '12 at 21:30
0

I used a computed property to route to a model's property and store the last value upon its set.

registrationGroup: Em.computed('regionModel.registrationGroup', 
get:->
  @.get('regionModel.registrationGroup')
set:(key, value, oldValue)->
  @.set('lastRegistrationGroup', oldValue)
  @.set('regionModel.registrationGroup', value)
)
J. Barca
  • 538
  • 6
  • 19