Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ember.js - define route with optional parameter and default model

I want to define a route in emberjs, that has an optional parameter eg:

/video and /video/123

if no parameter is supplied, I want to use a default model/fixture. if a parameter is supplied, then I want to obviously lookup the model using the parameter.

if I then go to a different route, and return to the route without the parameter, I want to use the previously loaded model.

eg:

startup app

/video - shows my default/fixture model

/video/123 - shows model 123

/another-route - shows new route

/video - shows model 123

is this possible?

like image 754
kabal Avatar asked Nov 15 '13 00:11

kabal


2 Answers

There is a clean way to do this, although it is slightly "tricky". The idea is to use a nested route to hold the id, but not render it, instead having the parent route be responsible for rendering it using the render helper. When you do it this way, all the logic can live in VideoChoiceController, and it will be used for displaying either the default video or a specific one. When doing it this way, there is no need to explicitly "remember" the last video, the state machine that the route engine represents does it for you.

App.Router.map(function) {
  this.resource('video', function(){
    this.route('choice', {path: ':video_id'});
  });
});

App.VideoRoute = Ember.Route.extend({
  model: function(params) {
    return App.get('defaultVideo');
  },

  setupController: function(controller, model) {
    var video_choice = this.controllerFor('video.choice')

    // this is necessary if you want, for example,
    // to display the name or a link to the default video
    // when a specific video is being displayed
    controller.set('model', model);

    if(Ember.isEmpty(video_choice.get('model'))){
      video_choice.set('model', model);
    }
  }
});

App.VideoChoiceRoute = Ember.Route.extend({
  model: function(params) {
    return this.get('store').find('video', params.video_id);
  },

  renderTemplate: function() {
    // if you don't override renderTemplate to do nothing, 
    // everything will still work but you will get an assertion
    // error that render should only be used once with a
    // singleton controller
  }
});



<script type="text/x-handlebars">
  <div>
    {{outlet}}
  </div>
</script>

<script type="text/x-handlebars" data-template-name='video'>
  <div> attrributes here always refer to the default video: {{name}} </div>
  {{render "video.choice"}}
</script>

<script type="text/x-handlebars" data-template-name='video'>
  <div> 
    attrributes here always refer to specific or last video, 
    or default if a specific video has never been loaded: {{name}}
  </div>
</script>
like image 28
Michael Johnston Avatar answered Oct 18 '22 17:10

Michael Johnston


I ended up using a different solution:

  this.resource('video', function() {
    this.route('index', {path: '/'});
    this.route('item', {path: ':id'});
  });

These routes support:

/video - shows my default/fixture model

/video/123 - shows model 123

When the user access to /video, the VideoIndexRoute must redirect to VideoItemRoute without any id.

var VideoIndexRoute = Em.Route.extend({

  afterModel: function() {

    // this is the tricky part
    this.replaceWith('video.item', '');

  }

});

Now, the VideoItemRoute must check if there is any model associated, and when it is missing, it should use the default fixtures or a new one.

var VideoItemRoute = Em.Route.extend({

  model: function(param) {
    if (param.id) {
      return this.store.find('video', param.id);
    }
  },

  setupController: function (controller, model) {
    if (!model) {
      model = this.store.createRecord('video',{
        name: 'default Name'
      });
      // or use fixture...
    }
    this._super(controller, model);

  }

});
like image 174
ppcano Avatar answered Oct 18 '22 15:10

ppcano