Ember queryParams not updating URL - ember.js

I'm trying to set queryParams in an Ember controller, but they don't seem to be updating the URL at all.
I have this abbreviated mixin being applied to the route:
import Ember from 'ember';
import ControllerPaginationMixin from './controller-pagination';
export default Ember.Mixin.create({
setupController(controller, model) {
this._super(controller, model);
controller.reopen(ControllerPaginationMixin);
}
});
And here's the abbreviated controller mixin that is applied above:
import Ember from 'ember';
export default Ember.Mixin.create({
sortKey: null,
queryParams: ['sortKey'],
actions: {
sort(key) {
this.set('sortKey', key);
}
});
When I call the sort method from a component, I can see in the Ember Inspector that the sortKey property has been changed to the correct new value, but the URL remains unchanged. Am I missing something?

Your problem is that you're trying to customize the controller class at the runtime.
You will reopen the controller every time a user visits the route, that's ridiculous.
Simply extend the controller definition with the mixin and you're good to go.

Related

Need help to understand how ember.js Ember Data works

I am very new to ember.js.
I have the following code which I need to change to retrieve data from the server using multiple models (using multiple JSON/RESTful calls).
This (single model version) WORKS:
In app/routes/index.js:
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.get('store').queryRecord('wallet', {balance: true});
}
});
and in wallet-balance.hbs:
<div>Your Total Score: {{wallet.balance}} </div>
I changed to this and it WORKS:
import Ember from 'ember';
import RSVP from 'rsvp';
export default Ember.Route.extend({
model() {
return RSVP.hash({
wallet: this.get('store').queryRecord('wallet', {balance: true})
});
}
});
and in wallet-balance.hbs:
<div>Your Total Score: {{wallet.wallet.balance}} </div>
BUT if I change to the following ("wallet" -> "anythingelse"), it WON'T WORK:
import Ember from 'ember';
import RSVP from 'rsvp';
export default Ember.Route.extend({
model() {
return RSVP.hash({
anythingelse: this.get('store').queryRecord('wallet', {balance: true})
});
}
});
and in wallet-balance.hbs:
<div>Your Total Score: {{anythingelse.wallet.balance}} </div>
I'm trying to understand why. Where is it picking up from for the definintion "wallet" - and why changing to "anythingelse" won't work? Where else is the code for "wallet" referring to?
Components are isolated from their surroundings, so any data that the component needs has to be passed in. so you need to understand how to pass properties to component.
I assume, in all the above three examples you are including wallet-balance component like this.
{{wallet-balance wallet=model}}
If you want to make {{anythingelse.wallet.balance}} this one work for 3rd example, then you need to include the component like {{wallet-balance wallet=model.anythingelse}}
For debugging in template hbs file, you can make use of log helper, like {{log 'model object' model}} this will print model object in console.
Here is the reasoning behind the screen,
Whatever is returned from model hook will be set in corresponding controller's model property by default through setupController hook method.
In your case, you didn't override setupController so default behavior is applicable.
return this.get('store').queryRecord('wallet', {balance: true});
queryRecord will return Promise and it will be resolved to single record wallet and it will be set in controller's model property. now model is equivalent to single wallet record object. you can access it in template `{{model.balance}}
return RSVP.hash({
anythingelse: this.get('store').queryRecord('wallet', {balance: true})
});
queryRecord will return Promise and it will be resolved to single record wallet and it will be set in inside the object {anythingelse:walletRecord} now model is equivalent to {anythingelse:walletRecord}. you can access it in template like {{model.anythingelse.balance}}
You need to set your model in setupController().
import Ember from 'ember';
import RSVP from 'rsvp';
export default Ember.Route.extend({
model() {
return this.get('store').queryRecord('wallet', {balance: true});
},
setupController(controller, wallet) {
controller.set('wallet', wallet);
}
});
Hope this helps.

Ember.js in Component send Action with data to another Component

Component action triggers perfectly.
import Ember from 'ember';
export default Ember.Component.extend({
actions: {
searchField: function() {
console.log('searchField');
this.sendAction('searchField');
}
}
});
Route does not get triggered.
import Ember from 'ember';
export default Ember.Route.extend({
actions: {
searchField: function() {
console.log('ROUTE');
}
}
});
Handlebars
{{input key-up='searchField' searchField=(action "searchField")}}
I have spent so much time with this I am starting to lose interest with Ember.js, as I have also tried according to the documentation, but I get the same result.
http://emberjs.com/api/classes/Ember.Component.html#method_sendAction
sendAction will not reach to route.
You have two options,
define searchField function in controller and from there you can route functionthis.send('searchField')
To directly call from component to route, there is addon ember-route-action-helper for this.
ember install ember-route-action-helper
Refer answer for more info.
To play around - Sample Twiddle

Assertion Failed: You need to pass a model name to the store's modelFor method

