Is there really no way to make an optional dynamic segment in Ember?

I tried to add dynamic segment which should be optional for my app, first thing is I didn’t know they cannot be optional with the :param syntax, and I started getting a strange non descriptive error out of my templates of all things

Uncaught TypeError: Cannot read property 'shouldSupercede' of undefined 

It took me over an hour to realize what’s going on and no matter how much I tried to make the param optional with *param and (:param), but I could not.

This is a huge problem. If anyone knows the proper way to do this I’ll be very grateful

You can make a dynamic segment optional by adding another route. For instance, let’s say that you had a post route with a post_id dynamic segment. To make it optional, just do this:

App.Router.map(function() {
    this.route('default_post', { path: '/post' });
    this.route('post', { path: '/post/:post_id' });
});

Or, my preferred way of doing it:

App.Router.map(function() {
    this.resource('post', { path: '/post' }, function() {
        // 'index' route is created automatically
        this.route('by_id', { path: '/:post_id' });
    });
});

Either way, going to /post will take you to a different route than /post/123. From that separate route, you can either have separate logic, or just redirect to the route with the dynamic segment.

3 Likes

I see, thank you. I will experiment with this soon

OK this worked:

this.resource('articles',  function() {
	this.resource('articles.create', { path: '/create' }, function() {
        // 'index' route is created automatically
        this.route('type', { path: '/:type' });
	});
	this.route('view', { path: ':article_id' });
	this.route('edit', { path: '/edit/:article_id/' });
	this.route('type', { path: '/type/:type' });
});

But now I run into a whole lot more nonsense. I have to create a new route for articles/create/type/:type, so I can get the param out, so I have to copy paste a bunch of code from the original create route. After that I specify the model, but the model will not be picked up by the template. It actually gives me the ArticlesCreate controller and the model defined in the ArticlesCreate route.

I’m not sure I understand what you’re saying. Why would you have to copy and paste code from one route to another? You can get parent route models using the modelFor function. Also, what do you mean by “but the model will not be picked up by the template”?

I was able to do what I needed to by upgrading to Canary and using a query param for the /create route and not having to nest another route or resource

Have you tried doing something like:

App.CreateRoute = Ember.Route.extend({
...
)};

App.CreateTypeRoute = App.CreateRoute.extend({
...
)};

You shouldnt have to copy and paste code from one route to another. Try just extending the parent route (and eventually controller if you want).

A little late joining the conversation, but wanted to say that what you suggested, gives me the error: Uncaught TypeError: Cannot read property 'extend' of undefined

Currently, if you get the beta this problem can be solved using query params

I’d like to come back and bump this to provide a more complete solution. If you want a truly optional dynamic segment (not a query parameter), look at this Gist. We used this at work a few weeks ago and it works wonderfully. (Let me know if you find any issues with it though.)

3 Likes