Ember router breaks on redirect to '/' - ember.js

I'm having a hard time making my router understand that we are on a certain page and it should be displayed in the navigation as active. The situation is as follows:
this.route('mainRoute', function() {
this.route('list');
});
The path we are interested in is /mainRoute. Unfortunately, there are a lot of legacy links that point to /mainRoute/list. The workaround for this was to redirect from the /mainRoute/list component back to the /mainRoute component.
beforeModel() {
this.replaceWith('/mainRoute');
}
Now, my issue is that the /mainRoute navigation link will never be seen as active. I've tried adding a path for the /mainRoute ('/', '/mainRoute', 'mainRoute'), I've tried transforming it to a resource and a bunch of other things that passed my mind. But it either won't work, or will go in an infinite redirecting loop.
Any thoughts on it? Thanks so much, I really need a solution for this!

If the navigation links are {{link-to}} components. There is a current-when property you could use here. It accepts either a boolean or a string. The string is a space separated values with the route names you want this link to be active when.
From the docs
If you need a link to be 'active' even when it doesn't match the current route, you can use the
current-when argument.
<LinkTo #route='photoGallery' #current-when='photos'>
Photo Gallery
</LinkTo>
{{#link-to 'photoGallery' current-when='photos'}}
Photo Gallery
{{/link-to}}

Related

Ember: get target url from afterModel

I have two paths pointing to the same route:
this.route('items', { path: ':fruitName/rotten' });
this.route('items', { path: ':fruitName' });
I am trying to get the current URL from within the afterModel hook of the /items/fruitName route. If I use this.get('router.url'), it will give me the previous URL before the transition. I need to detect if the target has 'rotten' at the end of the path.
I found that transition.intent.url has the path as a string, but only when its manually entered into the browser. If I click a link to transtion, transition.intent.url is undefined.
How can I determine if the transition URL is /items/fruitName/ or /items/FruitName/rotten?
Before determine which url you have, my question is : how do you make a transition with a different url to the same route (by using {{link-to}}) ?
In your case, you have the same route for 2 different purpose, I understand what you want to accomplish but you will break ember rules (which force you to make a bad design).
You can accomplish what you want to do by using nested routes. let me explain:
/:fruitName and /:fruitName/rotten have the same base (same data): a page for a fruit but in the rotten route, you want to display in another tab.
It's a design problem not a route problem, you can make the most of nested route to:
use index to display main content
subroute to display other thing
you can check an example here with a basic css tab.
Could you use queryParams for this? So your URL would look something like this:
items/tomato?rotten=false
or perhaps you want to go with something more generic:
items/tomato?state=rotten
The route and controller will be set up something like what can be found here: https://guides.emberjs.com/v2.14.0/routing/query-params/

Transition from one route to another with a different model in Emberjs

I have a search page where we are getting different types of search results. In the list of search results I would like to use
{{#linkTo 'someResources.someResource' result}}{{result.Name}}{{/linkTo}}
And on the route someResources.someResource I want to use a totally different model than on the search page. How do I do that? When I click on the link for the linkTo it doesn't load the model again, instead it tries to use the model named result here.
So what I would like to do is to reload the model when I navigate to someResources.someResource based on the values in result.
The I do have a model named App.SomeResource and a find method for it that works if I go directly to that page.
Ember will bypass the model() hook when using linkTo as you've discovered. The assumption is that you passed a model to it, so it and will use that(result) as the model.
The next hook you can use is setupController. Since you have a model hook that works on the direct route, you can use call it directly from here.
One caveat is that you need to also allow for the direct route loading where the model will already have loaded.
setupController: function(controller, model) {
if (!model.isModel) {
this.model().then(function(result)) {
controller.set('model', result)
}
}
}
model.isModel is this check via an isModel property on the directly loaded model, which should be absent when passed with linkTo.
Note: the above code assumes that you are returning a Promise in your model() hook.
Since the problem is that I want a full reload of the model when doing the transition using linkTo won't work since that is using the model given to it. The solution to the problem is actually quite simple, just use a regular html a-tag instead. What I ended up doing was this:
<a {{bindAttr href="somePropertyInYourModel"}}>{{someTextProperty}}</a>
The property somePropertyInYourModel is a property containing the url to the new page. If the url is in the ember routes it will be as if you where typing that address in the address bar and pressing enter, but without the full reload of the page.
I think this is something that could be improved in ember, it would be much nicer if I could write something like:
{{#linkToRoute "resourceA.routeB" params="val1,val2,val3"}}Go here{{/linkToRoute}}
given I have this routes set up:
App.Router.map(function() {
this.resource("resourceA", {{path: "/resourceA"}}, function() {
this.route("routeB", {{path: "/:prop1/:prop2/:prop3");
}
});
I would like to get:
Go here
The order of the val1,val2,val3 matters, if the order is changed they should also be changed in the final url.

How to make a one page menu in Ember?

I'm wondering how can I make a menu like in this website with Ember ?
The page is split in different sections and we can scroll to go to each section, a click on the menu make the page scroll to the wanted section.
I'm not sure if I should have different routes in the router for this behavior, I would guess it's not the case as when we change route the view is remove from the DOM.
Then, how should I build the anchor link to each section ?
The best solution will automatically updates route when we scroll the page but any solution to handle the link and URL recognition will be fine.
Some may argue otherwise, but Ember may be a little overkill for a website landing page like you've shown. Ember is meant more for robust web apps that have multiple views and data they need to be connected with.
First off, if you look at their script, they're using jQuery to animate the body's scrollTop position to the respective div and setting window.location.hash to the hash of the menu element's href which also happens to be the ID of the <section/> the body scrolls to:
$(document).on('click', '#nav a, .clients-capabilities a', function(){
var target = $(this);
var hash = this.hash;
var destination = $(hash).offset().top;
stopAnimatedScroll();
$('#nav li').removeClass('on');
target.parent().addClass('on');
$('html, body').stop().animate({
scrollTop: destination
}, 400, function() { window.location.hash = hash; });
return false;
});
Secondly, they are not doing anything special to load to a specific position on page load. If you load any page on the web with a hash, the browser will look for an element with that ID and load at that position. For example, http://emberjs.com/#download.
Even if you still want to use Ember for this, you'd probably end up doing something similar with a single view loaded from the / route so I wouldn't even worry about Ember until your site becomes a full fledged web app.
Try using a *: catch all pattern in the router on the page you want to handle this scenario.
so lets say that index will work as a single page that you have to be able to automatically scroll to certain elements from the url.
Also you have posts and about sub pages that you can go to from the index page via links.
then...
App.Router.map(function() {
this.route('about');
this.route('posts');
this.route('index',{path:'*:'});
});
so if you have an element with id="elementToScrollTo" then the url /#elementToScrollTo would load the index page and scroll to that element.
See How to handle 'no route matched' in Ember.js and show 404 page? also for some other ways to solve this.
I hope this helps you.

Cant figure out views rendering (they are prepended right before /body)

First my apologies: Im very new to ember.js and struggling so far.
I have a pretty basic app written and I've been using this as my main guide: http://trek.github.com/
My biggest issue right now is figuring out how to deal with Views, specifically the rendered HTML and where it appears in the DOM. It appears, at least with my app currently, the DOM elements are created and inserted into the page but right before /body. So everything just loads below the footer of my main site design.
Doesnt seem to matter where placement of the script templates are in relation to the page, or anything like that??
Is there a way to render views to an existing container div or something? Am I thinking about this wrong? Im used to working with jsRender where I have templates setup, but they typically rendered to an in-memory string that I then needed to insert into an existing container like $('#containerDiv').html(myRenderedHtmlFromATemplate);
Thanks for any help or guidance with this!
Ember will want its views to be hierarchical in the DOM so it can rely on event propagation. You probably noticed a <div class="ember-application"> that gets injected, and then all of your views are rendered inside of that.
You can specify the rootElement when you create your Application. The application will be created inside that element and leave the rest of the DOM untouched. If you don't specify that rootElement, then Ember will insert itself right before </body> as you observed.
Example:
window.MyApp = Ember.Application.create({
rootElement: "#containerDiv"
});
Without seeing code it isn't completely clear what exactly is going on, but, I do not see you mention anything about outlets so I assume that might be your problem.
Check out this url on outlets: http://emberjs.com/guides/outlets/
tuxedo25 has the best solution. If you are using StateManager you can also use the following:
App.StateManager = Ember.StateManager.create({
rootElement: '.content',
initialState: 'initial',
initial: Ember.ViewState.create({
route: 'initial',
view: Ember.View.create({ templateName: 'initial' })
})
});

How can we load multiple instances into a template?

We're trying to put together a portal, where a layout can have any number of core widgets in any sequence in the main layout.
To simulate this, we've got a number of outlets:
<h1>{{title}}</h1>
{{outlet pos1}}
{{outlet pos2}}
{{outlet pos3}}
{{outlet pos4}}
{{outlet pos5}}
{{outlet pos6}}
{{outlet pos7}}
{{outlet pos8}}
{{outlet pos9}}
{{outlet pos10}}
And in the router, we're attempting to load them in one by one:
connectOutlets: function(router, group) {
router.get('applicationController').connectOutlet('group', group);
router.get('groupController').connectOutlet('pos9', 'toDo', App.ToDo.find(41));
router.get('groupController').connectOutlet('pos3', 'toDo', App.ToDo.find(15));
However, when there are more than one, the final context is used. So in this example, we get two instances of the toDo object, both of which are for id #15.
Am I approaching this in the right way and is it possible to do this programatically, rather than having a fixed layout of outlets?
Thanks,
Dan
Edit: My answer is based on the assumption that this complex solution is really needed in your case. Based on your simple example one could also say, that you could use an ArrayController for all your ToDo items. But here is my try on the answer to the complex problem:
the problem are the following 2 lines:
router.get('groupController').connectOutlet('pos9', 'toDo', App.ToDo.find(41));
router.get('groupController').connectOutlet('pos3', 'toDo', App.ToDo.find(15));
What you basically do there is:
Connect the outlet with name pos9 with the Controller named 'todo'. Set the content of this controller to ToDo with Id 41.
Connect the outlet with name pos3 with the Controller named 'todo'. Set the content of this controller to ToDo with Id 15 (so you are overriding the content of the same controller).
The result is that you end up with both outlets connected to the same instance of a controller. And you the same ToDos since you have set the content property of this single instance twice. The core problem is from my point of view: EmberJS uses single instances of controllers by default.
So my solution approach would be to instantiate a new Controller for each outlet you have. Unfortunately this also requires modification to the lookup of the View. As you likely know, Controller and View are matched by name. So roughly the algorithm would be in pseudocode:
Create new instance of Controller, e.g.: var newController = App.ToDoController.create();
Inject this controller into the router with the appropriate name, e.g. router.set('todoControllerForPos9', newController);
Based on this name, you must enable Ember to find the matching view, e.g. App.set('TodoControllerForPos9View', App.ToDoView);
Finally call connectOutlet on the router, e.g.: router.get('groupController').connectOutlet('pos9', 'todoControllerForPos9', App.ToDo.find(41));
As you might guess, i ran into this problem myself. I did ask this question before and this is the solution, i came up with. I think, this is a missing feature i ember. I call it dynamic outlet names. See my original question here: How to implement a list of multiple elements, where an element can be expanded by click? (Dynamic Outlet Names?)
Have a look at the Update section and the fiddle provided there and you will recognize my pseudo code provided here.
It would be still great, if someone could have a look at my solution, because it is still hacky at the moment, but seems valueable to me. Hope this will gain some attention now with this big answer :-)