Accessing the store from model function in routes - ember.js

I want to be able to set my model dynamically depending on a record in the store - mainly because I want to control whether or not a form submission should be a POST or PUT.
export default Ember.Route.extend({
model: function() {
let bankAccount = this.store.get('bankAccount').objectAt(0);
if (bankAccount.is_connected) {
bankAccount = this.store.createRecord('bankAccount', { account_label: 'new' });
}
return Ember.RSVP.hash({
appState: this.get('appStates').currentAppState(),
user: this.store.findAll('user').thenGetFirst(),
bankAccount: bankAccount,
});
},
});
The issue is that let bankAccount = this.store.get('bankAccount').objectAt(0); return null when I refresh the page.
However, if I run App.store.get('bankAccount').objectAt(0) in my browser console, it returns the correct record.
What am I doing incorrectly?

The store has no get method do you mean to use findAll to get the accounts?
export default Ember.Route.extend({
model() {
// A better way to do this would be to `findRecord` with an id
return this.store.findAll('bank-account').then(bankAccounts => {
let bankAccount = bankAccounts.get('firstObject');
// Properties are usually camel cased this seems weird
// Can you add your model definition?
if (bankAccount.get('is_connected')) {
bankAccount = this.store.createRecord('bank-account', { account_label: 'new' });
}
});
return Ember.RSVP.hash({
appState: this.get('appStates').currentAppState(),
// A better way to do this would be to `findRecord` with an id
user: this.store.findAll('user').thenGetFirst(),
bankAccount: bankAccount,
});
},
});
On a side note your code needs some major cleanup and rethinking, especially if you are going to be dealing with people's financial data.

Related

Ember: Return a value or set a model property from Ember promise

Update - more information below
If I have a promise, is it possible to return a value from it?
let itemData = [];
model.data.get('products').then(relatedItems => {
relatedItems.forEach(function(item,index) {
console.log(item.get('name')); // Product 1, Product 2 etc
itemData.pushObject(item);
});
},reject => {
console.log('error '+reject);
});
If I try and return the itemData array after the promise has resolved I get undefined.
Alternatively (and preferably) I'd like to be able to set a model property when the promise has resolved:
// component code
itemData:null,
init() {
let model = this.get('data');
model.data.get('products').then(relatedItems => {
relatedItems.forEach(function(item,index) {
this.set('itemData',item);
});
},reject => {
console.log('error');
});
}
The reason for all of this is that I need to sort the product items which I can only access via the promise (in this example). Having set the itemData property I was intending to do something like:
sortedItems:computed.sort('itemData','sortProperties'),
sortProperties:['name:desc']
More information:
In my product route, product.items.item I have a pagination component
{{pagination-item-trad data=model}}
The model hook in the route product.items.item is
model(params) {
let itemModel = this.store.findRecord('product',params.id);
let mainModel = this.modelFor('product.items');
return Ember.RSVP.hash({
data:itemModel,
mainData:mainModel
});
}
The mainModel will include the category model for that particular product item.
Since the product-category model has a many-to-many relationship with products, I need to access the product data in my component using a promise, which was not a problem until I needed to sort the product data. What I am trying to do is obtain the product information from the promise (itemData below) and then use that in the computed property. So the question is how I can extract the data from the promise for use elsewhere in the code? Is there a better way to achieve this? I hope this is clearer!
sortedItems:computed.sort('itemData','sortProperties'),
sortProperties:['name:desc']
The component in more detail:
import Ember from 'ember';
const {computed} = Ember;
export default Ember.Component.extend({
itemData:null, // i would like to set this within the promise
sortedItems:computed.sort('itemData','sortProperties'),
sortProperties:['name:desc'],
init() {
let allData = this.get('data');
let mainModel = allData.mainData;
var self = this;
let itemData = [];
mainModel.data.get('products').then(relatedItems => {
relatedItems.forEach(function(item,index) {
console.log(item.get('name')); // prints Product 1 etc etc
itemData.pushObject(item);
});
self.set('itemData',itemData); // I can't do this
},reject => {
console.log('error '+reject);
});
}
// rest of code omitted for brevity
});
Your scope is wrong inside your forEach, this no longer points to your component. You can either use another fat arrow or maintain a reference to the component scope using a variable.
Additionally, I doubt you are meaning to iterate and overwrite itemData on each iteration.

