Add Ember Data query helper method - ember.js

I'm using Ember API with a JSON API backend. The API accepts filters like this:
/users?filter[simple][name]=John
Right now, whenever I want to make a query, I'm doing this:
this.store.query('users', {
filter: {
simple: {
name: 'John'
}
}
});
It would be nice to avoid this verbosity by adding a helper function that works like this:
this.store.simpleQuery('users', { name: 'John' });
That function would pass its arguments directly to query(), just wrapping the query in { filter: { simple: ... } }.
Is this possible? How do I do this?

Well what is stopping you from creating your own method in the adapter to do exactly that?
// ... your adapter code
simpleQuery: function(modelName, query) {
return this.store.query('users', {
filter: {
simple: {
name: 'John'
}
}
});
}
// ...

You need to extend the default store. Add the following to app/services/store.js
import DS from 'ember-data';
export default DS.Store.extend({
simpleQuery(modelName, query) {
return this.query(modelName, {
filter: { simple: query }
});
},
simpleQueryRecord(modelName, query) {
return this.queryRecord(modelName, {
filter: { simple: query }
});
}
});
And you'll be able to do this:
this.store.simpleQuery('users', { name: 'John' });
this.store.simpleQueryRecord('users', { email: 'john#example.com' });

Related

How to add nested resolver to schema?

Let's say my query looks like this:
query {
post {
id
user { id, name }
}
}
And resolver map looks like this:
{
Query: {
post: myPostResolverFunc,
}
}
How I can add additional "nested" resolver for post.user?
I tried this but it does not work:
addResolveFunctionsToSchema(schema, {
Query: {
post: {
user: postUserResolveFunc,
},
}
});
You just have to write a resolver for your field. Assuming your schema is something like this :
type Post {
id: ID!,
user: User
}
type User {
id: ID!,
username: String!
}
type Query {
post(id: ID!): Post #assuming you want to request a simple post here
}
You can write resolvers like this :
addResolveFunctionsToSchema(schema, {
Post: {
user(root) {
return getUserById(root.user)
}
}
Query: {
post(root, args, context) {
return getPostById(args.id)
}
}
});

Filter data using params in ember

I'm developing search feature in my app. What I'm trying to do is to filter a car by brand. I have the following in my route:
import Ember from 'ember';
export default Ember.Route.extend({
actions: {
filterCars(car){
this.transitionTo('cars',{ queryParams: {brand: car}});
}
},
queryParams: {
brand: {
refreshModel: true
}
},
model(params) {
if(params['marca'] != null){
this.get('store').query('car', { filter: { brand: params['marca'] } }).then(function(cars) {
return cars;
});
} else {
return this.get('store').findAll('car');
}
}
});
When I get the brand from params, I filter only the cars with that given brand. I thought it would work, but it's not working. Any idea of what I'm doing wrong ?
What is the symptom of your problem?
I notice that your model hook has an if statement with two branches, but only one of them (the else branch) results in the function returning a value. The if branch resolves a promise, but does nothing with it.
Also: is the code you show for the cars route? You may be able to simplify this page by just changing the brand parameter. I'm not sure you need to transition to the same route at all.
Use where clause in filter
import Ember from 'ember';
export default Ember.Route.extend({
actions: {
filterCars(car){
this.transitionTo('cars',{ queryParams: {brand: car}});
}
},
queryParams:{
brand:{
refreshModel: true
}
},
model(params) {
if(params['marca'] != null){
this.get('store').query('car', { filter: { where:{brand: params['marca'] } }}).then(function(cars) {
return cars;
});
} else {
return this.get('store').findAll('car');
}
}
});

Ember : fetch data afterwards

I get some data from my API through model in route.js. This data contains somewhere an id on its own, with no relationships or included stuff to get details. So I have to make another API request to get the object with that id.
I did it with a component (to be able to send the id argument) and it works, but I would like to know if that's the way to go and if so, if I did it right and it cannot be simplified (because it looks complex to me for such a simple task):
I call my component with {{resource-name id=evaluation.manager}}
Component template just contains {{name}}
component.js:
import Ember from 'ember';
export default Ember.Component.extend({
store: Ember.inject.service(),
_getResource(id) {
return this.get('store').findRecord('resource', id);
},
resource: Ember.computed('id', function() {
const id = this.get('id');
const proxy = Ember.ObjectProxy.extend(Ember.PromiseProxyMixin);
return proxy.create({
promise: this._getResource(id)
});
}),
name: Ember.computed('resource.isFulfilled', function() {
if (this.get('resource.isFulfilled')) {
return `${this.get('resource.lastName')} ${this.get('resource.firstName')}`;
}
else {
return "...";
}
}),
didReceiveAttrs() {
const id = this.getAttr('id');
Ember.assert('resource-name must have an "id" attribute!', !Ember.isBlank(id));
}
});

How to make a computed property that depends on a global class attribute?

I wanna create a property that depends on a global attribute:
App.Test= Em.Object.extend();
App.Test.reopenClass({ all: Em.A() });
App.Other = Em.object.extend({
stuff: function() {
return "calculated stuff from this.get('foo') and App.Test.all";
}.property('foo', 'App.Test.all.#each.bar')
});
As a workarround I could create a observer and always set a dummy property with a new random value to trigger the property change, but is there a better way to do this?
I need this for some caching. I've a really crazy, and single threaded backend. So I write my own Model classes. So I try to reimplement a bit of the logic in the client for a better caching.
Ive an Item class (App.Item) and another class where each instance has a calculated reduced list of Items.
App.Model = Em.Object.extend({
});
App.Model.reopenClass({
all: Em.A(),
load: function(hash) {
return this.get('all').pushObject(this.create(hash));
}
});
App.Item = App.Model.extend({
});
App.List = App.Model.extend({
loadedInitItems: false,
items: function() {
if(!this.get('loadedInitItems')) { this.set('loadedInitItems', true); Backend.call('thelist', function(item) { App.Item.load(this); }); }
return App.Item.all.filter(function(item) {
// heavy filter stuff, depends on a lot of propertys on the current list instance
});
}.property('someprops', 'App.Item.all.#each.foo')
});
Backend.call represents some AJAX stuff
the point is, that now any item could change so that the filter will return something diffrent. And there are other places om the application, where the user can add Items. I dont want to call the backend again, because its very slow! And I know that the backend will not modify the list! So I wanna cache it.
This is just a reduced example of my use case, but I think've described the point. In reallity I have this dozend of times, with over 25000 objects.
have you tried adding 'Binding' to your property and then the value you want to bind to ?, something like this:
App.PostsController = Em.ArrayController.extend({
nameOfYourVariableBinding: "App.SomeObject.propertyYouWantToBindTo"
})
It looks like the problem is the double uppercase letter. So App.test ist working, but not App.Foo.test.
But I was able to find a Solution with the ArrayProxy.
Its about this:
App.Model = Em.Object.extend({
});
App.Model.reopenClass({
all: Em.A(),
load: function(hash) {
return this.get('all').pushObject(this.create(hash));
}
});
App.Item = App.Model.extend({
});
App.List = App.Model.extend({
loadedInitItems: false,
items: function() {
var self = this;
if(!this.get('loadedInitItems')) {
this.set('loadedInitItems', true);
Backend.call('thelist', function(item) {
App.Item.load(this);
});
}
return Em.ArrayProxy.extend({
content: App.Item.all,
arrangedContent: function() {
return this.get('content').filter(function(item) {
// heavy filter stuff, depends on a lot of propertys on the current list instance
// use self.get('someprops')
})
}.property('content.#each.foo')
});
}.property('someprops')
items: function() {
if(!this.get('loadedInitItems')) { this.set('loadedInitItems', true); Backend.call('thelist', function(item) { App.Item.load(this); }); }
return App.Item.all.filter(function(item) {
// heavy filter stuff, depends on a lot of propertys on the current list instance
});
}.property('someprops', 'App.Item.all.#each.foo')
});

Mapping data in ember-data

I have a basic ember-data model object:
App.Group = DS.Model.extend({
//attributes
});
I have json which is structured like this:
root.levelone.leveltwo.property
I don't want to map this project as is but would like to map property in the json to property in the model like this:
App.Group = DS.Model.extend({
property: DS.attr('string')
});
Is it possible to define a mapping that is different from the incoming json? I don't have much control on what is coming from the server.
If this is not possible with ember-data, what is the best way to model this deep nesting?
FYI, the latest version of Ember (v.10) requires custom transforms to be defined on the DS.JSONTransforms object. And the 'to' and 'from' properties have been renamed to 'serialize' and 'deserialize'.
I'm not sure quite what you're asking but you can define custom DS.attr transforms.
Something like this maybe? Haven't tested it.
DS.attr.transforms.deepNest = {
from: function(serialized) {
return this.root2.property
},
to: function(deserialized) {
return { root2: property }
}
}
property: DS.attr('deepNest', {key: 'root1'})
IT changed FROM THIS:
DS.attr.transforms.object = {
from: function(serialized) {
return Em.none(serialized) ? {} : serialized;
},
to: function(deserialized) {
return Em.none(deserialized) ? {} : deserialized;
}
}
TO THIS:
DS.RESTAdapter.registerTransform('object', {
fromJSON: function(serialized) {
return Em.none(serialized) ? {} : serialized;
},
toJSON: function(deserialized) {
return Em.none(deserialized) ? {} : deserialized;
}
})
Ember data v 1.0 beta 2 requires this approach:
CustomTransform = DS.Transform.extend({
deserialize: function(serialized) {
...
},
serialize: function(deserialized) {
...
}
});
Ember.Application.initializer({
name: "customTransforms",
initialize: function(container, application) {
application.register('transform:custom', CustomTransform);
}
});