emberjs find then filter - ember.js

In emberjs, considering the following data
(only showing 1 record, normally there would be multiple records):
{ "service": [{
"service-_id":"service_5606ece79bdb05546479739866",
"service-_rev":"5-62dc477c13ef3ea92869bcdf1a67f1a6",
"service-company-name":"ABC co.",
"service-address":"1 2 3 Main Street",
"service-address-line-2":"",
"service-city":"asfd",
"service-state-current":"NY",
"service-zip":"12345",
"service-phone":"111",
"service-fax":"",
"service-email":"asdf#adsf.com",
"service-category-current":"web",
"service-type":"service",
"id":"service_5606ece79bdb05546479739866"
}]}
If I want to return all the records, I can simply do this:
App.ServicesRoute = Ember.Route.extend({
model: function(){
return this.store.find('service');
}
});
However, let's say I want to return all the records that have the current category as 'web'. So in the example data, there is this key: service-category-current
How would I adjust my model to find 'service' then filter where service-category-current = 'web' ?

The best way would be to make your API backend handle query params you send to it (so your records would be filtered on a backend, preferably query params could be used to query the database), so response from server would return only records that match your query. Example store.query call:
this.store.query('service', {
'service-category-current': 'web'
});
Which results in fetching records from URL:
http://api.com/services?service-category-current=web
And you're done. But, if you can't refactor your backend, you could filter records client-side:
model() {
return new Ember.RSVP.Promise(resolve => {
this.store.findAll('service').then(services => {
resolve(services.filterBy('service-category-current', 'web'));
});
});
}
Not ES2015 + using Ember.RSVP.Promise instead of native Promise (maybe will help you with Safari issue):
model: function() {
var that = this;
return new Ember.RSVP.Promise(function(resolve) {
that.store.findAll('service').then(function(services) {
resolve(services.filterBy('service-category-current', 'web'));
});
});
}

Related

How to resolve custom nested graphql query with Apollo CacheRedirects

