I'm working with a design that has a couple of sections on a single state, and I'd like to be able to link to each section individually.
Is there a way I could render a single template with no outlets at a base level, and then trigger a scroll when transitioning into any of the sub routes?
Additionally, is there a way I could prevent a transition from altering the browsers history, so I could transition around states as the user scrolls without forcing them to hit back several times to escape the page?
The main application template must have an outlet. Else you won't be able to render anything unless you want to do manual rendering with {{render}}. You could put some logic in the template to use with {{render}} but it seems overkill to avoid the convenience of {{outlet}}
You can model your state as a sub state with routes like, post, post/new, post/delete which correspond to routes nested in a post resource.
But, I don't think you can selectively use a route and not affect the url. You can only set the location to none to turn off location changes completely.
I'd just make sure the UI has a contextual back button that takes the user back to the previous state, skipping over states as necessary. So users don't have to rely on the browser's back button too much.
Related
I am trying to build a UI with left side bar having filters and right side having actual filtered data.
For loading data into the dynamic part of the UI(right side), which approach is considered better in terms of code quality and app performance ?
Use sub routes (for dynamic part of the UI)
Use separate components that load their own data (for dynamic part of
the UI)
There is not a direct correct answer for that; you can use both ways but here is a few things to consider and in the end I generally prefer to use sub-routes due to the following:
Waiting for UI to load: In case you are using separate components to load their own data; then you need to handle the loading state of the components. What I mean is; if you simply use sub-routes; then model hooks (model, beforeModel, etc.) will wait for the promises to be solved before displaying the data. If you simply provide a loading template (see the guide for details) it will be displayed by default. In case you use components, you might need to deal with displaying an overlay/spinner to give a better UX.
Error Handling: Similarly like loading state management; Ember has already built in support for error handling during route hook methods. You will need to deal with that on your own if you prefer components to make the remote calls. (See guide for details)
Application State: Ember is SPA framework; it is common practice to synchronize application state with the URL. If you use sub-routes; you can simply make use of the query parameters (see the guide for details) and you will be able to share the URL with others and the application will load with the same state. It is a little bit trickier to do the same with components; you still need to use query parameters within the routes and pass the parameters to the components to do that.
Use of component hook methods: If you intend to use the components then you will most likely need to use component hook methods to open the application with default filter values. This means you will need to make some remote call to the server within one or more of init, willRender, didReceiveAttrs component hook methods. I personally do not like remote calling within those methods; because I feel like this should better be done within routes and data should be passed to the components; but this is my personal taste of coding that you should approach the case differently and this is perfectly fine.
Data down, actions up keeps components flexible
In your specific example, I'll propose a third option: separate components that emit actions, have their data loaded by the route's controller, and never manipulate their passed parameters directly in alignment with DDAU.
I would have one component, search-filter searchParams=searchParams onFilterChange=(action 'filterChanged'), for the search filter and another component that is search-results data=searchResults to display the data.
Let's look at the search filter first. Using actions provides you with maximum flexibility since the search filter simply emits some sort of search object on change. Your controller action would look like:
actions: {
filterChanged(searchParams){
this.set('searchParams', searchParams);
//make the search and then update `searchResults`
}
}
which means your search-filter component would aggregate all of the search filter fields into a single search object that's used as the lone parameter of the onFilterChange.
You may think now, "well, why not just do the searching from within the component?" You can, but doing so in a DRY way would mean that on load, you first pass params to the component, a default search is made on didInsertElement which emits a result in an action, which updates the searchResults value. I find this control flow to not be the most obvious. Furthermore, you'd probably need to emit an onSearchError callback, and then potentially other actions / helper options if the act of searching / what search filter params can be applied together ever becomes conditionally dependent on the page in the app.
A component that takes in a search object and emits an action every time a search filter field changes is dead simple to reason about. Since the searchParams are one-way bound, any route that uses this component in it's template can control whether a field field updates (by optionally preventing the updating of searchParams in an invalid case) or whether the search ever fires based of validation rules that may differ between routes. Plus, theres no mocking of dependencies during unit testing.
Think twice before using subroutes
For the subroutes part of your question, I've found deeply nested routes to almost always be an antipattern. By deeply, I mean beyond app->first-child->second child where the first child is a sort of menu like structure that controls the changing between the different displays at the second child level by simple {{link-to}} helpers. If I have to share state between parents and children, I create a first-child-routes-shared-state service rather than doing the modelFor or controllerFor song and dance.
You must also consider when debating using children route vs handlebars {{if}} {{else}} sections whether the back button behavior should return to the previous step or return to the route before you entered the whole section. In a Wire transfer wizard that goes from create -> review -> complete, should I really be able to press the back button from complete to review after already having made the payment?
In the searchFilter + displayData case, they're always in the same route for me. If the search values need to be persistent on URL refresh, I opt for query params.
Lastly, note well that just because /users/:id/profile implies nesting, you can also just use this.route('user-profile', { 'path' : 'users/:id/profile' }) and avoid the nesting altogether.
I know this question might seem a little duplicate but the other version of this question is old and some of the content (such as Views) aren't even a part of ember anymore.
I'm about 4 weeks into my internship as a front-end developer working with EmberJS. I still don't understand when it's better to use the route over the controller or vice-versa. It seems to me that every action in the route can also be used in the controller.
The one recent thing I heard was that ember routes should be stateless where as controllers should be stateful.
What is the current state of controllers and routes. When should one be used over the other?
Consider the following example to understand the state of a controller (or route, or anything), in simple terms and in current context -- lets say you have a page (like a form) with three tabs; each tab can be considered as a state - it would call different components based on the state (or the tab you are in). Now if you would happen to go back for some reason, and hit the form link again, you would see that the state would remain the same. (if you were on tab 2 when you hit back, on returning to the form, you would still be on tab 2).
So to maintain these states, controllers are the way to go, since they are singletons. Route would have lost that information, and started fresh. So basically your variables/objects in a controller would define the 'state'.
Route-actions can be as easily used as controller actions- see https://github.com/DockYard/ember-route-action-helper. So if your template for this route is just using model as the object directly, and you don't need to maintain the 'state', you can pretty much do without your controller.
But if your template was using variables which needed manipulation, you would need controller.
Hope this helps!
In the Aurelia SPA template it assumes that every page will be inside app.html and use the same nav-bar at the top. But I'll have many pages that don't want the nav-bar at the top and actually not use app.html at all. I was looking at main.js and it looks like I could hook into bootstrap() and change the aurelia.setRoot() for certain pages (I'm just guessing here), but then I start mucking up the main.js file and it won't be long before it gets really messy and maintenance headache. I really just want to have some pages use their own format altogether and ignore the app.html formatting without doing any crazy configurations.
My initial thought is maybe app.html should just be an empty file and make every page decide whether or not it wants the nav-bar and include it on the individual pages. But now I'm duplicating the code across many pages and if the standard page layout changes I have to change all the individual pages. Not sure the best way to go about his. Any suggestions?
I actually disagree with Gilbert's answer. Using .setRoot is a best practice; the root is just the parent view/viewModel pair and you will often have different parents. This is essentially what you're doing when you're creating an empty app view/viewModel, creating a new parent that doesn't do anything. But adding an unnecessary, unused layer is just extra complication.
One of the best use cases for this is a login page. The login page is totally different from your normal app page--there's a login prompt, no navigation bar, etc. Therefore, make a "login" app root and a "app" app root and switch back and forth between them. I've built a template called Sentry that demonstrates how to do this.
Sentry in action
Sentry on GitHub
Using set root, like you said, is a bad idea. Similar to what you said, you can make app.html contain just the router view tag. Different parts of your app, that you want to share a similar page layout, will be gruped under different routes. Now each of theses routes will point to another router that will have its different styles in the view
Just think of it as an empty app.html with child app.html's that have styles in them(e.g. Different navbars, page layout etc)
Scenario:
You have something like a wizard. The user completes something on the first page, click next and go to second page and so on until he reaches the last page. Here he clicks finish and all his input is stored and persisted using an ember model.
There are two other questions similar to this one:
Ember.js wizard control
Multi-step form (or multiple "pages") in one route using Ember.js
At first I've tried with a route/controller/view for each step, but since the answers are basically a controller's state variables and get lost while transitioning, it is obvious that it cannot work like this!
Then I took the approach described in the above links. One route, one controller, one template with lots of {{#if }} so that I show only the fields of the current step. I think I might improve this by using partials and so each step will have its own template.
The question is: is this the only/the best approach? Does anyone figured out a better way to implement such a flow?
If you make each wizard page a component and then pass the model as a template parameter to each component, you get a pretty nice workflow. The URL is stable (not constantly adding junk onto the end in order to pass state around); it's easy to drop the user into the first step whenever they enter the route (such as manually inputing the URL or perhaps more importantly, finishing the wizard and then hitting the browser's back button); and you can perform validation on each page of the wizard.
Check out my longer answer here
One of the possible approaches would be using Query Parameters so that you can manage a state of each step in a single wizard controller.
I am looking into ember.js, after working with SproutCore 1 previously. I am looking for some examples on how to add and remove views from the DOM as the user navigates the application.
For instance, I have an application that contains a set of cases and each case has a workflow. There are also administration pages, etc.
When the user starts up the app, a dashboard-like user interface is shown. From here the user is able to search or click on a case in order to bring up that case. At this point I want to do the following:
I want to remove the GUI for the Dashboard, and i want to show the GUI for the case - which is a complex GUI in itself with its own set of navigation rules etc.
Also, within the case I want to add and remove portions of the GUI as the user navigates and manipulates the case.
When the user clicks on the "Dashboard" link, I want the current GUI to be removed, and the dashboard to be added again.
As this will be a somewhat large application I am not sure if toggling the isVisible parameter is sufficient, or if other measures needs to be taken in order to not overload the user's browser.
Is there a guide, or an example that shows how to do this ?
WARNING: OUTDATED ANSWER
A view inherits from Ember.View which means it gets some key methods. append(), which appends to body, appendTo(arg) which takes an argument and remove().
The argument is a jQuery style selector of where to insert the element in the DOM.
// my view
App.PartsView = Ember.View.extend({
...
});
// create/insert my view
App.partsView = App.PartsView.create();
App.partsView.appendTo('#partcontainer');
In my code I have a <div id="partcontainer"></div>.
// remove from DOM
App.partsView.remove();
The documentation has a good part on Building a View Hierarchy and later a section on Ember.ContainerView depending on whether you want to do it all programatically or not.