I have the following setup:
App.Router.map(function() {
this.route('tab', { 'path' : 'tab/:which' });
});
App.ApplicationStore = DS.Store.extend({});
App.ApplicationAdapter = DS.RESTAdapter.extend({
host: '../api'
});
App.TabAdapter = DS.RESTAdapter.extend({
find: function(store, type, id) {
alert("I doesn't get invoked");
return this._super(store, type, id);
}
});
App.TabRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('emails', {tab: "inbox"});
}
});
And when visiting the route #/tab/inbox i wanna rewrite the URL for the endpoint from
http://localhost/ba/api/emails?tab=inboxinto
http://localhost/ba/api/emails/inbox. Therefore i'm overriding the find()-method on the TabAdapter, but when this.store.find('emails', {tab: "inbox"}); runs, it doesn't enter my overridden method(and my test-alert doesn't get invoked).
Why does my overridden find()-method not get invoked?
you're overriding the wrong find method. You're finding by query, not id and should be overriding that method
findQuery: function(store, type, query) {
// Do your thing here
return this.ajax(this.buildURL(type.typeKey), 'GET', { data: query });
}
And you're using a TabAdapter which would be specific to models of type tab not of type email(s). You should be creating a Email(s)Adapter. The general convention is for a model to be singular btw.
See also: How do you create a custom adapter for ember.js?
Related
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));
}
});
In one of my routes I need to findAll() of user's Items, but instead of making the standard /items request it has to go to /my/items.
My current solution involves:
// routes/my/item.js
export default Ember.Route.extend({
model() {
this.store.unloadAll('item');
return Ember.$.getJSON('/my/items').then((payload) => {
this.store.pushPayload(payload);
return this.store.peekAll('item');
});
}
});
But unfortunately it's not ideal since it requires to unloadAll() items before making the request as to ensure that the model only returns freshly fetched records while unloading any cached.
A better solution will probably involve creating a custom adapter specifically for this route and overwriting either the findAll() method or urlForFindAll(), but I'm not sure how to properly create and import such custom adapter.
Just for testing I overwrote the default Item adapter and returned findAll('item') in the route's model, and everything worked, the request was prefixed with /my/:
// adapters/item.js
findAll: function(store, type, sinceToken, snapshotRecordArray) {
var query, url;
if (sinceToken) { query = { since: sinceToken }; }
// prefix url with `my` str
url = `my${this.buildURL(type.modelName, null, null, 'findAll')}`;
return this.ajax(url, 'GET', { data: query });
},
// routes/my/item.js
export default Ember.Route.extend({
model() {
return this.store.findAll('item');
}
});
..but that obviously overwrites all findAll() queries for this model, wherein I need to make a custom query only in this route.
This can be solved by using adapterOptions to pass options to the item's adapter using findAll:
1) In the route use adapterOption to pass prefix to the adapter:
return this.store.findAll('item', { adapterOptions: { prefix: 'my' } });
2) In ember-cli overwrite item's default adapter with ember g adapter item.
3) In the adapter overwrite the default findAll to prefix url if such option is passed:
// /app/adapters/item.js
import ApplicationAdapter from './application';
export default ApplicationAdapter.extend({
findAll: function(store, type, sinceToken, snapshotRecordArray) {
var query, url;
if (sinceToken) { query = { since: sinceToken }; }
let prefix = Ember.get(snapshotRecordArray, 'adapterOptions.prefix');
url = `${prefix || ''}${this.buildURL(type.modelName, null, null, 'findAll')}`;
return this.ajax(url, 'GET', { data: query });
},
});
4) Success, this.store.findAll('item', { adapterOptions: { prefix: 'my' } }); will now make a my/items instead of items!
I wonder how to get the param (the ID) of my edit route in Ember.js.
That's how I defined my route:
this.resource('accounting', function() {
this.resource('accounting.requests', { path: '/requests' }, function() {
this.route('new');
this.route('archived');
});
this.resource('accounting.request', { path: '/request/:request_id' }, function() {
this.route('edit');
});
});
Now I want to fetch the model by its ID on this edit route /accounting/request/3/edit:
App.AccountingRequestEditRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('request', params.request_id);
}
});
But this doesn't work because params is empty.
This works as expected, but the edit route doesn't:
App.AccountingRequestRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('request', params.request_id);
}
});
When you are in a child route you can use this.modelFor, example:
App.AccountingRequestEditRoute = Ember.Route.extend({
model: function(params) {
return this.modelFor('request');
}
});
Because in order to go to request.edit, you have to provide an id or object to its parent resource, request either directly going to the route passing the url to the browser or indirectly by transition from other routes that happen to link to edit.
Let's say that I want to have URLs like /users/JoshSmith for maximum readability/shareability.
I set up my Router:
this.resource('user', path: '/users/:username')
And my route:
var UserRoute = Ember.Route.extend({
model: function(params) {
debugger
return this.store.find('user', { username: params.username });
}
});
But this findQuery function actually returns an array, since it's calling /users?username= instead of calling /users/:username like I would normally do.
I'm a little lost as to how I should be handling this; I'm assuming there's a convention out there, I just can't find it.
As suggested here: http://discuss.emberjs.com/t/find-by-different-property/2479
Just override serialize on your route.
var UserRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('user', { username: params.username });
},
serialize: function(model) {
return { username: model.get('username') };
}
});
This replaces the default which looks like this:
serialize: function(model) {
// this will make the URL `/posts/12`
return { post_id: model.id };
}
Source: http://emberjs.com/api/classes/Ember.Route.html#method_serialize
I had this same problem as well, findQuery always returns a record array. The way I got around this was to simply change my model hook in the router to
model: function(params) {
return this.store.find('user', { username: params.username }).then(function(users) {
return users.get('firstObject');
});
}
I want to have a dynamic segment path in Ember without using the :id attribute
As per the Ember Guides, I'm using the serialize method to achieve this.
Heres my Router:
App.Router.map(function() {
this.resource("orders", function(){
this.resource('order', { path: ':order_sequence'}, function(){
this.route('edit');
})
});
});
And my Route:
var OrderRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('order', params.order_sequence)
},
serialize: function(model) {
return { order_sequence: model.get('sequence') };
}
});
module.exports = OrderRoute;
However, my URL's still behave using the id attribute in the path instead of the sequence attribute..
Any ideas?
Is your browser caching something, because that's correct. Are you passing in the id instead of the sequence/model in any of your transitionTo/transitionToRoute/link-to?
Oh, you aren't talking about the slug in the url, nor the route, you are talking about the id of your model. You need to create a serializer for that particular model and override the primary key
App.OrderSerializer = DS.RESTSerializer.extend({
primaryKey: 'sequence'
});
The Fixture Adapter has a constraint on defining the id, but you can lazily get around it by extending the fixture adapter and overriding a single method
App.OrderAdapter = DS.FixtureAdapter.extend({
fixturesForType: function(type) {
if (type.FIXTURES) {
var fixtures = Ember.A(type.FIXTURES);
return fixtures.map(function(fixture){
// aka we massasge the data a bit here so the fixture adapter won't whine so much
fixture.id = fixture.sequence;
var fixtureIdType = typeof fixture.id;
if(fixtureIdType !== "number" && fixtureIdType !== "string"){
throw new Error(fmt('the id property must be defined as a number or string for fixture %#', [fixture]));
}
fixture.id = fixture.id + '';
return fixture;
});
}
return null;
},
});