How to access the params in controller ember.js - ember.js

This is my router.js code.
this.route('contact',{'path': '/contact/:chat_id'});
and This is my route.js code.
model(params) {
return this.store.findRecord("chat", params.chat_id)
},
and This is my controller.js code and can i use like this. It shows error as null value
please help me. How to use params in controller
recordChat: function() {
var chat = this.get(params.chat_id)
Ember.RSVP.hash({
offer_id: chat,
})
}

Edited for 2021: There's actually probably a much simpler way to do this now in Ember. See https://guides.emberjs.com/v3.27.0/routing/query-params/
Original Answer
I think the simplest answer is to create a variable in route and then set it in the setupController:
your_route.js
export default Ember.Route.extend({
model(params) {
this.set('myParam',params.my_Params);
// Do Model Stuff...
},
setupController(controller, model) {
// Call _super for default behavior
this._super(controller, model);
// Implement your custom setup after
controller.set('myParam', this.get('myParam'));
}
});

In your route.js, you need to call the setupController function like this:
setupController(controller, model) {
this._super(...arguments);
controller.set('chat', model);
}
Now you have access to it in your controller by calling:
recordChat() {
const chat = this.get('chat');
// if you don't call the setupController function, you could also do:
// const chat = this.get('model') or this.get('model.id') if you want the id only
}
UPDATE:
See working twiddle here

I think you can fetch the id you want from the model because you used the parameter chat_id to find the record and that id is now part of your chat object (which is the route model itself).
So, in your controller you just need to do: this.get('model').id

Related

Ember - How to reload route model after save/update records

