Search code examples
objectember.jsbindingcontrollerobservers

Ember.js - how to bind an object value with property of a controller


Ember.js newbie question:

What is the best way (if it is possible), to bind an object value with property of a controller ?

Assuming, we have this bit of code:

App.Stalker = Ember.Mixin.create({
  init: function() {
    this._super();
    var props = this.get('watchProperties');
    Ember.assert("watchProperties should be an array", Ember.isArray(props));
    props.forEach(function(property) {
      Ember.addObserver(this, property, this, '%@Changed'.fmt(property));
    }, this);
  },

  willDestroy: function() {
    this._super();
    this.get('watchProperties').forEach(function(property) {
      Ember.removeObserver(this, property, this, '%@Changed'.fmt(property));
    }, this);
  }
});

App.states = Ember.Object.createWithMixins(App.Stalker, {
  watchProperties: 'state'.w(),
  stateChanged: function() {
    console.log("idle changed");
  }
});

App.IndexController= Ember.Controller.extend({
  state: *// How do I bind with App.states.state*,
  contentDidChange: function() {
    console.log('idle !!!');
    this.transitionToRoute("idle");
  }.observes('state')
    });

// Here is use a jQuery plugin to check if the User of the app goes in the idle mode

App.IndexView = Ember.View.extend({
  didInsertElement : function(){
    self = this;
    this._super();
    $( document ).on( "idle.idleTimer", function(event, elem, obj){
        App.states.set('state', 'idle');
        console.log(self.get("controller.state"));
        });
  }
});

Solution

  • This will get your example working... sort of.

    App.ApplicationRoute = Ember.Route.extend({
      actions: {
        lazyState: function (state) {
          console.log("lazyState: " + state);
          switch (state) {
            case "idle":
              this.transitionTo("lazy");
              break;
            case "active":
              this.transitionTo("index");
              break;
            default: throw new Error("Invalid state");
          }
        }
      }
    });
    
    App.LazyView = Ember.View.extend({
      _handler: null,
      didInsertElement : function(){
        console.log("insert lazy");
        var self = this;
        this._super();
        var handler = function (event, elem, obj) {
          Ember.run(function () {
            self.get("controller").send("lazyState", "active");
          });
        };
        $(document).on("active.idleTimer", handler);
        this.set("_handler", handler);
      },
      willDestroyElement: function() {
        $(document).off("active.idleTimer", this.get("_handler"));
        this.set("_handler", null);
      }
    });
    
    // Index Views functionality
    
    App.IndexView = Ember.View.extend({
      _handler: null,
      didInsertElement : function(){
        console.log("insert index");
        self = this;
        this._super();
        var handler = function (event, elem, obj) {
          Ember.run(function () {
            self.get("controller").send("lazyState", "idle");
          });
         };
        $(document).on("idle.idleTimer", handler);
        this.set("_handler", handler);
      },
      willDestroyElement: function () {
        $(document).off("idle.idleTimer", this.get("_handler"));
        this.set("_handler", null);
      }
    });
    

    JSBin

    I whipped this into a working order, however I almost didn't post it as the answer. IMO this is neither the most stellar implementation, nor the correct approach. To continue from here, you'd need to refactor IndexView implementation into some sort of Mixin, or move it to ApplicationView. Then you'd need to implement some way to save the current location when entering 'idle' mode, so you don't always go back to index...

    Keep at it long enough, and I think you'll arrive to what I feel is the right solution: don't use Routes and Views at all.

    From your description, all you need is to show some kind of screen-saver while user is idle. You don't need to switch routes for that. There is no user interaction to be had on the idle screen. Instead, just render whatever DOM you want for idle screen into a hidden div in the main layout, and use jQuery to show/hide it while user is idle. Or put it into a #if isIdle block and change isIdle from a global event handler.