I am trying to create a service that acts as a shopping basket, allowing users to add/remove items. However I am encountering the following error message Cannot read property 'addServiceItem' of undefined. Here is my code so far:
controller.js
$scope.addServiceItem = function(bookingSelected, title, price, length) {
if (bookingSelected === true) {
BusinessService.manageServiceItems.addServiceItemcont(title, price, length);
}
}
services.js
function BusinessService($http) {
function manageServiceItems() {
var serviceItem = [];
function addServiceItem(title, price, length) {
serviceItem.push({
title: title,
price: price,
length: length
});
}
this.getServiceItem = function() {
return serviceItem;
}
}
The service definition should be returning an object or function. From docs
The service factory function generates the single object or function
that represents the service to the rest of the application. The object
or function returned by the service is injected into any component
(controller, service, filter or directive) that specifies a dependency
on the service.
function BusinessService($http) {
var BusinessService = this, serviceItem = [];
BusinessService.manageServiceItems = {
addServiceItem: function(title, price, length) {
serviceItem.push({
title: title,
price: price,
length: length
});
},
getServiceItem: function() {
return serviceItem;
}
}
return BusinessService;
};
var myModule = angular.module('myModule', [])
.factory('BusinessService', BusinessService);
Related
I'm having a little problem trying to pass a service within controllers.
What I'm trying to do is a shopping cart, I have a list of items and when I hit a button, those items get added to the cart, then I want to list those items in the cart in a separate page using a separate controller, so I'm trying to use a factory for the cart, but I don't know if you can set a factory object within a controller.
Here's my code, hope you can point me in the right direction.
var app = angular.module("Shop", []);
app.factory('DataService', function () {
var cart = [];
var set = function (data) {
cart = data;
}
var get = function () {
return cart;
}
});
app.controller("catalogController", function ($scope, $http) {
$scope.bookStore = {
selected: {},
books: null
};
$scope.cart = [];
$http.get("json/books.json")
.success(function (data) {
console.log(data);
$scope.bookStore.books = data;
})
.error(function (err) {
});
$scope.addToCart = function (book) {
var found = false;
$scope.cart.forEach(function (item) {
if (item.id === book.id) {
item.quantity++;
found = true;
}
});
if (!found) {
$scope.cart.push(angular.extend({
quantity: 1
}, book));
}
};
$scope.removeFromCart = function (item) {
var index = $scope.cart.indexOf(item);
$scope.cart.splice(index, 1);
};
$scope.getCartPrice = function () {
var total = 0;
$scope.cart.forEach(function (product) {
total += product.price * product.quantity;
});
return total;
};
});
app.controller("checkoutController", function ($scope, DataService) {
$scope.cart = DataService;
});
Change things a bit to something like:
app.factory('DataService', function () {
var cart = [];
return {
set: function (data) {
cart = data;
},
get: function () {
return cart;
},
add: function (item) {
cart.push(item);
}
}
});
...
app.controller("checkoutController", function ($scope, DataService) {
$scope.cart = DataService.get();
});
And then move the $http.get method and all the operations on the card in the other controller to functions in the factory and declare them on the same way as the above Dataservice.get()
You should do something like this:
A service is a singleton in angular js, that's mean you only have one instance of this class in your app.
var app = angular.module("Shop", []);
app.factory('DataService', function ($http) { // usualy your service is the one which call your API (not your controller)
var cart = null; // the cart array is set in the instance of the class as private
return{ // here you declare all the functions you want to call from outside (your controllers)
set : function (data) {
cart = data;
},
get: function(){
return cart;
},
getFromAPI = function () { // the code you have in your controller should goes here
return $http.get("json/books.json")
.success(function (data) {
console.log(data);
cart = data; //now you set you cart variable
})
.error(function (err) {
});
},
});
Then in your controllers:
app.controller("catalogController", function ($scope, DataService) { // include your service as a dependency
$scope.bookStore = {
selected: {},
books: null
};
$scope.cartInCatalogController = DataService.get(); // it will set the value of cart that's in your service to your controller's scope
if(!$scope.cartInCatalogController) {// if it's null so call the API
DataService.getFromAPI()// this function should return a promise
.success(function(data){// so call the success function
$scope.cartInCatalogController = data;
})
.error(function(error){
// do something here if you want
});
});
You can do the same in your other controller.
About the addToCard function and other stuff I let you find it by yourself.
You can start from here :)
I have an object of products. The products i get from a resource (Product), store in a factory (productsStore), and iterate through in a controller.
In another controller, i want to either empty of refresh products via productsStore.empty(); or productsStore.get();. But either of them does nothing. Meaning my forEach, below, is not being run again, and the list not updated.
What am i doing wrong?
Controller:
vm.products = productsStore.get();
vm.products.$promise.then(function (data) {
angular.forEach(vm.products, function (child) {
//. some code
)};
)};
Factory:
myApp.factory('productsStore', function ($http, $q, Product) {
var products = "";
var get = function () {
return products = Product.query();
};
var empty = function () {
return products = {};
};
// Bind products
products = get();
return {
get: get,
empty: empty
};
});
Resource:
myApp.factory('Product', function ($resource) {
return $resource('http://api.com/api/products/:id', { id: '#id' }, {
update: {
method: 'PUT'
}
});
});
try to loop through the data you get after the promise is resolved, otherwise it's a rather useless variable.
angular.forEach(data, function(child) {
//...do something with the child
});
Also, you have a typo, that I'm not sure you have in your actual code. In your controller, end of block should be }) and not )}
In an Angular app, I have made a factory, where I get all my products and put them into a factory variable. From there I can get grab them, wherever I need them. So far s good.
I then want to manually loop through all the items in the products array. I have tried the following;
My factory first
myApp.factory('myStore', function ($http, $q, Store) {
var products = "";
// Bind products
products = Store.query();
return {
get: function () {
return products;
},
getSingle: function(id) {
},
set: function (data) {
products = data;
},
add: function (data) {
}
};
});
In my controller i have
// Bind products to list
vm.products = myStore.get();
console.log(vm.products);
for (var prop in vm.products) {
// returns just 2 objects?
console.log(prop);
}
for (i = 0; i < vm.products.length; i++) {
// Does not wok at all?
}
vm.products.forEach(function (child) {
// Does not work either. Simply does nothing.
});
What do I need to do to iterate through my array?
The returned products are in the correct format, and the correct values.
What gives?
The resource:
myApp.factory('Store', function ($resource) {
return $resource('http://api.com/api/products/:id', { id: '#id' }, {
update: {
method: 'PUT'
}
});
});
The output from console.log(vm.products): http://screencast.com/t/Q52EJ2F7gf
I changed the loop to
vm.positions.$promise.then(function(data)
and then it worked. So ti was a promise issues.. ofcourse.
While experimenting with nodejs I encountered a problem of enabling isntances creation via Constructors. I create simple cart basket functionality.
I got file cart.js
var items = [];
function addItem (name, price) {
items.push({
name: name,
price: price
});
}
exports.total = function () {
return items.reduce(function (a,b) {
return a + b.price;
}, 0);
};
exports.addItem = addItem;
I run it with node
var cart = require('./cart')
But what if I need to create multiple instances of a Cart?
I tried to refactor my code, creating a Constructor, that holds items[] addItem() and total() functions, like this:
exports.Cart = function () {
var items = [];
function addItem (name, price) {
items.push({
name: name,
price: price
});
}
function total () {
return items.reduce(function (a,b) {
return a + b.price;
}, 0);
}
};
I run it like this:
var cart = require('./cart');
cart.addItem('Pepsi',199); // no problem with this
cart2 = new cart.Cart(); // it gives me undefined can't be a function
I understand, that I can use PROTOTYPE property to add functions and props to my Cart
So I create a second file cart2.js and place something like:
function Cart () {
this.items = [];
}
Cart.prototype.addItem = function (name, price) {
this.items.push({
name: name,
price: price
});
};
Cart.prototype.total = function () {
return this.items.reduce(function (a,b) {
return a + b.price;
}, 0);
};
module.exports = Cart;
And now it works.
But in order to explore all possiblities, I want to know how I can solve it the first way I tried. When I can use it as "instanceble" Class thing and as singleton thing, with only one instance, at the same time.
Can you please advice me how to solve it the way I wanted in the first place?
I'll appreciate if you provide some other ways to solve this task of creating instanceable Classes.
The first option might look like this:
exports.Cart = function () {
var items = [];
// ...other private stuff...
return {
addItem: function (name, price) {
items.push({
name: name,
price: price
});
},
total: function() {
return items.reduce(function (a,b) {
return a + b.price;
}, 0);
}
// ...other public stuff...
}
};
Usage:
var carts = require('carts');
firstCart = carts.Cart();
second = carts.Cart();
I want to create a find method that loops through an array returned by the $resource service in Angular.
If I have a service like so:
'use strict';
angular.module('adminApp').factory('ProductType', function($resource) {
var ProductType;
ProductType = $resource('http://localhost:3000/api/v1/product_types/:id.json', {
id: '#id'
}, {
update: {
method: 'PUT'
}
});
ProductType.find = function(typeName){
var types = this.query(),
typeObject = {},
self = this;
for(type in types) {
var result = types[type],
resultName = self.normalizeName(result.name),
if(typeName === resultName) {
typeObject = result;
}
}
return typeObject;
};
return ProductType;
});
I tried wrapping it all in a function and returning the function thinking it had something to do with it being async and I also tried nesting a callback in the query method but that just allowed me to modify the response and not actually return anything differently.
When I try and set the return value to $scope in the controller I get a blank object
The this.query() method would return an array which might not be filled until the this.query() method has got its results back from the server. You will need to do something like this to wait until the call to the server has completed. As this is sort of async you will need to return a promise from this method that is resolved when the initial query has completed and you have searched the results.
'use strict';
angular.module('adminApp').factory('ProductType', [
'$q',
'$resource',
function($q, $resource) {
var ProductType;
ProductType = $resource('http://localhost:3000/api/v1/product_types/:id.json', {
id: '#id'
}, {
update: {
method: 'PUT'
}
});
ProductType.find = function(typeName) {
var defer = $q.defer(),
types = this.query(),
self = this;
types.$promise.then(function () {
var result,
resultName,
typeObject,
type;
for(type in types) {
result = types[type];
resultName = self.normalizeName(result.name);
if(typeName === resultName) {
typeObject = result;
break;
}
}
defer.resolve(typeObject);
}, function (err) {
// the called failed
defer.reject(err);
})
return defer.promise;
};
return ProductType;
}]);
Taken from the angular docs https://docs.angularjs.org/api/ngResource/service/$resource
It is important to realize that invoking a $resource object method immediately returns an empty reference (object or array depending on isArray). Once the data is returned from the server the existing reference is populated with the actual data. This is a useful trick since usually the resource is assigned to a model which is then rendered by the view. Having an empty object results in no rendering, once the data arrives from the server then the object is populated with the data and the view automatically re-renders itself showing the new data.