Ember: use segments instead of query parameters for API? - ember.js

Currently my Ember-cli application calls my API in this way:
apiurl:3000/ingredients?name=something
apiurl:3000/ingredients?filter=som?limit=10
I'd like to make Ember query for:
apiurl:3000/ingredient/something
apiurl:3000/ingredients/som?limit=10
But I've two problems:
WARNING: Encountered "ingredients" in payload, but no model was found for model name "ingredient" (resolved model name using myapp#serializer:application:.modelNameFromPayloadKey("ingredients"))
I can't find a way to make Ember use URL segments instead of query parameters.
I think I'm missing something?

Have you looked into overriding your RESTAdapter's buildURL method?
http://emberjs.com/api/data/classes/DS.RESTAdapter.html#method_buildURL
As what you're trying to do appears to be quite specific you could use this method to override the building of these requests, while at the same time keeping the default Ember model/params implementation the same.

Related

Not possible to use shorthand route handlers if RestSerializer is used? (ember-cli-mirage)

I set up a simple Ember Twiddle to show you my error that is occurring when trying to update a model.
It's considerable that I'm using ember-cli-mirage for mocking the data.
According to the docs, I created a shorthand route that should handle the PUT request.
It does, but with the error: Your handler for the url /api/shops/1 threw an error: Cannot convert undefined or null to object
When using the JSONAPISerializer, everything is working with shorthands (mirage/config.js) and I'm able to update models, but in my case I have to use the RESTSerializer with serialized IDs in the responses.
The request payload when I'm sending the model's attrs are without Id at the end of the property name, f.e.:
// attrs object in PUT request
{
name: "Shop 1",
city: "1" // belongsTo relationship,
}
Now Mirage is trying to find those properties on the respective database model that has to be updated, but cannot find it, because in the database it's cityId and not just city...
I also found this issue report and it’s working, but I was hoping I could avoid something like this. As far as I can remember, in previous versions of ember-cli-mirage (v0.1.x) it was also not needed to override the normalize method in the serializer to be able to make use of the RestSerializer with serializedIds…
My question is:
Is there a way to stick to shorthand route handlers only, or do I really have to write a helper or other custom solution only because I have to use the RestSerializer?
That would be really sad, but at least I would know then.
Thanks for your support!
Short answer: it looks like you need the custom serializer for now until the bug fix for it is merged.
Long answer: that issue looks to be an issue that occurred in the 0.2 -> 0.3 upgrade for Mirage, likely because of underlying DB changes made in Mirage. It'll probably get fixed, but for now you'll need to work around it.

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?

Ember Data TypeForRoot no longer being called

We are currently on Ember Data 1.0.0-beta.18, Ember 1.12 and CLI 0.2.7.
Since this last update, we are getting ember data assertion warnings like the following:
[Warning] WARNING: Encountered "open_requests" in payload, but no model was found for model name "open-request" (resolved model name using vault#serializer:appuser:.typeForRoot("open_requests")) (vendor.js, line 15423)
I also noticed that a console log we included in the appuser serializer is no longer called:
export default DS.RESTSerializer.extend( {
typeForRoot: function(root) {
console.log('appuser serializer called', root);
if (root === 'open_requests') return this._super('openrequest');
return this._super(root);
},
});
I can confirm that TypeForRoot was called prior to this update. We have had a number of other issues since upgrading and it seems that they could all be related in some way to the serializers, so the questions are why are we getting these warnings and why is typeForRoot no longer being called?
Ember Data switched to modelNameFromPayloadKey, typeForRoot is no longer used in the code unless you are specifically calling it.
http://emberjs.com/api/data/classes/DS.RESTSerializer.html#method_typeForRoot
beta.18 introduced a potentially breaking change - looks like it caused your app to break..
Short answer: rename typeForRoot to modelNameFromPayloadKey
From the CHANGELOG
[#3034] POTENTIALLY BREAKING CHANGE if you override typeForRoot currently introduce modelNameFromPayloadKey and deprecate typeForRoot #fivetanley
RESTSerializer#typeForRoot has been deprecated. You can use RESTSerializeer#modelNameFromPayloadKey instead.
Added RESTSerializer#payloadKeyFromModelName. This allows you to
typeKey on Snapshots and Model classes has been deprecated. Use modelName instead. specify the outgoing root key for a JSON payload.
From the Blog Post
DS.RESTSERIALIZER.TYPEFORROOT IS NOW DS.RESTSERIALIZER.MODELNAMEFROMPAYLOADKEY
To gain more consistency in the naming change of typeKey to modelName, typeForRoot has been renamed to modelNameFromPayloadKey. The function serves the same purpose, so this should be a quick refactor you can achieve via search and replace in your project. While calling typeForRoot will trigger a deprecation warning, overriding in a subclass won't.

Ember Data override find method

I need to override the find() method in ember-data to make it compatible with my app. Its not a huge modification that I have to do, but I don't know where to start.
So far when I try to do this : this.store.find('enquiry'); Ember-Data is trying to fetch information from http://localhost/enquiries instead of http://localhost/enquiry. My problem is that I don't need to get the plural of my url..
I thought also using the jquery method but, I would rather using Ember-Data for this. How can I do that ?
Another question : After this is working, is Ember-Data generate dynamically the model in the app ? Because I have a lot field in my JSON and I can't write them down manually...
Can I do something like this :
App.Store = DS.Store.extend({
adapter: '-active-model'
});
App.Enquiry = DS.Model.extend();
Thanks for your help !
This page will show you exactly how to use a custom adapter in your application. And this page will show you how to override a method in your subclass.
I didn't see your response on the Ember forum yesterday, but in my opinion, you'd still be better off writing your own adapter. It seems like you're going to do more work trying to modify the REST adapter than if you just created your own.
But if you still want to extend the rest adapter, here is how:
App.ApplicationAdapter = DS.RESTAdapter.extend({
find: () {
//...
}
}):
As for your second question, no, Ember-Data will not pick up the fields automatically. I'm pretty sure it'll throw an error if you include fields in your JSON that are not declared in the corresponding model. This is by design. If you don't know your fields at development-time, how can you use them in templates or controllers?

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.