Ember.js: accessing queryParams in before/afterModel hooks - ember.js

The Ember.js docs indicate that queryParams should be passed into the before/afterModel hooks on a Route (http://emberjs.com/api/classes/Ember.Route.html#method_afterModel), but that argument is always undefined for me and I haven't been able to figure out why.
Here's an example: http://jsbin.com/xeyaxova/1/edit
Why is this argument undefined, and how else can I access the queryParams inside these hooks?

The query params must come after the hash, that's where your ember app does all of its routing.
#/?query=cxZxc
Example: http://jsbin.com/ucanam/3008#/?query=o
the beforeModel/afterModel hook aren't properly working right now (canary builds) Here's a workaround, the queryParam object is attached to the transition object.
http://emberjs.jsbin.com/giweqeze/1/edit

Related

Append query parameters after queryParam update in Ember.js

I defined the queryParams in the controller and I need to add additional query parameters which is not used in the Ember application and it can have any name. It's an analytics thing. Those query parameters are not known in advance thus, we are not able to define those analytics values in the queryParams property. What happens now is Ember deletes all additional parameter after the queryParams and URL binding is triggered.
Example:
In the Controller:
queryParams: ['fin', 'ftw'],
fin: null,
ftw: null,
url:
localhost:3000?fin=111&ftw=121&anatylicsvalue=1
when I change the value of fin or ftw, anatylicsvalue is removed. I need the app to retain that bit of string.
What is the best way of doing this?
You need to define analyticsvalue within queryParams array of the controller, or queryParams json object of the router whether or not it is going to be used by the controller. So, if you declare queryParams as ['fin', 'ftw', 'analyticsvalue'] it will retain.
In order to achieve a somewhat dynamic query parameter (where even the name of the parameter is not known in advance), the only option I can suggest is using a named query parameter and using JSON.stringify to put the query parameters with that name. Take a look at the following twiddle. index.js route defines queryParams as queryParams: ['fin', 'ftw', 'additionalParams'] and additionalParams query parameter holds dynamic parameters since it is a JSON object that can hold any dynamic sub-parameter. Take a look at the updateAdditionalParams inside actions it defines a dynamic property and dynamic values as you can see.
Unfortunately, Ember does not support custom query parameters that are not known in advance. You can use the approach I have illustrated in the twiddle. Hope this helps.
Unfortunately, Ember does not support custom query parameters that are not known in advance.
It's a shame there's no "remainder" capability, if I understand what you're saying correctly. If an SPA was called with the requirement that any query parameters that it does not understand must be passed through verbatim on all its outgoing URLs, the SPA would need to scrape these off itself during routing, outside the Ember mechanisms? Do I have this right?
With controllers going away, will all of the query parameter support end up on the route? Is there something we should be doing to "future-proof" our design now?

What is the difference between set(this, 'agentName', 'John') and this.set('agentName', 'John') in Ember?

The same is true for 'get' methods too. Like get(this, 'agentName') and this.get('agentName') returns the same value.
In the official ember doc for get method, it shows that we are supposed to pass 2 values to get. Then how does this.get('agentName') work accurately?
this.get(...) is a shortcut for Ember.get(this,...). However it is only available on ember objects, so only Ember.get works in plain js objects.
Have a look at this.get(...) implementation.
The getter and setter in Ember has been upgraded to handle unknown property, computed property and observers. Not many people would use setUnknownProperty() hook or the unknownProperty() hook with get and set, but there are computed properties and observers throughout most people's code. More on computed properties and observers.
So, the basic difference between your set() and this.set() has to do with the context of your function. When you call just set(), it has to be defined in that scope or imported from somewhere to get things done. Howeve, with this.set() the scope here is this. Depending on where you are calling the function the scope changes. For instance if you are in a component, this refers to the component class itself. Similar would be the case with controllers, routes and other ember objects. If you have object of your own and it does not extend any of ember classes, then this would still work with how it does in any other JavaScript code. So it will fall back to default getter and setter in JavaScript.
As you may have realized by now, when you call get(this, 'foo'), you are calling JavaScript's getter function and pass it the current context with property to be searched for. And when you say this.get('foo') you are calling get() from Ember.Object class, which can handle things I mentioned above. And as #Lux mentioned this.get('foo') is simplified to call Ember.get(this, 'foo').
Hope this helps. I do encourage reading Ember guides and API docs. Curent ember-cli and ember-data version is #2.12.0

Fetch new data from API in jQuery plugin's callback

I am new to ember, so please treat me like a fool. What I'm trying to do first is to understand the concept.
In my application I heavily rely on few jQuery plugins they fetch new portion of data in their callbacks, that's how these plugins are designed, but I am not sure how can I trigger them to fetch a new portion of data from API passing to API updated query parameters after plugin has been rendered.
I have wrapped the plugin in a component, in component's template I send data to it as (I use emblem.js syntax here)
= plotly-chart chartData=model
In model I have
//app/models/data-points.js
import DS from 'ember-data';
export default DS.Model.extend({
// time: DS.attr(),
ch1: DS.attr(),
ch2: DS.attr(),
ch3: DS.attr(),
temperature: DS.attr(),
});
And then in component itself I fetch data
//app/components/plotly-chart.js
dataPoints: Ember.computed.map('chartData', function(item){
return item.getProperties('ch1', 'ch2', 'ch3', 'temperature');
}),
and make some manipulations with data, which isn't so important for the question itself.
Ah, and I have a route graph/ which later calls that component
//app/routes/graph.js
import Ember from 'ember';
export default Ember.Route.extend({
queryParams: {
start_timestamp: {
refreshModel: true
},
end_timestamp: {
refreshModel: true
}
},
model(params) {
return this.get('store').query('data-point', params);
}
});
So as you see I have tried to fetch new properties via query params, finally it works great if I just update the url in browser, but now can I trigger new call to API and fetch new data and get this new data in a component itself?
Also I'm struggling to understand what role controllers play in all of these. It is mentioned that controllers will be deprecated soon, but still used here https://guides.emberjs.com/v2.10.0/routing/query-params/
My code seems to work without controllers, so this is really confusing.
Also I suspect maybe I should use services for what I'm trying to achieve, but not sure how.
Ember experts, could you please point me into a right direction? The most important thing is how to fetch new portion of data from API with updated query parameters (query parameters to API itself, not nessesarely the ember application, but I suspect in ember-data it is the same thing? or not %) %) %)).
UPDATE 1
Really I should use services for that, shouldn't I? Pass arguments into a service and query a store there. Would that be a correct pattern?
Then query parameters in url are not the same as querying the store and is an independent thing. Am I right?
but how can I trigger new call to API and fetch new data and get this new data in a component itself?
If you change your queryParam values in a controller using an action (combined with your current route setup) it will adjust your route and re-call your API, as the values are bound together to make this particular use case simple :-) You're about 98% of the way there ... :-)
Re controllers going away, they won't for a long time as the replacement hasn't been worked out yet. You could do some of this in a service if you want to, but there is no need as you are almost done.
Thanks, that make sense though. I just worried I'm doing it wrong.
By they way finally I found a way to access store from the controller Access store from component but:
1. I was unable to take out the data from that variable, probably it's me being stupid.
2. I double it's the right way to access store directly in a component and better to use services for that or rely on “Data Down Actions Up” (DDAU) paradigm?
Finally I was able to fetch new portion of a data calling a controller's action from within the controller, but then the next problem raised - the data was updated, but the JS code did not know about that because I feed the jQuery plugin with this data and it did not pick up changes automatically. I think I might be doing it a wrong way there %)
But finally I get it working by adding an Ember's observer to that variable and in observer calling a redraw function (for chart in this particular place).
#acorncom Thanks!

