Reputation: 181
I'm trying to set up a shopping bag model in Ember. The shopping bag will be created on load of the page and saved in LocalStorage using the LocalStorage adapter. At any given time, there should only be one instance of a bag saved, as a user only needs to add products to one shopping bag. My question is this: it seems that I'm being hack-y with my methods of getting and setting data on my bag as Ember data caters to models with more than one instance. Is there a better way to structure/define my bag model that is better suited for one-instance models? Here's my model:
import DS from 'ember-data';
export default DS.Model.extend({
products: DS.hasMany('product', {async: true}),
productCount: function() {
return this.get('products.length');
}.property('products.length')
});
When I want to get the productCount in my template, the only way I can seem to get it to print is use an {{#each}}
statement with {{productCount}}
nested inside. As there is only one bag, this seems inefficient. In other parts of my code, I need to get the current instance of the bag and act on it. To get this to work, I'm finding all bags, then getting the firstObject
, which also seems hack-y:
import Ember from "ember";
export default Ember.ArrayController.extend({
actions: {
addToBag: function(model) {
this.store.find('bag').then(function(bags) {
var bag = bags.get('firstObject');
bag.get('products').then(function(products) {
products.pushObject(model);
bag.save();
});
});
}
}
});
My application route uses the bag as its model, and sets up the controller:
import Ember from "ember";
var ApplicationRoute = Ember.Route.extend({
activate: function() {
var store = this.store;
store.find('bag').then(function(bags) {
var existing_bag = bags.get('firstObject');
// If there isn't already a bag instantiated, make one and save it
if(typeof existing_bag === 'undefined') {
var new_bag = store.createRecord('bag');
new_bag.save();
}
});
},
model: function() {
return this.store.find('bag');
},
setupController: function(controller,model) {
controller.set('content', model);
}
});
Any ideas here to make this more efficient? I don't want this to fester into code that is messy. Thanks so much in advance!
Upvotes: 1
Views: 331
Reputation: 2409
If that is your BagController above, it should be an ObjectController instead.
The reason why you're having to get the first object is because find
fetches all items of that model type in your store
. You may only have one, but find
doesn't know that unless you provide an id and if this bag hasn't been stored in your database, it may not have one yet.
Instead of fetching the bag model from the store
, I would link you Bag and Products controllers with needs
, then simply access the model property of that controller. You can even set up an alias to be able to access it quickly.
For example:
import Ember from "ember";
export default Ember.ArrayController.extend({
needs: 'bag',
bag: Ember.computed.alias("controllers.bag.model").
actions: {
addToBag: function(model) {
this.get('bag').get('products').pushObject(model);
}
}
});
The ApplicationRoute
is only fired once, when you're app first boots up so you don't need to check if there's already a bag model present. The only one that will be there is the one you create. You should do this in the model
hook. You don't need to set content as the model. It'll be hooked up like that by default.
import Ember from "ember";
export default Ember.Route.extend({
model:function() {
return this.store.createRecord('bag');
}
});
Upvotes: 1
Reputation: 1847
If you may want the ability to have multiple shopping bags going forward you could try using the 'singleton' approach Discourse follows: https://github.com/discourse/discourse/blob/master/app/assets/javascripts/discourse/mixins/singleton.js
Basically it adds 'current' property that you can use as your single instance throughout your code.
However, if you won't have a need to have multiple instances this may not be the best choice.
Upvotes: 1