Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

no good way to make optional dynamic segments #102

Closed
mgenev opened this issue Apr 25, 2014 · 17 comments
Closed

no good way to make optional dynamic segments #102

mgenev opened this issue Apr 25, 2014 · 17 comments

Comments

@mgenev
Copy link

mgenev commented Apr 25, 2014

The whole Ember app breaks if a dynamic segment is defined, but not provided. It gives a strange non-descriptive error. There is no good way to make parameters optional.

People suggested nesting routes which makes the route available, but then it makes a whole lot more complications with which template, model and controller are loaded.

This is a really fundamental feature and the implementation is a huge pain in the ass, I've wasted hours and still haven't made it work and while in something like Express params are optional and it just works.

@GetContented
Copy link

Sorry to hear you're having these issues still, man. would love to help you out and to modify the docs so they're more understandable to people trying to do what you're trying to do, but you'll need to provide some very specific info about what you're trying to do. A JSBin would be really handy. Feel free to re-use your existing stack overflow.

@mgenev
Copy link
Author

mgenev commented Apr 25, 2014

All I want to do is have an /article/create route which I also want to be able to take a type parameter which can be page or blog. If the type is passed I want to preselect what type of article I'm making in the template. It should be so simple, but ember makes it really , really hard.

I've defined it like this and it does give me the /articles/create/type/page route as an option

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

But then it still loads the model, controller and template from the ArticlesCreateRoute, not the one I define in ArticlesCreateTypeRoute. Even though it does hit that route's model hook, it also hits its setupController if I make one, but it will not hit the renderTemplate hook. So I end up with nothing usable anyway.

My project is here:
https://github.com/mgenev/GC-StartUp-CMS/tree/master/public/ember

@GetContented
Copy link

Okay cool... what does the create route do?

In ember, data bindings take away the need to have so many "routes". Here's what I mean:
remember it's not a REST service application, it's a javascript front end application... chances are you just need two or three routes. One could be a list, another would be an item, and a "new" route. The item would include the ability to edit or view. If you want to preselect the type, then that could be an "optional new route", otherwise the model would know what type it was. (Probably would implement the "new" as a transition to "newWithType", but using null or a default as the type value). I'm sure that others will provide a better way to achieve what you want to do. Thanks very much for providing your context. I really hope this helps. If it doesn't, speak up and I'll try to help out further.

The "new" routes handle the contexts of new and create. The "view" route handles the edit, update and show/view contexts. I hope this helps. This is roughly how I'd do it (from memory, unchecked):

  this.resource('articles',  function() {
    this.route('view');
    this.route('new');
    this.route('newWithType', {path: '/:type'});
  });

@stefanpenner @machty would love your inputs at this juncture. If we can use this to ease the pain point here of a lot of people, I think this would be reat (in the docs). I'm happy to help out here if I can. I myself felt this particular pain point as well, coming from other web-based (server-side) frameworks.

@mgenev
Copy link
Author

mgenev commented Apr 25, 2014

I get what you mean with the isEditing part now, that's quite good thanks. About the parameter though, I don't see how it helps me. I want to have a button in my nav and other places in my nav which says 'create article' and gives me choices of what type of article i want to make. I want another button saying 'create page'. I want that to take me to a UI where I create a page which is really an article of type page which is why I'd want the UI slightly altered for entering a page in particular, not multiple choices of articles, which I can achieve with conditionals, but the whole problem is I cannot pass param only some of the times to specify the type when i'm coming from a 'create page' link. I have to make a whole new route and then it will have to have a new controller etc. lots of code bloat and I still haven't done it successfully through nesting after hours, because of the problems i mentioned with it pulling the model from the parent route instead of the child.

If I was able to have a conditional param, I would simply check for it being defined in the create route and if it was i'd assign the type value to it and flip a flag that i'm in 'create page' mode of UI.

If there was no param coming in, i'd allow the user to choose one through the UI.

@GetContented
Copy link

Ah okay. :) The plot thickens! :) Excellent... right now we're getting to your intent. Excellent.

What I personally would do in that case (and actually what I have done in my app) is to have a separate UI, but separate out the common bits into extracted chunks of code and that way you don't repeat yourself.

So, I'd have an articles/new route, but I'd also have a pages/new route... the routes in ember are for the users... this is how you define your app's "api" so to speak... if you think of a user interace as a kind of API (which it is).

From that point, you can customize your routes... and then you don't have the problem of optionality, which isn't a nice user interace anyway...

However... if you really do want to do this optionality thing, then specify them both is my recommendation.

It seems you're trying to specify your "polymorphism" at the URL level, but I'd heartily recommend against that plan of attack. The only reason I can imagine you'd actually need to do that is if you wanted to have a "programmable" API... that is, you want users to be able to specify the "type" of article in some admin section or other...