Lazy loading route definitions in Ember.js

I'm trying to implement lazy loading in an Ember.js application. Ideally, I'd prefer to have all the relevant code for each module, including any controller and route definitions, in a separate .js file that gets lazy loaded.
Right now, the js file gets loaded correctly when I transition to the route, but because Ember implicitly generates a route definition, the implicitly-generated route object is used instead of the route in my lazy-loaded js file.
In my lazy-loaded js file, I've got a route App.UsersManagerRoute that should be linked to the users.manager route. In the Ember Inspector, I can see that an implicitly generated route is being used instead, even after I've loaded the js file.
To try to fix this, I've tried to manually register the route after loading the js file, but it doesn't seem to be working. This is my code that does the lazy loading:
Ember.Router.reopen({
_doTransition: function (_targetRouteName, models, _queryParams) {
var resourceName = _targetRouteName.split('.')[0];
var self = this;
var transition = self._super(_targetRouteName, models, _queryParams);
transition.abort();
var fullRouteName = 'route:' + camelizeRouteName(_targetRouteName);
self.container.unregister(fullRouteName);
App.lazyLoad(resourceName, function() {
self.container.register(fullRouteName, App[sentenceCasedRouteName(_targetRouteName) + 'Route']);
transition.retry();
});
return transition;
}
});
After I unregister the implicitly generated route and register my lazy-loaded route, the route definition seems to be used correctly, but for some reason, I see a blank page instead of the right template. I'm not too sure what I'm missing here, or if what I'm trying to do is the recommended approach.
All the examples of lazy loading in Ember I've seen so far place the Route outside the lazy-loaded file. Is that the only option that would work?
Auto generation of ember routes is caused by link-to component through href computed property. Never fight against it. Ember will not work properly and you will loose. But you should know it deeply in order to understand the mechanism.
href LinkToComponent method ask for a URL. Before answer, Ember looks for the route. If it doesn't exist, Ember creates one from route:basic.
Container and Registry have some useful method: reset and lookup the former, register and unregister the latter.
register and unregister modify the registry.
lookup creates instances if they don't exist, looking for the factory in factoryCache, and storing them in cache. If the factory doesn't exist there, it asks the Registry.
reset clears cache and factoryCache of the specified member.
That said, the right sequence in order to achieve lazy loading should be:
unregister(fullName);
reset(fullName);
register(fullName, factory);
lookup(fullName);
For an initial solution, have a look at https://github.com/ricottatosta/ember-wiz
Auto generation of ember object is caused by link-to component through href computed property. In order to avoid this behavior (it could be responsible of blank pages) I advice to change href to avoid calling function that calculate so called 'intention' that autogenerate missing objects.

replaceRoute with an object id

I want to use the replaceRoute in an ember controller but need to pass an object id. Something along the lines of:
this.replaceRoute('projects.edit', 4)
Is that possible at all or should it be done differently? It's going to the route I tell it to but with an object id of undefined.
Is that possible at all or should it be done differently?
replaceRoute expects to be passed the route's context, not an id. You can use the id to locate the context, for example:
this.replaceRoute('projects.edit', App.Project.find(4));
Here is a jsfiddle demonstrating how this approach using the fixture adapter
http://jsfiddle.net/mgrassotti/mhyjG/1/