Pretty new to Ember so maybe someone can help me out. I keep running across this error and have no idea how to solve it.
Ember : 2.5.1
Ember Data : 2.5.3
Below is my router.js.
//app/router.js
import Ember from 'ember';
import config from './config/environment';
const Router = Ember.Router.extend({
location: config.locationType
});
Router.map(function() {
this.route('organization', {path: '/organization/:id'}, function(){
this.route('about', { path: '/about' });
this.route('admin', { path: '/admin' }, function(){
this.route('team', { path: '/team/:team_id' });
});
});
});
The organization/:id/about and organization/:id/admin routes work fine. But when I try to load the organization/:id/admin/team/:team_id route, the error is thrown. Below is the routes/organization/admin/team.js file:
//app/routes/organization/admin/team.js
import Ember from 'ember';
export default Ember.Route.extend({
model(params) {
let organization = this.modelFor('organization');
return organization.get('team');
}
});
Not really sure what other information I should post, so please ask for any additional information you may think is necessary to help debug. My guess is it's something pretty simple and I'm completely oblivious to it.
EDIT
I've added a couple more files to help diagnose the problem:
//app/routes/organization.js
import Ember from 'ember';
export default Ember.Route.extend({
model(params) {
return this.store.findRecord('organization', params.organization_id)
}
});
//app/routes/organization/admin.js
import Ember from 'ember';
export default Ember.Route.extend({
actions: {
changeValue(){
this.currentModel.save();
}
}
});
Where currentModel is the model for the organization route. I've removed the organization.admin.team model hook for now and am just testing a
{{#link-to 'organization.admin.team' model.team.id}} Team {{/link-to}}
in a component rendered in the organization.admin template where I pass model=model. But now I get the same error (Assertion Failed: You need to pass a model name to the store's modelFor method) in the Javascript console when rendering the organization.admin template.
If you pass Object to {{#link-to}} helper. It skips the model hook. So you could basically send {{#link-to 'team' organization.team}}Without having to write "model" hook.
"It makes sense and it might save a request to the server but it is, admittedly, not intuitive. An ingenious way around that is to pass in, not the object itself, but its id" - https://www.toptal.com/emberjs/the-8-most-common-ember-js-developer-mistakes".
So you should do
hbs
{{#link-to 'team' organization.team.id}} Link to team management {{/link-to}}
route
model(params) {
return this.store.findRecord('team', params.team_id)
}
you can use modelFor('parent') method to get organization model.
like that
//app/routes/organization/admin/team.js
import Ember from 'ember';
export default Ember.Route.extend({
model(params) {
let organization = this.modelFor('parent');
return this.store.findRecord('team', params.team_id)
}
});
i think you wants to do something like that.
basically ember does not support nested routes.

Ember custom conventions

I'm build a list-view, which renders a list of records in a table. The list-view is build as a reusable mixin, and has a reusable template as well. I want the list-view to be as easy to use as possible, and not have to write too much code, to make it work - but only overwrite what I want to change.
Idealy I only want to tell the controller (or perhaps even better) the router, that it's going to render a list-view, and only render custom template, if I have one defined.
Example:
import Ember from 'ember';
import MixinList from '../../mixins/mixin-list';
export default Ember.Route.extend(MixinList, {
model: function() {
return this.store.find('category');
}
});
Currently I have to write this code, to make the list-view work:
Categories route:
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.find('category');
}
});
Categories controller:
import Ember from 'ember';
import MixinList from '../../mixins/mixin-list';
export default Ember.Controller.extend(MixinList, {
actions: {
itemAction: function(actionName, item) {
if (actionName === 'edit') {
this.transitionToRoute('categories.edit', item.get('id'));
}
}
}
});
Categories template:
<h1>Categories</h1>
{{partial 'mixin-list'}}
Is it possible to setup conventions, so routes which are using a specific mixin, are given a default controller and template, if they arent added to the project by the user?
After some further research (and some fresh eyes), I found the solution:
import Ember from "ember";
export default Ember.Mixin.create({
renderTemplate: function() {
var currentController = this.container.lookup('controller:' + this.routeName);
if (currentController.isGenerated) {
currentController.reopen(MixinList);
this.render('mixin-list-view');
}
else {
this.render();
}
}
});
That allows me to only define the route, and include the mixin, and let that mixin do the magic:
import Ember from 'ember';
import MixinList from '../../mixins/mixin-list';
export default Ember.Route.extend(MixinList, {
model: function() {
return this.store.find('category');
}
});
The important part here, is the renderTemplate method, and the lookup to the currentController. The currentController exposes a property, that tells if it's autogenerated (not explicitly created by the user). In that case, we can overwrite the rendered template, and even add functionallity to the controller - for example by adding a mixin to the controller (.reopen(...)).

How to access query parameters from route in Ember 1.7

In 1.7 Ember should support Query Parameters. I have no problems using them in controller but I'd like to access them in Route, ideally in beforeModel hook but model hook would work as well.
The API docs mention a queryParam parameter for the beforeModel hook but if I try to use it, it is always undefined.
The Query Parameters guide seems to suggest that the query parameters should be accessible as a part of the first parameter to the model hook. But that is also undefined. See the code below for examples.
Is there a way to access the query parameters from Route?
Thank you for your help.
App.ApplicationRoute = Em.Route.extend({
beforeModel: function(transition, queryParams){
console.log(queryParams.test); //undefined at /?test=123
},
model: function(params){
console.log(params.test); //undefined at /?test=123
}
});
Pretty sure it's a bug, but you can access them in the meantime via the transition object:
App.ApplicationRoute = Em.Route.extend({
beforeModel: function(transition){
console.log(transition.queryParams.test);
}
}
By specifying the query params in the controller, params will contain them automatically
ApplicationController = Ember.Controller.extend({
queryParams: ['test'],
test: null
});
In the latest version of ember (2.12 at the time of writing this answer), queryParams can be accessed in the model hook as follows:
import Ember from 'ember';
export default Ember.Route.extend({
queryParams: {
test: ''
},
model(params) {
console.log(params.test);
},
});
Observe that now both dynamic segment and queryParams are accessible via the params object. Since params is not available in the beforeModel hook, this solution works on when you have to access the queryParams in the model hook.
In latest ember version you can get the value in Route as
import Ember from 'ember';
export default Ember.Route.extend({
beforeModel(params){
console.log(params.queryParams.test); //if the parameter contains test as the key
}
});
This works only if the locationType is auto in your environment.js file.
If you want to access in didTransition action,
didTransition: (queryParams) ->
console.log(#get('queryParams'))