X2theZ
X2theZ

Reputation: 276

Observing a controller attribute in the view and react to it

Thanks to the great presentation from Luke Melia during the last EmberJS NYC meetup, I've spent the night and all day refactoring my stuff to apply some of your concept and I really appreciate how I got to better understand part of the framework.

When questioned about how he would handle enabling the controller to dictate some of the view's behavior, he mentioned observing the controller's attribute in the view. With that in mind, I went ahead and split my app to make sure I was leveraging the router ability to manage state. So I created an EditProfile route.

To dictate the toggling of my EditProfile section I created a showEditProfile attribute on my EditProfileController and I set up an observer in my EditProfileView.

The problem is that I can't get it to work. If I click on the "save" or "cancel" button in my EditProfile template, it triggers the "confirmProfileUpdate" or "cancelProfileUpdate" respectively which in turns set the showEditProfile to false. Doing that should trigger the view's observer, right? It doesn't seems to be the case.

Here is the code:

application_routes.coffee

    App.ApplicationRoute = Ember.Route.extend(
      setupController: ->
        @controllerFor("user").set "model" App.User.find(App.current_profile_id)
)

edit_profile.hbs

<div id="profile_edit">
  <div class="section">
    <h1>Edit Profile</h1>
  </div>
  <form id="edit_user">
    <div class="section">
      <label>Name</label>
      {{view Em.TextField valueBinding="name" }}
      <label>Location</label>
      {{view Em.TextField valueBinding="location" }}
      <label>Motto</label>
      {{view Em.TextField valueBinding="description" }}
    </div>
    <div class="section">
      <label>Email</label>
      {{view Em.TextField valueBinding="email" }}
      <label>Password</label>
      {{view Em.TextField valueBinding="password" type="password" }}
      <label>Re-enter Password</label>
      {{view Em.TextField valueBinding="password_confirmation" type="password" }}
      <div class="btns">
        <button type="submit" class="btn" {{action "confirmProfileUpdate" content}}>Save</button>
        <button type="submit" class="btn" {{action "cancelProfileUpdate" content}}>Cancel</button>
      </div>
    </div>
  </form>
</div>

edit_profile_controller.coffee

App.EditProfileController = Ember.ObjectController.extend(
  showEditProfile: true
)

edit_profile_routes.coffee

App.EditProfileRoute = Ember.Route.extend(
  renderTemplate: -> 
   @render "edit_profile", {outlet: "edit_profile", controller: 'user'}

  events:
    confirmProfileUpdate: (record) ->
      record.get("transaction").commit()
      # @transitionTo('index')
      console.log "confirmed! toggling the slider back up"
      @controller.set "showEditProfile", false

    cancelProfileUpdate: (record) ->
      record.get("transaction").rollback()
      # @transitionTo('index')
      console.log "cancelling! toggling the slider back up"
      @controller.set "showEditProfile", false
)

edit_profile_view.coffee

App.EditProfileView = Ember.View.extend(

  toggleEditProfile: (->
    console.log "toggling!"
    $("#profile_ edit").slideToggle "slow"
  ).observes("controller.showEditProfile")

  didInsertElement: ->
    @controller.set "showEditProfile", true
)

I've created a simplified example of Luke's approach which works: http://jsbin.com/ujosew/4/edit

So at this point, I am wondering if there is not a confusion about which controller my view is observing (you will have noted that EditProfileController is using the User model).

Any hint to a solution would be beneficial as I am running short of options...

--- Edit thanks to the help of Alex Matchneer (@machty) on the #emberjs IRC chan (which I recommend to everyone looking for guidance) ---

As Teddy pointed out in his answer, by changing the controller, it was normal that the observer was not going to react. So I changed the code to this and it now works

application_routes.coffee

    App.ApplicationRoute = Ember.Route.extend(
      setupController: ->
        @controllerFor("user").set "model" App.User.find(App.current_profile_id)
)

edit_profile.hbs

<div class="section">
  <h1>Edit Profile</h1>
</div>
<form id="edit_user">
  <div class="section">
    <label>Name</label>
    {{view Em.TextField valueBinding="name" }}
    <label>Location</label>
    {{view Em.TextField valueBinding="location" }}
    <label>Description</label>
    {{view Em.TextField valueBinding="description" }}
  </div>
  <div class="section">
    <label>Email</label>
    {{view Em.TextField valueBinding="email" }}
    <label>Password</label>
    {{view Em.TextField valueBinding="password" type="password" }}
    <label>Re-enter Password</label>
    {{view Em.TextField valueBinding="password_confirmation" type="password" }}
    <div class="btns">
      <button type="submit" class="btn" {{action "confirmProfileUpdate" content}}>Save</button>
      <button type="submit" class="btn" {{action "cancelProfileUpdate" content}}>Cancel</button>
    </div>
  </div>
</form>

edit_profile_controller.coffee

App.EditProfileController = Ember.ObjectController.extend(
  needs: ['user']
  visible: true
)

edit_profile_routes.coffee

App.EditProfileRoute = Ember.Route.extend(
  renderTemplate: Ember.K

  setupController: ->
    @controllerFor("edit_profile").set "model", App.User.find(App.current_profile_id)

  activate: ->
    @controllerFor("edit_profile").set "visible", true

  deactivate: ->
    @controllerFor("edit_profile").set "visible", false

  events:
    confirmProfileUpdate: (record) ->
      record.get("transaction").commit()
      @transitionTo('index')

    cancelProfileUpdate: (record) ->
      record.get("transaction").rollback()
      @transitionTo('index')
)

edit_profile_view.coffee

App.EditProfileView = Ember.View.extend(
  classNames: ['profile_edit']

  toggleEditProfile: (->
    $(".profile_edit").slideToggle "slow"
  ).observes("controller.visible")

  didInsertElement: ->
    $(".profile_edit").slideToggle "slow" if @get("controller.visible")
)

Upvotes: 0

Views: 2529

Answers (1)

Teddy Zeenny
Teddy Zeenny

Reputation: 3971

By overriding renderTemplate:

@render "edit_profile", {outlet: "edit_profile", controller: 'user'}

You set the view's controller to be App.UserController.

In the route, @controller is App.EditProfileController, but in the view, controller property refers to App.UserController.

They are referencing different controllers.

Upvotes: 1

Related Questions