Append query parameters after queryParam update in Ember.js - 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?

Related

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!

Ember dynamic query parameters

I have what I believe to be common but complicated problem to model. I've got a product configurator that has a series of buttons. Every time the user clicks on a button (corresponding to a change in the product configuration), the url will change, essentially creating a bookmarkable state to that configuration. The big caveat: I do not get to know what configuration options or values are until after app initialization.
I'm modeling this using EmberCLI. After much research, I don't think it's a wise idea to try to fold these directly into the path component, and I'm looking into using the new Ember query string additions. That should work for allowing bookmarkability, but I still have the problem of not knowing what those query parameters are until after initialization.
What I need is a way to allow my Ember app to query the server initially for a list of parameters it should accept. On the link above, the documentation uses the parameter 'filteredArticles' for a computed property. Within the associated function, they've hard-coded the value that the computed property should filter by. Is it a good idea to try to extend this somehow to be generalizable, with arguments? Can I even add query parameters on the fly? I was hoping for an assessment of the validity of this approach before I get stuck down the rabbit hole with it.
I dealt with a similar issue when generating a preview popup of a user's changes. The previewed model had a dynamic set of properties that could not be predetermined. The solution I came up with was to base64 encode a set of data and use that as the query param.
Your url would have something like this ?filter=ICLkvaDlpb0iLAogICJtc2dfa3
The query param is bound to a 2-way computed that takes in a base64 string and outputs a json obj,
JSON.parse(atob(serializedPreview));
as well as doing the reverse: take in a json obj and output a base64 string.
serializedPreview = btoa(JSON.stringify(filterParams));
You'll need some logic to prevent empty json objects from being serialized. In that case, you should just set the query param as null, and remove it from your url.
Using this pattern, you can store just about anything you want in your query params and still have the url as shareable. However, the downside is that your url's query params are obfuscated from your users, but I imagine that most users don't really read/edit query params by hand.

Ember.js: accessing queryParams in before/afterModel hooks

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

ember-data adapter to read from cloudant RESTful API

The cloudant RESTful API is fairly simple but doesn't match the way ember-data expects things to be. How can I customize or create an Adapter that deals with these issues...
In my specific case I only want to load records from one of several secondary indexes (ie. MapReduce fnctions).
The URL for this is below, where [name] and [view] would change depending on user selection or the route I am in.
https://[username].cloudant.com/[db_name]/_design/[name]/_view/[view]
Looking at the ember-data source there doesn't seem to be an easy way of defining URLs like this. I took a look at findQuery and it expects to send any variables through as url params not as part of the actual URL itself.
Am I missing something? Is there an obvious way of dealing with this?
Then the data comes back in a completely different format, is there a way to tell ember what this format is?
Thanks!
I had similar problem where URL's are dynamic. I ended up creating my own adapater by extending DS.RESTAdapter and overriding the default buildURL method. For example:
App.MyAdapter = DS.RESTAdapter.extend({
buildURL: function(record, suffix) {
var username, db_name, name, view;
// Do your magic and fill the variables
return 'https://'+username+'.cloudant.com/'+db_name+'/_design/'+name+'/_view/'+view;
}
});
I ended up also defining my own find, findAll, findQuery, createRecord, updateRecord, deleteRecord etc. methods as I had to pass more variables to buildURL method.
If returning data is in different format then you can also write your own serializer by extending DS.JSONSerializer and define your own extraction methods extract, extractMany etc.
You should evaluate how well your API follows the data format required by ember/data RESTAdapter. If it is very different then it's maybe better to use some other component for communication like ember-model, ember-restless, emu etc, as ember-data is not very flexible (see this blog post). You can also write your own ajax queries directly from routes model hooks without using ember-data or other components at all. It is not very hard to do that.

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/