I create a shop cart. I using fixture adapter.
My models
App.Clothing = DS.Model.extend({
name: DS.attr('string')
, category: DS.attr('string')
, img: DS.attr('string')
, price: DS.attr('number')
, num: DS.attr('number')
, fullPrice: function(){
return this.get('price') + " $";
}.property('price')
})
App.CartRecord = App.Clothing.extend({
numInCart:DS.attr('number',{defaultValue:1})
, fullPrice: function(){
return this.get('price')*this.get('numInCart');
}.property('numInCart','price')
})
App.CartRecord.FIXTURES = [];
Route
App.CartRoute = Em.Route.extend({
model: function(){
return this.store.find('cartRecord');
}
})
And my controller
App.CartController = Em.ArrayController.extend({
totalPrice: 0
});
How i can calculate a total price?
You can put together a reduceComputed property for sum. Here are a few links for inspiration: one, two, and three. Basically, you can do something like this:
Ember.computed.sum = function (dependentKey) {
return Ember.reduceComputed.call(null, dependentKey, {
initialValue: 0,
addedItem: function (accumulatedValue, item, changeMeta, instanceMeta) {
return accumulatedValue + item;
},
removedItem: function (accumulatedValue, item, changeMeta, instanceMeta) {
return accumulatedValue - item;
}
});
};
Then, in your controller do something like this:
App.CartController = Em.ArrayController.extend({
prices: Ember.computed.mapBy('content', 'fullPrice'),
totalPrice: Ember.computed.sum('prices')
});
Related
I am trying to set data from two models (that has hasMany & belongsTo relationship) and save them to firebase.
'list' data ends up being saved to firebase but not user data.
I think I'm doing something wrong at step 3. I'd appreciate your help!
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.find('list');
},
actions: {
createList: function() {
var newListTitle = this.controllerFor('lists').get('newListTitle');
var username = this.get('session.user.displayName');
alert(this.get('session.user.displayName'));
if (Ember.isBlank(newListTitle)) { return false; }
//1
var list = this.store.createRecord('list', {
title: newListTitle,
user: username,
});
//2
this.controllerFor('lists').set('newListTitle', '');
var _this = this;
//3
list.save().then(function(list) {
user.get('lists').addObject(list);
user.save();
_this.transitionTo('lists.show', list); //4
});
}
}
});
Restructured your adding logic as well as user defined models, also modified your route, which could look like this in Edit and View mode. Meaning you can have more than one item returned from "model".
// Update models
App.List = DS.Model.extend({
value: DS.attr('string')
});
App.User = DS.Model.extend({
name: DS.attr('string')
});
App.UserLists = DS.Model.extend({
user: DS.belongsTo('user'),
list: DS.belongsTo('list')
});
export default Ember.Route.extend({
LIST:SHOW ROUTE
model: function(params) {
var store = this.get('store');
var userPromise = store.find('user', params.id);
return Ember.RSVP.hash({
user: userPromise,
userList : userPromise.then(function(user) {
return store.find(userList, { WhereUserIdIs : user.get('id') })
});
});
},
actions: {
createList: function() {
var self = this;
var failure = function(reason) {
// handle stuff
};
var list = this.store.createRecord('list', {
title: this.get('title'),
});
var user = this.get('user');
var usersList = store.createRecord('userList', {
'user': user,
'list': list
});
list.save().then(function(list) {
user.save().then(function() {
userList.save().then(function() {
self.transitionTo('lists.show', list.get('id'));
}, failure);
}, failure);
}, failure);
}
});
I'm updating a model using the following controller:
payment_controller.js
App.PaymentNewController = Ember.ObjectController.extend({
needs: ['student'],
isNew: true,
actions: {
createPayment: function() {
var date = new Date(this.get('date'));
if (!date) { return false; }
var amount = this.get('amount');
if (!amount) { return false; }
var studentId = this.get('controllers.student.id');
if (this.isNew)
{
var payment = this.store.createRecord('payment', {
date: date,
amount: amount,
});
}
else
{
var payment = this.get('model');
payment.set('date', date);
payment.set('amount', amount);
}
var self = this;
var onSuccess = function(payment) {
self.set('date', '');
self.set('amount', '');
self.transitionToRoute('student', studentId);
};
var onFail = function(payment) {
};
if (this.isNew)
{
this.store.find('student', studentId).then(function(student){
payment.set('student', student);
payment.save().then(onSuccess, onFail);
});
}
else
{
payment.save().then(onSuccess, onFail);
}
},
}
});
The data returned from the server is the following, and it looks in the correct format expected by ember:
{"payment":{"amount":1111,"date":"2014-09-09T00:00:00.000Z","student":"54024bb9cd52254e181325af","_id":"54024bdacd52254e181325b0"}}
The issue is that after saving the data is correctly saved to the database, but in the store is empty (UPDATE: except the id). As soon as I reload the page the data is shown correctly and is the updated version of the model. This made me think to the fact that the error could lie in the response of the PUT request, but it looks fine to me.
I've been trying many different things with no success. I can provide additional code or info if needed.
UPDATE 1
Here are the involved routes:
App.StudentsRoute = Ember.Route.extend({
model: function() {
return this.store.find('student');
}
});
App.StudentRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('student', params.student_id);
}
});
App.StudentEditRoute = Ember.Route.extend({
setupController: function(controller, model) {
this.controllerFor('student.new').setProperties({isNew:false, content:model});
},
renderTemplate: function() {
this.render('student/new');
}
});
App.PaymentNewRoute = Ember.Route.extend({
model: function() {
return this.store.createRecord('payment');
}
});
App.PaymentEditRoute = Ember.Route.extend({
setupController: function(controller, model) {
this.controllerFor('payment.new').setProperties({isNew:false, content:model});
},
renderTemplate: function() {
this.render('payment/new');
}
});
UPDATE 2
Here are the serializer and the adapter:
App.ApplicationAdapter = DS.RESTAdapter.extend({
host: 'http://localhost:3000',
});
App.ApplicationSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
primaryKey: '_id',
serializeId: function(id) {
return id.toString();
},
attrs: {
payments: {embedded: 'always'},
lessons: {embedded: 'always'}
}
});
UPDATE 3
Here are the models:
App.Student = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
email: DS.attr('string'),
nationality: DS.attr('string'),
dateOfBirth: DS.attr('date'),
payments: DS.hasMany('payment'),
lessons: DS.hasMany('lesson'),
});
App.Payment = DS.Model.extend({
student: DS.belongsTo('student'),
date: DS.attr('date'),
amount: DS.attr('number'),
});
UPDATE 4
While inspecting the store using the Ember Inspector in Chrome I've noticed that after the update is completed, the only property that contains data in the payment object (in the store) is the id. Both date and amount are empty.
I'm having an issue with two levels of calculated properties. I'm a bit new to ember so would appreciate some pointers.
The basic problem is that there are two levels of calculated properties - one at the order level and one at the item level. The order level is dependent on the calculation on the item.
After binding to the form - the item level calculation works great and the form is updated as I change the quantity. The order total however does not seem to calculate at all. Am I missing something in the property dependencies?
App.Order = DS.Model.extend({
items: DS.hasMany('item', { async: true } ),
payment_cash: DS.attr('number'),
payment_card: DS.attr('number'),
payment_credit: DS.attr('number'),
balance: DS.attr('number'),
total: function() {
return this.get('items').reduce(function(value,lineItem) {
value += lineItem.get('total');
});
}.property("items.#each.total"),
itemCount: function() {
return this.get('items').reduce(function(value,lineItem) {
value += lineItem.get('quantity');
});
}.property("items.#each.quantity"),
});
App.Item = DS.Model.extend({
order: DS.belongsTo('item'),
product: DS.belongsTo('product'),
quantity: DS.attr('number'),
adjustment: DS.attr('number'),
total: function() {
return this.get('product.price') * this.get('quantity')
}.property('product.price', 'quantity' )
});
App.Product = DS.Model.extend( {
name: DS.attr('string'),
description: DS.attr('string'),
price: DS.attr('number'),
imagePath: DS.attr('string')
});
The problem is that your reduce function is not returning anything. Try this:
total: function() {
return this.get('items').reduce( function(value, lineItem) {
return value += lineItem.get('total');
}, 0 );
}.property("items.#each.total"),
itemCount: function() {
return this.get('items').reduce( function(value, lineItem) {
return value += lineItem.get('quantity');
} , 0);
}.property("items.#each.quantity"),
This may be more of a structure question but the heading is my current issue.
I have the following basic app:
var App = Ember.Application.create();
App.ApplicationAdapter = DS.FixtureAdapter.extend();
App.Router.map(function () {
this.resource('numbers', {
path: '/'
});
this.resource('users');
});
App.UsersRoute = Ember.Route.extend({
model: function (params) {
return this.store.findAll('user');
}
});
App.NumbersRoute = Ember.Route.extend({
model: function (params) {
return this.store.findAll('number');
}
});
App.User = DS.Model.extend({
name: DS.attr('string'),
numbers: DS.hasMany('number', {
async: true
})
});
App.Number = DS.Model.extend({
value: DS.attr('number'),
user: DS.belongsTo('user')
});
App.NumbersController = Ember.ArrayController.extend({
total: function () {
var total = 0;
this.forEach(function (number) {
total += parseFloat(number.get('value'));
});
return total;
}.property()
});
App.User.FIXTURES = [{
id: 1,
name: 'Bob',
numbers: [100, 101]
}, {
id: 2,
name: 'Fred',
numbers: [102]
}];
App.Number.FIXTURES = [{
id: 100,
value: 25,
user: 1
}, {
id: 101,
value: 15,
user: 1
}, {
id: 102,
value: 60,
user: 2
}];
Working example with templates is here: http://jsfiddle.net/sweetrollAU/9DuR3/
The example shows a relationship between users and numbers. The first page is a list of all numbers in the app, their related user and a total of all numbers. The Users link shows the same content but each user should show its own subtotal for the numbers it has.
My question is basically, how can I access the NumbersController method 'total' in my UsersController? Should I be accessing this method or do I have the structure incorrect?
Thanks
In your case they are similar logic, but they ultimately come from different data sources. You can still create a Mixin that can help you share the code amongst different Ember objects.
App.AddNumberMixin = Em.Mixin.create({
sumNumbers: function(arr){
var total = 0;
arr.forEach(function (number) {
total += parseFloat(number.get('value'));
});
return total;
}
});
App.UserController = Ember.ObjectController.extend(App.AddNumberMixin, {
total: function () {
return this.sumNumbers(this.get('numbers'));
}.property('numbers.#each.value')
});
App.NumbersController = Ember.ArrayController.extend(App.AddNumberMixin, {
total: function () {
return this.sumNumbers(this);
}.property('#each.value')
});
http://jsfiddle.net/9DuR3/2/
#kingpin2k's answer seems sufficient, but here's another way to do it:
http://jsfiddle.net/9DuR3/3/
What happens here is that the numbers are rendered again, for each users, but this time with a different template. Namely the one between the {{render}} tags. The NumbersController is provided with the user.numbers collection, instead of the complete numbers collection.
Also it's important to specify on which properties the total function dependent is (.property('#each.value')).
Is there any possibility to filter the hasMany records from a model record? I want to get the active projects, grouped by the customer.
Customer model
Docket.Customer = DS.Model.extend({
name: DS.attr('string'),
initial: DS.attr('string'),
description: DS.attr('string'),
number: DS.attr('string'),
archived: DS.attr('boolean'),
projects: DS.hasMany('project',{ async: true })
});
Project model
Docket.Project = DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
number: DS.attr('string'),
archived: DS.attr('boolean'),
customer: DS.belongsTo('customer', { async: true })
});
Project route
Docket.OrganizationProjectsIndexRoute = Docket.AuthenticatedRoute.extend({
setupController: function () {
var customersWithActiveProjects = this.store.filter('customer', function(customer) {
return customer.get('id') && GET_ONLY_ACTIVE_PROJECTS_FROM_CUSTOMER?
});
this.controllerFor('organization.projects').set('filteredProjects', customersWithActiveProjects);
}
});
Update
I tried something like this but It does not work. I think this is a problem caused by asynchronous requests. But does it point in the right direction?
Docket.OrganizationProjectsIndexRoute = Docket.AuthenticatedRoute.extend({
setupController: function () {
// get customers because we group projects by customers
var customers = this.store.filter('customer', function(customer) {
return customer.get('id')
});
var sortedProjects;
// loop through each valid customer and filter the active projects
$.when(
customers.forEach(function(customer){
customer.get('projects').then(function(projects) {
var filteredProjects = projects.filter(function(project){
return !project.get('archived')
});
customer.set('projects',filteredProjects);
});
})
).then(function() {
sortedProjects = Ember.ArrayProxy.createWithMixins(Ember.SortableMixin, {
sortProperties: ["name"],
content: customers
});
});
this.controllerFor('organization.projects').set('filteredProjects', sortedProjects);
}
});
I think the following could work:
controller
Docket.OrganizationProjectsIndexRoute = Docket.AuthenticatedRoute.extend({
setupController: function () {
var projectsController = this.controllerFor('organization.projects');
this.store.find('customer').then(function(customers) {
var promises = customers.map(function(customer) {
return Ember.RSVP.hash({
customer: customer,
projects: customer.get('projects').then(function(projects) {
return projects.filter(function(project) {
return !project.get('archived');
});
});
});
});
Ember.RSVP.all(promises).then(function(filteredProjects) {
projectsController.set('filteredProjects', filteredProjects);
});
});
}
});
template
{{#each filtered in filteredProjects}}
Customer {{filtered.customer}}<br/>
{{#each project in filtered.projects}}
Project {{project.name}}<br/>
{{/each}}
{{/each}}
The trick is use Ember.RSVP.hash to group each customer by active projects.