Routes all link to '/#/:path' - ember.js

I am trying out ember.js with rails, and have a question about routing. Is a # sign supposed to be in every one of my routes?
I have a really simple app with only one route:
App.Router.map () ->
#resource "blogs"
When I go to my index path, my index template is rendered as expected, it has a link to the blogs route {{#link-to 'blogs'}}Blogs{{/link-to}} that has a corresponding template located as templates/blogs. When clicking on this link, my app redirects to host.com/#/blogs. Is this the expected behavior with the # being placed in the path? Navigating to /blogs simply renders the index template and not templates/blogs.

yes, Ember, like many frameworks, takes advantage of using the hash sign for its routing. You can disable it and use location as your history, but that will limit the browsers you support (http://emberjs.com/guides/routing/specifying-the-location-api/).
You'll recognize the hash sign is generally used as a way of bookmarking a spot on the page, and when you click a link with the hash the base url never changes. This allows the page to change the url, but not have to refresh the entire page.

Related

Why should I use {{#link-to}} instead of <a></a> in EmberJS?

This is a pretty newbie question. However, in EmberJS, I've found that both of the methods work for linking to the blog page in my application.
<p>{{#link-to 'posts'}} See my blog{{/link-to}}</p>
See my blog
Is it better to use {{link-to}} in EmberJS? How come?
The difference is that the {{link-to}} component will navigate to the specified route within the current running Ember application, while <a href="posts"> will do a new browser request to that location and re-start your Ember app at that route. You should use {{link-to}} since you'll be using the Ember internals to navigate within your single-page application and it will be a smoother user experience.
While they both can work, watch your browser closely and you'll see the anchor tag will give you a page refresh and re-launch your Ember app (though in the right location). Using a {{link-to}} will feel faster since Ember is presenting the new page via javascript rather than restarting after a page refresh. It's the difference between navigating within a single-page application, and jumping into a SPA from an external page.
While Ember does render an anchor tag in place of the {{link-to}} at run-time, it interjects to stop the default anchor tag behaviour. The docs explain it like so:
By default the {{link-to}} component prevents the default browser
action by calling preventDefault() as this sort of action bubbling is
normally handled internally and we do not want to take the browser to
a new URL (for example).
(from https://emberjs.com/api/classes/Ember.Templates.helpers.html#toc_allowing-default-action)
Also, with the {{link-to}} component you can pass a model directly into the route. This is a bit more advanced, but the Ember guides have some good examples.
https://guides.emberjs.com/v2.13.0/templates/links/

Pinterest Style Routes

I'd like to implement a Pinterest style modal page for items (images/items/posts/etc...). Whichever page you are on, when you click on a item a modal with the item details pops up, and the page (feed) where you are from still sits in the background. The url in the address bar changes so you can link to the item from other sites or share it to your friends. And when you close the item modal your still at the feed.
Does Ember support this type of routing?
I know react-router has support for it, so I imagine Ember would as well as it is heavily inspired by Ember-router.
Source: https://github.com/rackt/react-router/tree/master/examples/pinterest
"The url /pictures/:id can potentially match two routes, it all depends on if the url was navigated to from the feed or not."
"Click on an item in the feed, and see that it opens in an overlay. Then copy/paste it into a different browser window (Like Chrome -> Firefox), and see that the image does not render inside the overlay. One URL, two session dependent routes and UI :D"
Linked Ember.js discussion: http://discuss.emberjs.com/t/pinterest-style-routes/8487
I think there is no such native behavior since one path will match one route, but it can be managed with some outlets and conditional display and nested routing.
The following router will provide almost everything needed to perform what you want:
// router.js
import Ember from 'ember';
import config from './config/environment';
var Router = Ember.Router.extend({
location: config.locationType
});
Router.map(function() {
this.route('pictures', function() {
this.route('picture', {
path: '/picture/:picture_id',
resetNamespace: true
});
});
});
export default Router;
With route nesting, you should be able to display the pictures list if provided by the pictures route (and show link to single result), choose single picture display (popup vs single result) based on the presence or not of the results (picture route can access pictures model with this.modelFor('pictures')), and still get the benefits of the identifiable resource URL pattern.
YMMV, but you can also juggle with the picture's route renderTemplate method to render the result in specific outlet if you want to completely separate single picture rendering from pictures tree.
EDIT: link to react example is broken, did you mean https://github.com/rackt/react-router/tree/master/examples/pinterest?
EDIT2: Adding route to a modal/overlay in Ember.js

Ember CLI path based on server response

I'm developing a substantial ember app, using Ember CLI, and I'm struggling with a few aspects of it.
What I want to do is:
Show a dropdown list of options
When the user picks an option, post their choice to the backend
The response from the server contains data based on what the user picked in the dropdown. After getting the server response I want to transition to a new route where the path ends with one of the values returned by the server.
For example:
/path/to/dropdown -- shows the dropdown for the user to pick from, which is then POSTed to the backend. The backend responds with, amongst other data:
slug: <stringValue>
This then transitions to:
/path/to/slug -- where slug is <stringValue>
So far I've got 1 & 2 above working, but I can't figure out how to make step 3 work. I've tried using a serialize function in the /path/to/slug route and the /path/to/dropdown controller, but it always returns undefined.
The AJAX call to the server, based on the user's dropdown choice, happens in the /path/to/dropdown controller.
I've set up the router as:
this.route('options', { path : ':slug' });
Would be great if someone could point me in the right direction; I hope my example is clear enough but let me know if not.
Thanks.
To be honest I don't understand why do you use this.route('options', { path : ':slug' });. You just created the only route for all possible options (in fact, for all urls of form /anything), but that's not what you want.
I think your solution is this.transitionToRoute(url_string) which is available in any controller. Check the api example there. But before you should declare all that routes in the router and create operating files for them, of course.
If you don't want to create a route for each possible slug, so then your route is pretty excellent, but at least I'd consider to add one more path section. For example, this.route('options', { path : '/slugs/:slug' });.
After that you can run transitionTo and pass data (in any format) to it. The data will be assigned to the route model and you will be able to use it in the SlugRoute (and SlugController, if you didn't redefined the setupControler method) as a this.get('model') variable.
If you want to run transition from the view, firstly you need to obtain the controller and send a special command to it.

Replace, not append to, the site's body content in Ember.js

I had the following idea: my page at example.org serves classic HTML from the server. Besides, EmberJS is loaded, too, and waiting to come into action:
as soon as somebody hits an ember route then, for example example.org/#/login, the current should be replaced by what the view renders for it. From then, the whole app should serve as one-page-app.
Is that a good idea? Anyway, I don't know how to get that started. Overriding View's appendTo method or setting the rootElement property as in http://emberjs.com/guides/configuring-ember/embedding-applications/ does not suffice because if that were the body, the view output is just appended thereā€¦
If your entire Ember application requires a user to be logged in, it is valid to have two separate "apps":
A regular non single-page application (server-side using rails, PHP or C#) with a sign up and login pages
A single-page application (i.e. Ember) send as soon as the user hits the login button in your regular app
You will have 2 index.html pages, one for each application (and it's okay to do that!). The URL of the Ember App could be under example.org/app/.... You will need to configure the router of the regular application to server your Ember App for all URLs starting with /app/.
Does that help? :)

How to change a URL in the Ember.js router before a route is assigned?

My Ember.js app requires backwards compatibility for links that went to specific file extensions. (i.e. .pdf) In other words, I an example link like this, to return the PDF:
http://www.example.com/docs/my.pdf
I'm trying to preprocess the URL to remove the .pdf before the Ember.js Router assigns a Route to it, by taking a substring, and assigning it back as the URL to process:
http://www.example.com/docs/my
Obviously, getting the substring is trivial, but I don't know how to inject the updated URL back into the Ember.js Router.
Your approach is non-ember compliant from the start. The solution isn't to update the URL, but to simply use the URL you want in the first place.
Instead of sending the user to
http://www.example.com/docs/my.pdf
Send them to
http://www.example.com/docs/my
And then within one of the related route hooks (beforeModel hook would be my preference), send the my.pdf file to the user.
You could just use transitionTo().
Alternatively this answer suggests using
Ember.HistoryLocation.replaceState(<string>);
or
router.replaceWith('index');