Skip to content

Instantly share code, notes, and snippets.

@jamesarosen
Created January 11, 2012 00:21
Show Gist options
  • Save jamesarosen/1592144 to your computer and use it in GitHub Desktop.
Save jamesarosen/1592144 to your computer and use it in GitHub Desktop.
On Ember Views & States

I have a state machine for the major sections of the page:

App.States = Ember.StateManger.create({
  foo: Ember.State.create({})
});

I have a view that needs to reset itself whenever the user enters the foo state:

App.FooDisclosureView = Ember.View.extend({
  didInsertElement: function() {
    // whenever the button is pressed, toggle the disclosure:
    var self = this;
    this.$('button').click(function() {
      self.get('disclosedContent').toggleProperty('isVisible');
    });
  },

  reset: function() {
    // close the disclosure if it's open
    this.get('disclosedContent').set('isVisible', false);
  }
});

The question is how to call FooDisclosureView#reset when that state is entered.

Option 1: currentState binding

App.FooDisclosureView = Ember.View.extend({
  // as above...

  resetOnEnterStateFoo: function() {
    // if foo has child states, you can use a more intelligent matcher
    if (App.getPath('States.currentState.name') === 'foo') {
      this.reset();
    }
  }.observes('App.States.currentState.name')
});

Option 2: Controller Mediation

Essentially the same as 1, but with a Controller.

App.fooController = Ember.Object.create({
  active: false
});

App.States = Ember.StateManger.create({
  foo: Ember.State.create({
    enter: function() { App.setPath('fooController.active', true); },
    exit:  function() { App.setPath('fooController.active', false); }
  })
});

App.FooDisclosureView = Ember.View.extend({
  // as above...

  resetOnEnterStateFoo: function() {
    if (App.getPath('fooController.active')) {
      this.reset();
    }
  }.observes('App.fooController.active')
});

Option 3: State Change Events

This option requires changing Ember.StateManager to emit events when states change. This could be a monkey-patch or, if generally useful, incorporated into the library.

App.FooDisclosureView = Ember.View.extend({
  init: function() {
    Ember.addListener(
      App.States,
      'stateChange',
      this,
      this.resetOnEnterStateFoo
    );
  },

  resetOnEnterStateFoo: function(newState, oldState) {
    if (newState.get('name') === 'foo') { this.reset(); }
  },

  // as above...
});
@jish
Copy link

jish commented Jan 12, 2012

If viewStates won't work, then I would suggest the enter: method I proposed above, or the pattern you suggested earlier:

foo: Ember.State.create({
  disclosureVisible: true
})

then:

someBinding: 'currentState.disclosureVisible

edit: Nm, that pattern doesn't take into account the toggling nature you suggest :x

@jamesarosen
Copy link
Author

No, though it does suggest a slightly different approach:

foo: Ember.State.create({
  fooDisclosureAvailable: true
})
...
App.FooDisclosureView = Ember.View.extend({
  resetObserver: function() {
    if (this.getPath("App.States.currentState.fooDisclosureAvailable")) {
      // The observer only fires when this has just been changed and it's
      // now true, so it must have changed from false:
      this.reset();
    }
  }.observes("App.States.currentState.fooDisclosureAvailable")
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment