I am trying to make a simple app using Ember.js. I am using yeoman ember.
I have following application template
<h1>Dashboard</h1>
<div class="content">{{outlet}}</div>
I have a few routes defined. Now what I am not being able to do is, find a way to update the text inside <h1> for different routes. For example,
if I navigate to /#/users I need it to change to Users.
if I navigate to /#/users/1 I need it to change to the model's firstname.
I tried this in ApplicationRoute
setupController: function(controller){
controller.set('title', 'Dashboard');
}
and changing to <h1>{{title}}</h1>
but it only works for ApplicationRoute and no other route!
setupController: function(controller){
controller.set('title', 'Users');
}
This does not work in UsersRoute. Even the output of the route disappears.
What is the right approach to get this to work!
How about you simply create an action in ApplicationRoute that sets the title as passed to it as an argument, and then have your sub route call that action in setupController. That should do the trick.
So in you application route:
//in application route
actions:{
updatePageTitle: function(title){
this.controllerFor('application').set('title', title);
}
}
And in your individual child route:
setupController:function(controller, model){
this.send('updatePageTitle', model.get('something'));
this._super(controller, model);
}
Related
This is very basic question but i am not finding it any where.
As per my understanding before rendering template correspondin route or component.js's beforeModel() model() etc functions gets called.
What i want to do:
I want to show image of logged in user on my sidenav. User's data is stored in local storage.
My problems here
I am hoping that setting a variable in model and returning the same will solve my problem, but my model method is not being called at all.
My Code:
Template:
{{#paper-sidenav
class="md-whiteframe-z2"
name="right"
open=leftSideBarOpen2
lockedOpen=leftSideBarLockedOpen
position="right"
onToggle=(action (mut leftSideBarOpen2))}}
{{#paper-toolbar as |toolbar|}}
{{#paper-toolbar-tools}}
<img src="http://example.com/users/{{model.username}}.jpg" />
{{/paper-toolbar-tools}}
{{/paper-toolbar}}
{{#paper-content padding=true}}
Çup?
{{/paper-content}}
{{/paper-sidenav}}
Component.js
import Ember from 'ember';
export default Ember.Component.extend({
beforeModel(){
},
model(){
let user = localStorage.get('user');
console.log(user.username);
return user;
},
actions:{
toggle(propName) {
this.toggleProperty(propName);
}
}
});
In console i am getting error "GET http://example.com/users/.jpg 404 (Not Found)", which certainly says that user.username in my template evaluates to null. i tried debugging my model method in chrome's dev tool and found that model is not getting called
Could you please let me know where i'm mistaking?
Ember component does not have model() and afterModel() hooks, what you need to do, is first access the local storage data in afterModel() hook in route and pass the resolved model to component.
Main Route
export default Ember.Route.extend({
model(){
//load data
},
afterModel(model){
//access local store and set to model
model.set('users',data);
}
});
Main route hbs
{{component componetModel=model}}
inside component
import Ember from 'ember';
export default Ember.Component.extend({
init(){
this.set('users', componetModel.users);
}
});
component hbs
{{#each user in users}}
{{user.name}}
{{/each}}
You really have to read the documentation about component. It clearly state what are the lifecycle hooks for each stage,
init
update
delete
https://guides.emberjs.com/v2.11.0/components/the-component-lifecycle/
The only thing the template have access to from route is model so that you can use model directly inside the corresponding template OR pass it to the component(s).
If you need other property for your component you can either set them inside route's setupController(controller, model) hook OR create a controller for that route.
Illustration below is a sample for one specific route,
NOTE: Dashed line represents the runtime generate controller
See image above each "layer" only have access the property OR action direct inside the upper layer (special case is route auto inject model into runtime generated controller).
You do NOT need to specifically define your own controller because if the route cannot find one it will generate one at runtime.
The component is completed isolated from literally anything (unless you inject anything into it). The only way to work with the component is to pass data and action (usually used to handle events) to it.
Summary
(1) The component can only access the data you specifically passed to it (see code below).
{{component-name
internalName=externalName
}}
(2) The template can only access the data that is available inside the controller, either auto-generated one or via ember g controller controller-name. (model property is an exception)
(3) If you want to your template to have route data, using setupController hook.
Inside an application we allow users to create new records, related to an existing record. To achieve this, we use actions something like this:
createUser() {
var route = this;
var model = this.store.createRecord('user', {
client: route.modelFor('client'),
});
route.transitionTo('user.update', model);
},
The user.update route renders a user-form component, using the model that was passed in the transition. The same route is also used to update existing users.
The issue with this approach is as follows; when refreshing the page, the page errors because the route fails to find the respective record when querying the store (at this point, the URL is /users/null/update). Ideally I'd pass the client (or client.id) argument in the URL so that:
The page can be reloaded without issue.
The client associated with the user is set correctly.
How can I achieve this in Ember.js? I know that this can easily be done using nested routes (by nesting the user.update route inside a client route), but this doesn't make sense visually.
The relevant parts of the router are as follows:
this.route('clients');
this.route('client', {path: 'clients/:id'}, function() {
this.route('users');
});
this.route('user', {path: 'users/:id'}, function() {
this.route('update');
});
All I do in the user/update.hbs template is {{user-form user=model}}
The problem is that the model you just created has no id at that point because it is not saved, ember can´t route to a model without an id, if possible save the model before you try to transition to the route, if you don´t want to save the model because the user can cancel the action check this thread where a user had the same problem (if I understand you problem correctly), I provided a solution for that problem that I´m using in my own project
https://stackoverflow.com/a/33107273/2214998
I am accessing the query parameters in the route using the below code:
export default Ember.Route.extend({
afterModel: function(params, transition){
this.set('clientId', transition.queryParams.clientId);
},
setupController: function(controller) {
controller.set('clientId', this.get('clientId'));
}
});
The reason I am not using a controller is because I am feeding this data straight in to a component and I am of the understanding that in newer versions of Ember controllers will be phased out.
This is currently working however if I change any of the properties in the URL it doesn't update in the app unless I refresh the page or exit the route and re-enter it.
How can I "re-run" the route afterModel and update the properties which are passed to the component?
If the only option is to use a controller then I can implement this until a better solution comes along.
Try adding this to your route:
queryParams: {
'clientId' : {
refreshModel: true,
replace : true,
},
}
See here for more information.
Here's a JSBin demonstrating an issue I'm experiencing. This is the code for it.
I've run into a situation where if I navigate to a route in Ember.js after the application has been loaded (e.g. using transitionTo), everything works. But if I try to navigate to the route directly (by entering the url or refreshing the page once I'm already on the route) it doesn't render correctly.
I'm using renderTemplate to place the template into a parent route. But the parent route hasn't been rendered yet when visiting the route directly, so it fails with the following error in the console:
Cannot read property 'connectOutlet' of undefined
How can I ensure that the parent route's template is in place to avoid this error?
When you render a template into another template programmatically the other template must have already been rendered. You can schedule it to render after it's finished rendering.
App.BarRoute = Ember.Route.extend({
renderTemplate: function() {
var self = this;
Em.run.schedule('afterRender', function(){
self.render('bar', {into: 'baz'});
});
}
});
Example: http://jsbin.com/qilem/1#/bar
Really you're fighting the ember router heavily here. The application template should have an {{outlet}} defined inside of it, but when you use render you've created a nested scope where the outlet no longer exists in the application template. You can either use partial or pull the {{outlet}} out of the nested template.
Example Partial: http://jsbin.com/qilem/2#/bar
Example Render: http://jsbin.com/qilem/3#/bar
in Ember.js I have route with model. Could you help me, when I'm on route playlist how to reload this route (or set new data to model) called by callback from another JS function? I've been looking to documentation so long, but no help for me.
App.PlaylistRoute = Ember.Route.extend({
setupController: function(controller, model) {
$.getJSON('api/playlist.php?' + Math.random().toString(36), function (data) {
controller.set('model', data);
});
}
});
Thanks a lot!
It seems the solution in the answer won't work for current route.
I had a same issue and tried the solution here and it worked.
http://discuss.emberjs.com/t/refresh-current-view-page-after-language-change/4291/5#post_5
In your route.
actions: {
sessionChanged: function() {
this.refresh();
}
}
and in your controller.
observeSession: function() {
this.send("sessionChanged");
}.observes("session.isAuthenticated"),
There are two ways of doing it.
One is write an action in playlist route and call this.refresh() inside it
For more information you can visit Ember Guide refresh method for route.
The other way is in your controller depending on the situation when you need to reload your route use
this.get('target.target.router').refresh();
any of the two would help you in refreshing your route.
A small note of refresh method below from ember guides:
Refresh the model on this route and any child routes, firing the beforeModel, model, and afterModel hooks in a similar fashion to how routes are entered when transitioning in from other route. The current route params (e.g. article_id) will be passed in to the respective model hooks, and if a different model is returned, setupController and associated route hooks will re-fire as well.
From a controller use transitionToRoute:
this.transitionToRoute('playlist', newModel);
From a route use transitionTo:
this.transitionTo('playlist', newModel);
For example, imagine you have an action on your controller
App.PlaylistController = Ember.ArrayController.extend({
actions: {
grabNewModel: function(){
//get some new model
this.transitionToRoute('playlist', newModel);
}
}
});
This answer appears first on google when searching how to refresh a route with the current accepted answer being out of date. If you really need to refresh a route and perform the model hook action again from an action in a controller then use the following:
In the route
#action
refreshModel() {
this.refresh();
}
In order to call this action in the controller use
this.send('refreshModel');
For example:
#action
performUpdate(event) {
event.preventDefault();
// Perform necessary action
this.send('refreshModel');
}
Note: This will send the action to the corresponding route for the controller it is called from, and update that route only and any child routes.