I want to reload my route model after save/update action in the route.js file. Following is my route model.
export default Ember.Route.extend({
model() {
return Ember.RSVP.hash({
employeeList : this.store.findAll('employee'),
employee : this.store.createRecord("employee"),
});
}
I tried following and it doesn't worked.
this.refresh(); // I saw this in many posts.
Can anyone suggest how to reload a model after save/update operation?
Try to use store.push method.
"push is used to notify Ember Data's store of new or updated records that exist in the backend. This will return a record in the loaded.saved state"
store.push(store.normalize('employee', savedEmployee));
More: http://emberjs.com/api/data/classes/DS.Store.html#method_push
I'm wondering if it's a timing thing. New records show up in the data store for me great when I use a subroute...i.e. routes/employees.js returns this.store.findAll('employee') while routes/employees/new.js returns this.store.createRecord('employee').
If a new route isn't an option, perhaps a promise will help?
export default Ember.Route.extend({
model() {
// forces a createRecord after the store is already loaded with all employees
var newEmployeePromise = new Ember.RSVP.Promise((resolve, reject) => {
let allEmployees = this.store.findAll('employee');
allEmployees.then(() => {
resolve(this.store.createRecord('employee'));
}, (error) => {
reject(error);
});
});
return Ember.RSVP.hash({
employeeList : this.store.findAll('employee'),
employee : newEmployeePromise,
});
}
}
you could use setupController hook to fix the issue.
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return Ember.RSVP.hash({
employeeList : this.store.findAll('employee'),
employee : this.store.createRecord("employee"),
})
setupController(controller, model) {
controller.set('employeeList', model.employee);
}
});
Hope this help you !!!
Creating new record through this.store.createRecord() ,will update the store automatically, so you don't need to refresh the page, since it will update ui automatically.
If you still you need to update the page, like you said you can call this.refresh().. You please have a look at twiddle
If you provide ember-twiddle for non working.it would be good.

How can I set all route have the same method or action when I use ember?

My ember application use 1.11.3 version and ember-simple-auth(0.7.3). All of route must set the AuthenticatedRouteMixin and get the account infomation use beforeModel, I think is not good way to do this. Example:
App.Route = Ember.Route.extend(AuthenticatedRouteMixin, {
beforeModel: function() {
Account.find().then(function(user) {
this.set('user', user);
})
},
setupController: function(controller, model) {
controller.set('model', model);
controller.set('user', this.get('user'));
}
});
App.ApplicationRoute = App.Route.extend();
I set a jsbin: http://emberjs.jsbin.com/wipasusige/1/edit?html,js,console,output
If my all route use App.Route.extend();, it is has problem is AuthenticatedRouteMixin can not work.
I use Route.extent set all to route beforeModel to get the account infomations is a good way?
Maybe there is any better way to approach this problem?
As this customization makes sense only with the AuthenticatedRouteMixin, it makes sense to perform it in the mixin itself!
App.CustomAuthenticatedRouteMixin = AuthenticatedRouteMixin.extend({
beforeModel: function() {
// We do not want the default functionality to be removed
var superResult = this._super(transition);
var accountPromise =
Account
.find()
.then(function(user) {
this.set('user', user);
return superResult;
}.bind(this));
return accountPromise;
},
setupController: function(controller, model) {
controller.set('model', model);
controller.set('user', this.get('user'));
this._super(controller, model);
}
});
Then reuse it in routes as you would normally do with the AuthenticatedRoutMixin.
Mixins allow composing classes from various functionality sources which is a more flexible approach than inheriting from a single source class.
Disclaimer: the above code is just a concept, not a fully working solution. You'll have to think how to implement it in your project.

`needs` not waiting for data to be returned before rendering template

I am trying to implement a controller needing another (CampaignsNew needing AppsIndex), which looks like
App.CampaignsNewController = Ember.Controller.extend({
needs: ['appsIndex']
});
And in my CampaignsNew template I am showing it via
{{#if controllers.appsIndex.content.isUpdating}}
{{view App.SpinnerView}}
{{else}}
{{#each controllers.appsIndex.content}}
{{name}}
{{/each}}
{{/if}}
However controllers.appsIndex.content.isUpdating is never true. I.e. it attempts to show the data before it has been loaded.
My AppsIndex route has the model overridden:
App.AppsIndexRoute = Ember.Route.extend({
model: function(controller) {
var store = this.get('store').findAll('app');
}
...
});
I can get it to work if I put the same code within my CampaignsNew route and modify the template to each through controller.content. Which says to me that needs is not using the route? It also works if I go to the /apps page and it loads the data, and then navigate to the /campaigns/new page.
How do I get this to work? Thanks!
Edit:
As requested, the relevant parts of my router:
App.Router.map(function() {
this.resource('apps', function() {
...
});
this.resource('campaigns', function() {
this.route('new');
});
});
And the AppsIndex is accessed at /apps and CampaignsNew is at /campaigns/new
Edit2:
After implementing the suggestion by #kingpin2k, I've found that Ember is throwing an error. Below are the updated files and the error received.
App.CampaignsNewController = Ember.ObjectController.extend({
pageTitle: 'New Campaign'
});
App.CampaignsNewRoute = Ember.Route.extend({
model: function(controller) {
return Ember.RSVP.hash({
campaign: this.store.createRecord('campaign'),
apps: this.store.find('app')
});
// return this.store.createRecord('campaign');
},
setupController: function(controller, model){
controller.set('apps', model.apps);
this._super(controller, model.campaign);
}
});
Ember throws this error:
Error while loading route: Error: Assertion Failed: Cannot delegate set('apps', <DS.RecordArray:ember689>) to the 'content' property of object proxy <App.CampaignsNewController:ember756>: its 'content' is undefined.
I read online that this is because the content object doesn't exist. If I set it like so:
App.CampaignsNewController = Ember.ObjectController.extend({
content: Ember.Object.create(),
...
});
Then the page loads without error, and when inspecting the Ember Chrome extension, I can see the data has loaded. But it doesn't show on the page. Which I suppose happened because the content object existed and so Ember didn't wait for the model's promise to fulfill before rendering the template. Seems odd that you should have to define content in such a way though. Any insight on how to handle this?
Edit3: Question answered for me in another thread
Based on your router, apps isn't a parent of campaigns/new.
This means someone could hit #/campaigns/new and Ember would hit ApplicationRoute, CampaignsRoute, and CampaignsNewRoute to populate the necessary information for the url requested. Using needs as a way of communicating between controllers really only makes sense in an ancestral pattern (aka communicating with your parents, grandparents etc).
Just as another quick note, AppsIndex is a route of Apps, it won't be hit when your url includes a child. e.g.
Router
this.resource('apps', function() {
this.resource('chocolate', function(){
.....
});
});
Url being hit
#/apps/chocolate
Routes that will be hit
ApplicationRoute
AppsRoute
ChocolateRoute
ChocolateIndexRoute
The index route is only hit when you don't specify a route of a resource, and you are hitting that exact resource (aka nothing past that resource).
Update
You can return multiple models from a particular hook:
App.FooRoute = Em.Route.extend({
model: function(){
return Em.RSVP.hash({
cows: this.store.find('cows'),
dogs: this.store.find('dogs')
});
}
});
If you want the main model to still be cows, you could switch this up at the setupController level.
App.FooRoute = Em.Route.extend({
model: function(){
return Em.RSVP.hash({
cows: this.store.find('cows'),
dogs: this.store.find('dogs')
});
},
setupController: function(controller, model){
controller.set('dogs', model.dogs); // there is a property on the controller called dogs with the dogs
this._super(controller, model.cows); // the model backing the controller is cows
}
});
Check out the second answer here, EmberJS: How to load multiple models on the same route? (the first is correct as well, just doesn't mention the gotchas of returning multiple models from the model hook).
You can also just set the property during the setupController, though this means it won't be available when the page has loaded, but asynchronously later.
Which controller?
Use Controller if you aren't going to back your controller with a model.
App.FooRoute = Em.Route.extend({
model: function(){
return undefined;
}
});
Use ObjectController, if you are going to set the model of the controller as something, that isn't a collection.
App.FooRoute = Em.Route.extend({
model: function(){
return Em.RSVP.hash({
cows: this.store.find('cows'),
dogs: this.store.find('dogs')
});
}
});
Use ArrayController if that something is going to be a collection of some sort.
App.FooRoute = Em.Route.extend({
model: function(){
return ['asdf','fdsasfd'];
}
});
Note
If you override the setupController, it won't set the model of the controller unless you explicitly tell it to, or use this._super.
App.FooRoute = Em.Route.extend({
model: function(){
return Em.RSVP.hash({
cows: this.store.find('cows'),
dogs: this.store.find('dogs')
});
},
setupController: function(controller, model){
controller.set('cows', model.cows);
controller.set('dogs', model.dogs);
// uh oh, model isn't set on the controller, it should just be Controller
// or you should define one of them as the model
// controller.set('model', model.cows); or
// this._super(controller, model.cows); this does the default setupController method
// in this particular case, ArrayController
}
});

What's the proper way to access parameters from within Ember.Route. setupController?

Ember.Route.model has access to the params variable, but Ember.Route.setupController does not. This is troublesome for me, because my path has multiple dynamic segments, and I need to use all of them in my template.
Specifically, my path looks like this: /project/:project_id/task/:task_id. Note that a task can belong to many projects, not just one. Therefore we can't tell what project we're looking at just be looking at the task itself: we have to use the project ID found in the URL. Here's how I'm doing it currently:
App.TaskRoute = Ember.Route.extend({
// This works just fine:
serialize: function(model) {
return {
task_id: model.get('id'),
project_id: this.modelFor('project').get('id')
};
},
model: function(params) {
this.set('parentProjectId', params.project_id);
return App.Task.find(params.task_id);
},
setupController: function(controller, model) {
var parentProject = this.modelFor('project') ?
this.modelFor('project') :
App.Project.find(this.get('parentProjectId'));
controller.set('parentProject', parentProject);
controller.set('content', model);
}
});
Maybe I'm being paranoid, this just feels hacky. If the route was meant to have access to the parameters, then it would already have a params property attached to it. Is there a better way?
EDIT: I made some update to the code above. Also, my routes look like this:
App.Router.map(function() {
this.resource('project', { path: '/project/:project_id' });
this.resource('task', { path: 'project/:project_id/task/:task_id' });
});
You have no access to these params in the setupController hook. The model hook has access to a params object, because it is just called, when your app is entered via URL.
Your code looks quite fine, it you really know, that you want to do it this way. What does feel hacky to you about it? When looking at this code, i am asking myself why you did not split the logic of your Route into a ProjectRoute and a subordinated TaskRoute. Wouldn't that work for you?
Update: Response to your changes
Nesting resources is likely the key to success in your case:
App.Router.map(function() {
this.resource('project', { path: '/project/:project_id' }, function(){
this.resource('task', { path: '/task/:task_id' });
});
});
Since the TaskRoute is nested not you have to rename it to ProjectTaskRoute:
App.ProjectTaskRoute = Ember.Route.extend({
...
});
This should enable you to remove the parentProjectId property from the Route.
Since Ember 1.8, the Route class has a paramsFor function:
import Route from '#ember/routing/route';
export default Route.extend({
setupController(controller) {
this._super(...arguments);
const params = this.paramsFor('name.of.your.route')
}
});

Ember js pre 4 - router not resolving id on link click

I have a list of accounts and then i have a view link to view an account in detail and this is the account route. When i click the view link the (guid) doesnt update when going through the router, it only updates in the URL but it doesnt seem to be carrying through to the code.
When i do a browser refresh then the (guid) gets carried through to the router... Its not resolving for some reason.
Im not using ember-data but will use it in the future.
Here is my "Accounts" template code with the "View" link:
{{#each accountdata in controller}}
<tr>
<td>{{accountdata.accountnumber}}</td>
<td>{{accountdata.accountname}}</td>
<td>{{accountdata.accounttypestatus}}</td>
<td>{{accountdata.accountuser}}</td>
<td>{{#linkTo account accountdata}}View{{/linkTo}}</td>
</tr>
{{/each}}
accountdata is the context with the "accountguid" which is my id.
Here is my router:
App.Router.map(function() {
this.resource("accounts", { path: '/accounts' });
this.resource("account", { path: "/accounts/:accountguid" });
});
App.AccountsRoute = Ember.Route.extend({
setupController: function(controller, model) {
controller.set('searchfilter','ALL');
controller.search();
}
});
App.AccountRoute = Ember.Route.extend({
setupController: function(controller, model) {
controller.show(controller);
},
model: function(params) {
this.controllerFor('account').set('accountguid',params.accountguid);
},
serialize: function(model) {
return {accountguid: Em.get(model, 'accountguid')}
}
});
My controller.show is where i send the context to call a script to display the account details.
So i just need view to carry through the correct accountguid each time which it isnt and then to call the show(context) method.
Thanks
This is my old router code which worked 100%. When i clicked a link it resolved the :accountguid and when i did a browser refresh it did the same thing. i had no problems, everything just worked.
// //Accounts
// accounts: Ember.Route.extend({
// route: '/accounts',
// index: Ember.Route.extend({
// route: '/',
// connectOutlets: function (router) {
// router.get('applicationController').connectOutlet('accounts');
// router.get('accountsController').set('searchfilter','ALL');
// router.get('accountsController').search();
// }
// }),
// view: Ember.Route.extend({
// route: '/:accountguid',
// connectOutlets: function (router, account) {
// router.get('applicationController').connectOutlet('account', account);
// router.get('accountController').show(account);
// //router.get('accountController').connectOutlet('eventloghistory','eventloghistory');
// }
// })
// }),
I managed to solve my problem with the following code. I am now able to refresh the browser and i am able to click the link and it will carry through the current :accountguid in use to the show() method.
App.AccountRoute = Ember.Route.extend({
model: function(params) {
return {accountguid: params.accountguid};
},
setupController: function(controller, model) {
controller.show(model);
},
serialize: function(model) {
return {accountguid: Em.get(model, 'accountguid')}
}
});
Change your router map to the following and it should work
App.Router.map(function() {
this.resource("accounts", function(){
this.resource('account',{path:':account_id'});
});
});
Please, show us the code behind your AccountsController and AccountController. It would be most useful if you provide a jsfiddle with the whole construction.
In general, you may be unaware of the new flow of things. Here is what happens in the two scenarios:
1. You navigate to the AccountRoute by setting the URL (/account/5 for example).
1.1. the 'model' hook of the AccountRoute is called
model: function(params) {
return your model here...
}
with params = { accountguid: 5 }. Because you are not using ember-data, you should implement this hook and initialise and return the model there.
1.2. the setupController hook is called with the AccountController and the model returned by the model hook. Without the code behind
controller.show(controller);
It is not quite clear what its purpose is, but you should probably do something like
setupController: function(controller, model) {
this._super(controller, model);
controller.set('content', model);
controller.show(model);
}
As you can see, by not implementing the model hook, you URL stays correct, but the route does not know how to build the needed model resource.
You transition to the route via a linkTo call
{{#linkTo account accountdata}}View{{/linkTo}}
Here, linkTo expects accountdata to be the full account model for the route. Meaning it may not carry only partial data like id for example (read this for clearance: In latest Ember, how do you link to a route with just the id/name of a model, rather than providing all of its attributes in the linking page?).
2.1. The model hook of the AccountRoute is NOT called. The AccountRoute model property is set to the object that is passed to linkTo instead (in our case 'accountdata').
2.2. setupController is called with AccountController and the accountdata object.
If you accountdata object is not complete, it would be wise to create a complete instance here and set it to the controller.
As you can imagine, if you accountdata is like { id: 5, accountname: "John", accounttypestatus: "A", ...}, then after a click on linkTo, the URL will update correctly to /account/5, but the account template will receive accountdata, rather than an actual account.
P.P. If none of the above is any help, this might be your issue: https://github.com/emberjs/ember.js/issues/1709