Pinterest Style Routes - ember.js

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

Related

Ember route and navigation in modal, keeping parent route visible

Is it possible to have a route, say images/ show a list of images, and on click have an image open in a modal/viewer, say images/viewer/10, that has navigation (previous links to images/viewer/9, next links to images/viewer/11), updating the viewer modal while keeping the background images/ behind the modal as is, so that you can cancel the modal and go back to images/ without reloading the route?
see https://ember-twiddle.com/1da304530ea1367389361213fcf839c6?openFiles=templates.images.hbs%2C
You can define your routes like this:
Router.map(function() {
this.route('images', function() {
this.route('viewer', {path: "/viewer/:id"});
});
});
Pay attention in file structure in twiddle (images parent route for outlet and viewer nested route for modal, not images index route).
And then, you can show the modal viewer template in an outlet defined in images template.
Well, when you navigate to /images, you will see images template, without the modal. And the route's hooks will have executed.
From images, if you navigate to /images/viwer/x, the viewer route hooks will execute, but not images, because have already been executed.
When you navigate from images/viewer/x to /images will do the same (images route hooks will not execute one more time).
Hope this helps.
regards

Multiple layouts in ember.js 2.7.0

I want to have two layouts, one for guests which can see some routes like: /, /contacts, /rules, etc... and another one for authenticated users, it means they must login before they can go to authorized routes. How can I define two layouts for different groups of routes?
there is a way to use Ember's router to your advantage to solve this problem by nesting the authenticated routes inside a route. Here's an example router:
Router.map(function() {
this.route('contacts');
this.route('rules');
this.route('authenticated', { path: '/' }, function() {
this.route('settings');
this.route('profile');
});
});
Going to /contacts and /rules wouldn't need any authentication, but going to /settings would.
Notice the path option passed to the authenticated route. Since we set it to / so it doesn't show up in the URL, it'll take the place of application.index. If this sounds strange to you, read about what an index page is in the Ember.js tutorial.
The answer is to maintain a service that saves the user's state.
// services/user-state
import Ember from 'ember';
export default Ember.Service.extend({
loggedIn: true
});
Then, depending on how you organized things, you could inject the service into a controller or a route. So - you'd have access to loggedIn
In your template, you would use handlebars/htmlbars if helper.
{{#if loggedIn}}
render logged-in stuff...
{{/else}}
render message explaining that this is only for logged in users
{{/if}}
You can also redirect the user to another route based on the current session data. You may have entire routes that are for certain roles, or you may have portions of your template that behave differently based on role.
If you weren't logged in, then you may not be able to visit your profile page. That route may be off limits entirely, or it may redirect you to a login page. On the other hand, maybe it's just a "login" button component or a portion of your template that shows if you aren't logged in vs. "hello sheriffderek" if you were.
There are also Ember addons for more robust "role" outlines. Think about a blog page, that may have an 'edit' button for the actual user, but 'flag' button for a moderator. In this case, you can't just have an entirely different route nested somewhere.
Most authentication addons / libraries are going to have a 'service' with some sort of session state. But Template wise, the basic idea is like a JS if/else statement.
if (helpful) {
this.upvote();
} else {
// something else
}
I hope this helps. : )
Have a look at the ember-simple-auth addon.
It has many useful classes all about authorization, including mixins for your usecase. If you want to make route only visible for logged-in users, simply use the AuthenticatedRouteMixin like this:
/app/routes/protectedRoute.js
import Ember from 'ember';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
export default Ember.Route.extend(AuthenticatedRouteMixin);

How do you update a controller from another controller in ember.js?

In my application I have common header that outlets into the main application layout. In that header you can select a site. I need that site selection to update another template that is rendered in the application layout. Having struggled with this for a few days I think the correct way to solve this is to use a shared service, to have the header controller observe the site selection and set that value in the shared service, then to have the index controller use a Ember.computed.alias on the value in the service controller. The following is an example of my code:
controllers/header.js
import Ember from 'ember';
export default Ember.Controller.extend({
sessionService: Ember.inject.service(),
currentSiteChanged: Ember.observer('session.current_site', function(){
var current_site = this.get('session.current_site');
console.log('currentSiteObserver', current_site);
this.get('sessionService').set('currentSite', current_site);
}),
});
controllers/index.js
import Ember from 'ember';
export default Ember.Controller.extend({
sessionService: Ember.inject.service(),
currentSite: Ember.computed.alias('sessionService.currentSite'),
dashboardData: function(){
var currentSite = this.get('currentSite');
console.log("in dashboardData", currentSite);
//other code that uses the currentSite
}.property('currentSite'),
});
services/session-service.js
import Ember from 'ember';
export default Ember.Service.extend({
currentSite: null,
setCurrentSite: function(){
var currentSite = this.get('session.current_site');
this.set('currentSite', currentSite );
}.on('init'),
});
I think this should allow someone to select a site in the header and have the dashboardData property in index update to use that selection. When the page initially loads the header defaults to the first site and the index renders it has the correct site value that it must have gotten from the session-service, however if you select another site the index does not get updated. Via the console.logs and debugging I can see that the header is observing the change and setting the value on the session-service.
Additionally I have tried solving this other ways (injecting the header service into the index and observing a property, injecting the index in the header and directly setting the value, sending and listening to events,etc) but I am willing to try anything or to be corrected that this isn't the correct way to solve the problem.
I am using ember 1.13.8 and moving to 2 isn't an option at the moment.
I don't think a service is an appropriate solution to this problem.
You want your application to have a good RESTful url design (respect for urls is a corner-stone of the Ember framework), so try to capture your application state in the URL.
Consider that if a user were to select a site, and then hit refresh they would lose their selection unless you stored it somehow in a cookie or localStorage.
I would recommend using either routes or query parameters to solve your problem.
Routes
Using routes is fairly straightforward (http://whatever.com/sites/pet-hampsters).
Query Params
You can also use query params, something like this http://whatever.com/?site=pet%20hampsters.
To do this you would write an action that bubbles up to your application controller and sets the value of the 'site' queryParam. Any of your sub-controllers on the currently active route can then read the site value with the new Ember.inject syntax. This is the conventional way to manage dependencies between controllers.

Transition to a route mantaining the previous content in ember

I've a generic route /login that displays the login form inside a modal. Now I want the content behind to remain the same of the previous url.
How do you go about this?
Thank you
Trying to implement login modal form as a generic /login route is probably not the best option, as it does not go well with the way Ember routes work, which can be summarised as follows:
Given url is matched to (possibly multiple nested) routes.
Each matched route resolves it's model and then renders corresponding templates to appropriate outlets. Templates for previous route are wiped out.
Navigating to generic /login would wipe out the templates rendered by previous route - namely there would be no content in the modal background (see also related question).
I would suggest either:
Not rendering /login route in modal dialog, but instead in the main app outlet, and only then redirecting user to the previous route. UI experience is probably not much worse as user probably does not need any information from the modal background anyway.
Instead of having generic /login route, you could use query parameter, e.g. ?should_login=true on ApplicationController which would render login modal in appropriate outlet. This way primary application state would still be encapsulated in the route url, thus templates for the previous would be properly rendered in the modal background. Suggested approach on how to handle modal dialog can be found here.

Routes all link to '/#/:path'

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.