Ember return length of a model created today

I am trying to do this: I have a model called 'trip', and inside trip, an attribute called 'createdToday', which returns the date when a trip is created. What I want is to return a list of trips that were made today.
Here is my trip model:
import DS from 'ember-data';
export default DS.Model.extend({
driver: DS.belongsTo('driver', {
async: true,
inverse: 'trip'
}),
..... etc .......
createdAt: DS.attr('string', {
defaultValue() {
return new Date();
}
}),
isBookedToday: function(trip) {
var today = new Date().toDateString();
return (today === trip.get('createdAt').toDateString);
},
getTripsToday: Ember.computed('trip.#each.createdAt', function() {
var tripsToday = this.get('trip');
return tripsToday.filterBy('isBookedToday', true).get('length');
})
});
In my isBookedToday, I'm trying to see if an individual trip's created time is the same as todays time, and in getTripsToday, I am trying to loop through all the trips and filtering by isBookedToday.
And in my .hbs file, I'm saying: {{trips.getTripsToday}}, which won't render anything, so something's wrong.
I guess I am most confused at Ember's #each and exactly how it works.
Thanks for any feedback.
First you have to understand that your Trip Model instances represents a single Trip! Its absolutely not the right place to put a function that gives you a filtered list of trips!
Next isBookedToday is a normal function not a Computed Property. So you can't filterBy on it.
You may want to implement a isBookedToday on your trip, but you definitely have to filter your trips on the same place where you fetch them! Probably in a model() hook or a Computed Property on a component or a controller.
So you could do but don't need to do in your models/trip.js:
...
isBookedToday: Ember.computed('createdAt', {
get() {
let now = new Date();
let created = get(this, 'createdAt');
return now.getFullYear() === created.getFullYear() &&
now.getMonth() === created.getMonth() &&
now.getDate() === created.getDate();
}
})
...
And then in your model hook:
model() {
return this.store.findAll('trip').then(trips => trips.filterBy('isBookedToday'));
}
Or in a Computed Property in a controller or a component:
tripsToday: Ember.computed('trips.#each.isBookedToday', {
return get(this, 'trips').filterBy('isBookedToday');
})
Be careful. This will result in confusing things if you leave the page open overnight! when your date changes the Computed Properties will not recompute automatically!

Why do I need to manually pushObject after a successful hasMany create?