If that's what you want to do, then you might want to look into the query params (it's new, so you might want to hold off until it's "done"), or perhaps you could specify as I've explained.... articles/new, then another route for articles/new/:type (I didn't write it very clearly above, you'll have to excuse me, but I meant that newWithType route's path to actually say /new/:type).

I can see your requirement, though... you really seem to want to have two paths on a route, which should be possible... I wonder if the query params could be modified to allow this kind of thing? ( @machty ? ) beacuse the crux of this is you want a query param, but you want it baked into the path of the Route (ie the URL directly rather than specified as a query param (ie a ?blah=blah))

Something like this made-up API:
// this "paths" thing is a made-up API that doesn't exist
this.resource('articles', function() {
this.route('view');
this.route('new', paths: [ '/new/:type', 'new']);
});

Which would then pass a query param to the controller of "type", which you could check for and see if it's empty or not...?

As to code bloat, ember actually creates these controllers for you anyway, whether you specify them or not. It's not bloaty, really (other than your code being bigger), beacuse Ember is pretty lightweight with its controllers and routes. You can leave most of the defaults the way they are and just specify the differences. You can also get a route to render the template for another route, and use the controller for another route, too... you might want to take a look into the "needs" part of ember (it's in the guides), because it lets you reference existing controllers, and their data (especially if you're in a nested context).

@mgenev
Copy link
Author

mgenev commented Apr 25, 2014

Thank you for your reply. I've nested like I pasted, but Ember still doesn't function correctly, When the ArticlesCreateType route is hit, it loads the model and controller from the ArticlesCreate route, not the ArticlesCreateType route. I haven't found a way to make it pick up the right model from ArticlesCreateType which has the Type correctly populated from the param and bound to the UI. All this complication would be unnecessary if the router allowed optional parameters for a route.

Yes, passing a query param to the create route would work. /create?type=page, but there is no way to do that either. Which to me is baffling.

@KidkArolis
Copy link
Contributor

If optional dynamic segments were allowed - would they only be allowed if that's the last segment of the URL? Because how would you deal with optional segments in the middle of the URL - those kind of URLs could match multiple routes. Might get confusing?

As things are now I'd say using query params for optional params should be easier.

@mgenev
Copy link
Author

mgenev commented Apr 25, 2014

I fully agree. Except after how many years of development that feature is just now in Canary?

@machty
Copy link
Contributor

machty commented Apr 25, 2014

Sorry, I'd prematurely posted a response here but realized I need to absorb the use cases here a little more.

I'm happy having optional parameter support in route-recognizer and feature flagging in Ember so we can feel out the patterns a little more. The one awkward thing that comes to mind is how we use ordered params for link-to (in Ember) and transitionTo (for both Ember and vanilla router.js), e.g. transitionTo('somewhere', model1, model2), so we'd need to agree that, say, null in the place of an optional parameter means that the parameter is missing and shouldn't show up in the URL. I don't think agreeing on null will be all that hard, but it's still an awkward thing to remember to have to do with optional segment routes.

But if someone wants to get to work on making the fix in route-recognizer, I won't complain.

I don't have time at the moment but would be good for someone to at least absorb some of the discussion in angular-ui/ui-router#108 and https://docs.google.com/document/d/1bQ4bDq7-2Uvj7QhKOxFm-L3R8Qe_BtWo0sZaOsqTtu8/edit to see what we'll have to take into consideration. Any takers?

@mgenev
Copy link
Author

mgenev commented Apr 25, 2014

UGHHH, now i'm trying to achieve this with query params and the syntax that the guide shows blows up handlebars :(

Error: Parse error on line 32:
... {{#link-to 'posts' (query-params direct
-----------------------^
Expecting 'CLOSE', 'CLOSE_UNESCAPED', 'STRING', 'INTEGER', 'BOOLEAN', 'ID', 'DATA', got 'INVALID'

@machty
Copy link
Contributor

machty commented Apr 25, 2014

@mgenev let's not mix issues. That sounds more like an issue of your Handlebars compiler version being less than 1.3.

@mgenev
Copy link
Author

mgenev commented Apr 25, 2014

you're right, sorry, mixing issues is a bad idea

@mgenev
Copy link
Author

mgenev commented Apr 25, 2014

OK, I achieved the functionality I was after - being able to hit route /article/create with an optional type=page param so I can preselect the type in the UI, but it took me around 8 hours and I had to switch to Canary and upgrade my handle-bars compiler, Now I can finally do this. I really appreciate everyone who voiced in to help.

@mgenev
Copy link
Author

mgenev commented Apr 25, 2014

I'm thinking of writing an expanded tutorial on how to do this and how to set up complex routes. Any suggestions about what to include are welcome. My blog about Javascript gets around 1000 views a day, so this would get a fair amount of publicity.

@machty
Copy link
Contributor

machty commented Apr 25, 2014

@mgenev that would be extremely valuable to the community; happy to give it a first look when you have something 👍

@GetContented
Copy link

@machty thanks heaps for your input. @mgenev Dude, that'd be awesome! :) this is a really common problem, whether people speak up about it or not (I have had it in the past, and spent many more than 8 hours wrangling the router).

If you're intrested, rather than writing the use case into a tutorial external to ember, you could consider improving the docs here. I think that'd be absolutely awesome. Add a cookbook example for your use case. It'd improve Ember a lot because it'll do two things:

  1. Focus the core devs on a use case not often brought to light, but prevalent and common
  2. Help other people who "give up" on Ember and don't voice their issues where they really needent.

It'd be great if you could also write a mention on your blog linking in to the cookbook :)

If you need someone to help edit or help write it, I'm happy to help out.

<3 <3 <3

@nathanhammond
Copy link
Contributor

Closing in favor of emberjs/rfcs#154

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants