2

This official guide describes how you can bind a boolean property to disabled attribute of a HTML element. Yet it talks about a controller.

I have a button, that when clicked transitions the route (sorry it has to be a button and cannot be a link-to):

/templates/trails.hbs

<button type="button" class="btn btn-primary" disabled={{isEditing}} 
              onclick={{route-action 'addNew'}}>Add New</button>

(route-action is a helper that allows me to use closure actions in routes)

/routes/trails.js

import Ember from 'ember';

export default Ember.Route.extend({
  actions: {
    addNew() {
      this.transitionTo('trails.new');
    } 
  }
});

So, after the button is clicked, the route is changed to 'trails.new'

/routes/trails/new.js

import Ember from 'ember';

export default Ember.Route.extend({
  isEditing: true,
});

This property appears to be ignored and is not bound as I had expected it would be. I also tried adding a controller:

/controllers/trails/new.js

import Ember from 'ember';

export default Ember.Controller.extend({
  isEditing: true,
});

So how does the official guide suggest something that seems to not work? What piece of ember magic am I missing here?

rmcsharry
  • 5,363
  • 6
  • 65
  • 108

2 Answers2

1

Your template is templates/trails.hbs but you set isEditing in a subroute controller controllers/trails/new.js

You need to have controllers/trails.js and deinfe isEditing in it.

So in routes/trails.js implement this :

actions: {
    willTransition: function(transition) {
      if(transtions.targetName === 'trails.new'){
        this.controller.set('isEditing', true);
      }
      else{
        this.controller.set('isEditing', false);
      }
    }
  }
Ebrahim Pasbani
  • 9,168
  • 2
  • 23
  • 30
  • Ah ha - I was refactoring into a component whilst you were providing this answer (which is correct in that it does answer what I was trying to do). But I think the component is the future-proof way to do this. So thanks for the solution, just a few minutes late! – rmcsharry Sep 08 '16 at 17:28
  • 1
    @rmcsharry Absolutely components are better.l I just provide answer to your question. And I should mentioned that the best practice is compoent – Ebrahim Pasbani Sep 08 '16 at 17:29
1

After some digging around I discovered that what I was trying to do is not the right way to go about this at all. I would have to add a controller/trails.js and put the property 'isEditing' in that.

So I refactored this into a component: add-new-button. This is a far more 'ember' way.

First, I need an initializer (thanks to this question):

app/initializers/router.js

export function initialize(application) {
  application.inject('route', 'router', 'router:main');
  application.inject('component', 'router', 'router:main');
}

export default {
  name: 'router',
  initialize
};

(this injects the router into the component, so I can watch it for changes and also 'grab' the currentRoute)

My code refactored into the component:

app/components/add-new-button.js

import Ember from 'ember';

export default Ember.Component.extend({
  isEditing: function() {
    let currentRoute = this.get('router.currentRouteName');
    return ~currentRoute.indexOf('new');
  }.property('router.currentRouteName')
});

templates/components/add-new-button.hbs

<button type="button" class="btn btn-primary" disabled={{isEditing}} 
        onclick={{route-action 'addNew'}}>Add New</button>

templates/trails.hbs

{{add-new-button}}

The beauty of this is now I can use this button on my other top level templates to trigger route changes to the new route for each resource (and disable the button on arrival at the new route).

NOTE

return ~currentRoute.indexOf('new');

is doing a substring check on the route, if it finds 'new' returns true, otherwise returns false. See this.

In ES6 it can be replaced with (so I have!):

return currentRoute.includes('new);
Community
  • 1
  • 1
rmcsharry
  • 5,363
  • 6
  • 65
  • 108
  • I´m curious, is the initializer really necessary? If yes why/ for which part of the code ? – Christian Stengel Sep 09 '16 at 13:34
  • @ChristianStengel To change 'isEditing' value I need to know if I am on a 'new' route or not. Hence this.get('router.currentRouteName'); That didn't work without having the initializer (which injects the router into the component - remember Components are isolated from the outside world). Maybe I am 'breaking' that 'isolation' pattern doing this, not sure. – rmcsharry Sep 09 '16 at 14:08
  • 1
    @ChristianStengel Also, read the question I linked to at the start (more info about getting the current route). If you try to do it manually (ie without the initializer inject the router) then usually your component loads, sees the route change, works, then stops seeing any further route changes, so the component becomes 'locked' to that first state. – rmcsharry Sep 09 '16 at 14:12