I have a hasMany / belongsTo relationship
App.Appointment = DS.Model.extend({
employee: belongsTo('employee', { async: true})
});
App.Employee = DS.Model.extend({
appointments: hasMany('appointment', { async: true})
});
I have a simple form that lets me create the appointment
var appointment = {
employee: employee (another ember-data model)
}
this.store.createRecord('appointment', appointment).save().then(function(apt) {
self.get('target').transitionTo('day.index');
}
If I do the above my "employees" array never shows the inverse correctly (ie- it doesn't reflect the new appointment when I do something later like employee.get('appointments');
I've been able to "work around" this issue w/ something like the below
this.store.createRecord('appointment', appointment).save().then(function(apt) {
employee.get('appointments').pushObject(apt);
employee.save();
self.get('target').transitionTo('day.index');
}
I don't like this for 2 reasons
I feel like if I have ember-data wired up correctly it should just
"know" that I've added a new appointment w/ the related employee (as
I see that going across the wire)
this forces a "lookup" on my hasMany (so it then fires off a request
asking for that employees apts -often messing up the scope of "how
many" apts I want to show for a given context).
Is my relationship setup correctly here? Or is this a bug in ember-data 1.0 beta 4/5 ?
I'm currently using ember-data 1.0 beta 4 with ember.js 1.3.1
For what it's worth, here is what I'm currently using to do a recursive save on my 'items'. They have children, and they have permissions associated to them. Also worth noting is that these items are recursive (so, the items can have children, which are items that can have children... and so on and so forth). This will handle the case where some of the items are saved or not including all parent re-association and what not. It works for me. This might help you (or it might just confused you completely, I hope not though.)
If you can get something useful out of it, that's great :)
Also worth noting is that I don't do anything with my error catches. This isn't ideal, obviously!
saveAll: function() {
var saveExistingObjects, self;
saveExistingObjects = function(item) {
var promise;
promise = new Ember.RSVP.Promise(function(resolve, reject) {
return item.get('childItems').then(function(childItems) {
var childPromises;
childPromises = childItems.map(function(childItem) {
return saveExistingObjects(childItem);
});
return Ember.RSVP.all(childPromises).then(function(arrayOfSavedChildren) {
var itemPermissions, itemWasNew;
itemWasNew = item.get('isNew');
itemPermissions = item.get('itemPermissions');
return item.save().then(function(savedItem) {
if (itemWasNew) {
arrayOfSavedChildren.forEach(function(childItem) {
childItem.set('parentItem', savedItem);
return childItem.save();
});
itemPermissions.forEach(function(itemPermission) {
itemPermission.set('item', savedItem);
return itemPermission.save();
});
}
savedItem.set('childItems', []);
Ember.RSVP.Promise.cast(savedItem.get('childItems')).then(function(cb) {
return cb.addObjects(arrayOfSavedChildren);
});
return resolve(savedItem);
})["catch"](function(error) {
console.log("Didn't save!");
return reject(error);
});
})["catch"](function(error) {
console.log("Didn't finish saveExistingObjects and returning childPromises");
console.log(error);
return reject(error);
});
})["catch"](function(error) {
console.log("Didn't get childItems");
console.log(error);
return reject(error);
});
});
return promise;
};
self = this;
return saveExistingObjects(self);
}

Example of how to use Zendesk's ember-resource adapter

Is there any working and current example on how to use Zendesk's ember-resource adapter with ember.js? I think I understand how to define the models, but I can't find any hint on how to use it in controllers and routes.
In general, there are two approaches for a given route: (1) go immediately to the page and fill in data as it becomes available (2) wait for the data to be fetched before transitioning.
Case 1 is quite straightforward. You create an instance of the model class, call fetch, and return it.
var FooRoute = Em.Route.extend({
model: function(params) {
var foo = Foo.create({ id: params.id });
foo.fetch();
return foo;
},
setup: function(foo) {
// foo is a Foo, but may not have its data populated
}
});
Case 2 is more complicated because Ember-Resource's fetch method returns a promise that resolves with two arguments -- the underlying JSON data and the model itself. An Ember.Route that returns such a promise will only pass the first to setup, so we have to create our own promise:
var FooRoute = Em.Route.extend({
model: function(params) {
var foo = Foo.create({ id: params.id }),
deferred = $.Deferred();
foo.fetch().then(
function(json, model) { deferred.resolve(model); },
function(error) { deferred.reject(error); }
);
return deferred.promise();
},
setup: function(foo) {
// foo is a Foo with its data populated
}
});

Ember, working with a model

I'm confused about how to set up retrieve information from my (dynamic) model in Ember.js
Here is my model (works so far):
App.Router.map(function() {
    this.resource('calendar', { path: '/calendar/:currentMonth'});
});
App.CalendarRoute = Ember.Route.extend({
model: function (params) {
var obj = {
daysList: calendar.getDaysInMonth("2013", params.currentMonth),
currentMonth: params.currentMonth
};
return obj;
}
});
I just want to get back the 'currentMonth' attribute:
App.CalendarController = Ember.Controller.extend({
next: function() {
console.log(this.get('currentMonth'));
}
});
But I am getting an "undefined" error.
Do I have to explicitly declare my model (Ember.model.extend()) in order to get and set values?
There are some conventions that you might not be aware of in regards to setting a Model into a Controller.
In a Route, model can be any object or collection of objects you define. There is a huge deal of conventions that apply and for most cases, you don't have to specify anything as it uses the names of various objects to guide itself on building the query and a set the content of your controller, however, in your particular code, you are return obj as the model.
Ember provides a hook called setupController that will set this object into your controller's content property. Example:
App.CalendarRoute = Ember.Route.extend({
model: function (params) {
var obj = {
daysList: calendar.getDaysInMonth("2013", params.currentMonth),
currentMonth: params.currentMonth
};
return obj;
},
setupController: function(controller, model) {
// model in this case, should be the instance of your "obj" from "model" above
controller.set('content', model);
}
});
With that said, you should try console.log(this.get('content.currentMonth'));