We are using apollo-client in a react project. We made a cursor level on top of any list queries. For example:
query MediaList($mediaIds: [ID!], $type: [MediaType!], $userId: ID!) {
user {
id
medias_cursor(all_medias: true, active: true, ids: $mediaIds) {
medias {
id
type
category
name
}
}
}
}
Now for different MediaList query, the Media Objects might already exist in cache but we can not use it to skip network query. For example:
After we query medias_cursor({"all_medias":true,"active":true,"ids":["361","362","363"]}),
we've already got the three Media objects here - (Media:361, Media:362, Media:363).
So when we try to query medias_cursor({"all_medias":true,"active":true,"ids":["361","363"]}, we should have everything we need in the cache already. But right now, the apollo default behavior will just pass the cache and hit the network.
We tried to add a cacheRedirects config to solve this problem like this:
const cache = new InMemoryCache({
cacheRedirects: {
User: {
medias_cursor: (_, { ids }, { getCacheKey }) => {
if (!ids) return undefined
return {
medias: map(ids, id => {
return getCacheKey({ __typename: 'Media', id: id })
})
}
},
},
},
})
We are expecting that the cacheRedirects would help us to use the cache when it's available, but now it will skip the cache anyway.

How to Model.fetch(<object>) when the returned data is a single object

I want to make an API call for searching that looks like this:
https://myapi.com/search/<query>/<token>
where query is the search term and token (optional) is an alphanumeric set of characters which identifies the position of my latest batch of results, which is used for infinite scrolling.
This call returns the following JSON response:
{
"meta": { ... },
"results" {
"token": "125fwegg3t32",
"content": [
{
"id": "125125122778",
"text": "Lorem ipsum...",
...
},
{
"id": "125125122778",
"text": "Dolor sit amet...",
...
},
...
]
}
}
content is an array of (embedded) items that I'm displaying as search results. My models look like this:
App.Content = Em.Model.extend({
id: Em.attr(),
text: Em.attr(),
...
});
App.Results = Em.Model.extend({
token: Em.attr(),
content: Em.hasMany('App.Content', {
key: 'content',
embedded: true
})
});
In order to make that API call, I figured I have to do something like this:
App.Results.reopenClass({
adapter: Em.RESTAdapter.create({
findQuery: function(klass, records, params) {
var self = this,
url = this.buildURL(klass) + '/' + params.query;
if (params.token) {
url += '/' + params.token;
}
return this.ajax(url).then(function(data) {
self.didFindQuery(klass, records, params, data);
return records;
});
}
}),
url: 'https://myapi.com/search',
});
then somewhere in my routes do this:
App.Results.fetch({query: 'query', token: '12kgkj398512j'}).then(function(data) {
// do something
return data;
})
but because the API returns a single object and Em.RESTAdapter.findQuery expects an array, an error occurs when Ember Model tries to materialize the data. So how do I do this properly? I'm using the latest build of Ember Model.
By the way, I'm aware that it would be much more convenient if the API was designed in a way so I can just call App.Content.fetch(<object>), which would return a similar JSON response, but I would then be able to set the collectionKey option to content and my data would be properly materialized.
You simply need to override your models load() method to adjust the payload hash to what Ember.Model wants. There are no serializers in Ember.Model. There is both a class level load for handling collections and an instance level load for loading the JSON specific to a single model. You want to override the instance level load method to wrap the content key value in an array if its not one already.
I have been using Ember.Mode quite heavily and enhanced it for a number of my use cases and submitted PR's for both fixes and enhancements. Those PRs have been sitting there for a while with no response from the maintainers. I have now moved to Ember.Data which has been 'rebooted' so to speak and having a lot better result with it now.
I would strongly suggest walking away from Ember.Model as it appears dead with the new pragmatic direction Ember Data has taken and because the project maintainer doesn't appear to have any interest in it anymore.

Typeahead/Bloodhound - Using Jquery Ajax for remote causes only a single server side request

I need to use a jquery ajax setup in Bloodhound's remote property since I have a server side page that takes POST requests only. Everything works, but just once. Any subsequent change to the text in the typeahead input box calls the filter function, but does not fire a new server side request to fetch new data. It just filters through the data that it got in the first request. I need for it make a new request as the user removes the text and types in something else.
I am new to typeahead and I am spending way too much time trying to figure this out. Here is my code.
var users = new Bloodhound({
datumTokenizer: function (d) {
return Bloodhound.tokenizers.whitespace(d.value);
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: 'fake.jsp',
filter: function (users) {
return $.map(users, function (user) {
return {
value: user.USER_ID,
name: user.DISPLAYNAME
};
});
},
ajax: {
type: 'POST',
data: {
param: function(){
return $('#userid').val();
}
},
context: this
}
}
});
users.initialize(true);
$('#userid').typeahead({
minLength: 3,
highlight: true
}, {
name: 'userslist',
displayKey: 'name',
source: users.ttAdapter()
});
I had the same solution and discovered jQuery's cache: false; option does not work in this situation for whatever reason. Here is the solution I found:
remote: {
url: ...
replace: function(url, query) {
return url + "#" + query; // used to prevent the data from being cached. New requests aren't made without this (cache: false setting in ajax settings doesn't work)
}
}
try this:
remote: {
url: 'fake.jsp/?' + Math.random(),
.
.
.
it's not really the solution but at least the results will be fetched from server everytime the page is refreshed.

Ember-Data custom adapter for Rhom - FindAll Not working

I am writing an Ember-Data adapter for the Rhom API. I have written the code. I am using it in a simple Todo App. When I create a new item, it gets into the SQLite db. But when I start the app, the already existing ones donot get loaded in the store.
I wrote a console.log in the findAll of my adapter and I can see that it gets an object array from the Rhom API and returns a promise with those results. But why does it not load into the store?
I used the localstorage-adapter as an example and did this. Here is my findAll:
extractVars: function(rhomRecord) {
return rhomRecord.vars();
},
sourceIdToId: function(record) {
record["id"] = record.source_id;
return record;
},
findAll: function(store, type) {
var records = Rho.ORM.getModel(this.model).find('all');
var results = records.map(this.extractVars);
var results = results.map(this.sourceIdToId);
console.log(results);
return Ember.RSVP.resolve(results);
},
As you can see, the console.log prints the following out and its just an array of objects that contain what I need. When I tried with the locastorate, it also returned a same kind of objects.
What do I do?
PS: The extractVars and sourceIdtoId are auxillary to propery extract the objects from the records returned by Rhom.
I'm not really sure if this will help you but I guess just because .find() returns a promise you should use the .then() callback to resolve your model:
findAll: function(store, type) {
return Rho.ORM.getModel(this.model).find('all').then(function(records) {
var results = records.map(this.extractVars);
var results = results.map(this.sourceIdToId);
console.log(results);
return Ember.RSVP.resolve(results);
});
}
Hope it helps.

Ember.JS: Your server returned a hash with the key id but you have no mapping for it

Consider this Ember JS Model:
App.User = DS.Model.extend({
firstName: DS.attr('string')
});
I am able to successfully save the model on the server using this as an XHR request:
{
"user": {
"first_name":"dude"
}
}
but for some reason it gives me an error while returning this XHR response:
{
"id":1,
"user":{
"first_name":"dude"
},
"createdAt":"2013-04-12T03:13:52.382Z",
"updatedAt":"2013-04-12T03:13:52.382Z"
}
The error says: Your server returned a hash with the key id but you have no mapping for it
Ember expects the output to look like:
{
"user": {
"id":1,
"first_name":"dude",
"createdAt":"2013-04-12T03:13:52.382Z",
"updatedAt":"2013-04-12T03:13:52.382Z"
}
}
I think the problem lies in the request itself, but I'm not sure.
Note that I'm using the Sails API as my backend.
You can use a controller to marshal the data format to whatever you need-- but this raises an interesting question about adding support for different front-end conventions to the API blueprints. Right now, Sails.js API blueprints support Backbone out of the box, but obviously that doesn't do you a lot of good if you're using Ember :) I created an issue for that here https://github.com/balderdashy/sails/issues/317.
Here's a hacky example of how you'd use a custom controller to send back data in this format using Sails today:
// api/controllers/UserController.js
module.exports = {
// Create action: (e.g. using default route, you'd POST to /user/create)
create: function (req,res) {
// Grab attributes from request using Ember conventions
var newAttributes = req.param('user');
// Create the user object in the datastore
User.create(newAttributes, function (err, newUser) {
// If there was an error, handle it
if (err) return res.send(err,500);
// Respond with the user object using Ember conventions
res.json({
user: newUser
});
});
}
};
That's a weirdly formatted JSON response. Do you have access to the server?
Ember expects the response as a a hash with root keys
{
"user": {
"id":1,
"first_name":"dude",
"createdAt":"2013-04-12T03:13:52.382Z",
"updatedAt":"2013-04-12T03:13:52.382Z"
}
}