Angular - pass info from array from one controller to another - javascript

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 :)

Related

Undefined scope variable in AngularJS app

I am trying to set a Boolean property on an element in my array object, which I have in my scope.
In the code given below, when I try to set tasks[id].deleted = true, I get the following error.
angular.js:12798 TypeError: Cannot set property 'deleted' of undefined
at Scope.$scope.delete (main.js:54)
Where am I going wrong?
My whole code file is:
angular.module('ngMaterialTaskListApp')
.controller('MainCtrl', function ($scope, $mdDialog, TaskService) {
// Model from which View populates data
$scope.tasks = [];
console.log($scope.tasks);
$scope.showAddDialog = function (ev) {
$mdDialog.show({
controller: DialogController,
templateUrl: '../views/add-dialog-template.html',
parent: angular.element(document.body),
targetEvent: ev,
clickOutsideToClose: true,
fullscreen: true, //Only for xs and sm screen sizes
locals: { //For DialogController, as tasks
tasks: $scope.tasks
}
});
};
/*----------- Function to delete items onClick of delete icon -----------*/
$scope.delete = function (id) {
console.log($scope.tasks[id]);
console.log(id);
// console.log($scope.tasks[id].name);
$scope.tasks[id].deleted = true;
};
/*----------- DialogController function -----------*/
function DialogController($scope, $mdDialog, tasks) {
$scope.task = {};
$scope.hide = function () {
$mdDialog.hide();
//TODO Add a message as to what happened
};
$scope.cancel = function () {
$mdDialog.cancel();
//TODO Add a message as to what happened
};
/*----------- Method show the add dialog -----------*/
$scope.addData = function () {
if (null !== $scope.task.name && null !== $scope.task.description) {
/*----------- Using moment.js to parse date and time -----------*/
$scope.task.date = moment($scope.task.date, '').format('DD MMM YYYY');
$scope.task.time = moment($scope.task.time, '').format('h:mm a');
$scope.task.done = false; // Every new task is pending!
$scope.task.deleted = false; // Every new task exists!
var GlobalID = Date.now();
console.log(GlobalID);
$scope.task.id = GlobalID;
/*----------- Performing http POST -----------*/
TaskService.postTask($scope.task);
/*----------- Pushing to tasks object in $scope of MainCtrl -----------*/
// Have to update tasks again
tasks.push($scope.task);
$scope.hide();
console.log(tasks); //DEBUGGING
} else {
//TODO ADD INVALID/NULL DATA WARNING
}
};
};
// DEPRECATED - USED FOR DATA WHEN SERVER NOT AVAILABLE
TaskService.getTasks().then(function (response) {
$scope.tasks = response.data.tasks;
}, function (error) {
console.log(error + "This");
});
//USING THIS TO GET DATA FROM SERVER
TaskService.getAllTasks().then(function (response) {
// console.log(response.data);
$scope.tasks = response.data;
console.log($scope.tasks);
});
});
How is your html? I bet is like this inside a button in ng-repeat:
ng-click="delete(task.id)"
Try putting like this:
ng-click="delete($index)"

Update directive in parent scope

I'm pretty new to angular js, currently, it's going well but now I have a question.
I have a template with a topnav and a contentpart. All with its own controller.
With a button I can open a "submenu" where I can choose data from the database, within the "Liquid"-section. Thats working pretty well.
Since the Topnav is rendered at the login of the page, the topnav wont be rendered again.
If I add a Liquid in the content section, I have to reload the data behind the "Liquid"-Dropdown.
That dropdown is encapsulated in a directive:
function liquidselect(Data){
return {
restrict: 'AE',
templateUrl: 'views/controls/liquid_select.html',
scope: {
selectedValues : '='
},
link: function(scope) {
},
controller: function ($scope) {
//
Data.post('ownrecipes').then(function (results) {
$scope.searchRes = results;
});
// debugger;
//$scope.searchRes = RecipeDataService.data;
$scope.disabled = undefined;
$scope.searchEnabled = false;
$scope.searchRes = [];
$scope.flavoring = {amount: {}};
$scope.updateModelValue = function (selected) {
// console.log(selected);
//
$scope.selectedValues = selected;
};
}
}
}
The communication to the server is done by a factory:
app.factory("Data", ['$http', 'toaster',
function ($http, toaster ) { // This service connects to our REST API
// var deffered = $q.defer();
var serviceBase = 'api/v1/';
var obj = {};
obj.toast = function (data) {
toaster.pop(data.status, "", data.message, 3000, 'trustedHtml');
}
obj.get = function (q) {
return $http.get(serviceBase + q).then(function (results) {
return results.data;
});
};
obj.post = function (q, object) {
return $http.post(serviceBase + q, object).then(function (results) {
return results.data;
});
};
obj.put = function (q, object) {
return $http.put(serviceBase + q, object).then(function (results) {
return results.data;
});
};
obj.delete = function (q) {
return $http.delete(serviceBase + q).then(function (results) {
return results.data;
});
};
return obj;
}]);
How can I update/reload the data of the directive from a child scope? Is there any chance?
Here is a plunker which showcases my problem and want I want to do:
http://plnkr.co/edit/bNANkQYZfBaS4CHH3dwX
I hope it's helpful for you :-)
Layout:
Basic Layout

Angular service issue -- returning an array in a service containing promises

As you can see this is my first time attempting this and I appear to be doing it incorrectly. I just want to take some code, consisting of promises and http requests, and put it in a service before the controller uses it. My goal is to simply clean up the controller so it doesn't contain all of that code.
After logging it in the last step of the controller the object appears as undefined. Also, all the requests are being made successfully. So, it's jumping through all the hoops fine so I'm guessing it must not be returning any value in the service and nothing gets passed on to the subsequent function in the controller. How can I return the 'people' array in the service after the promises have been fulfilled?
var myApp = angular.module('myApp', []);
myApp.controller('AppCtrl', function ($scope, $http, dataService) {
var getPeople = function () {
return $http.get('/getpeople');
};
getPeople().then(function (response) {
dataService.compilePeople(response)
})
.then(function (people) {
console.log(people);
$scope.people = people;
});
});
myApp.service('dataService', function ($q, $http) {
this.compilePeople = function (response) {
var people = [];
names = response.data;
grandPromiseArray = [];
names.forEach(function (index) {
var name = index,
count = $q.defer(),
skills = [],
urls = '/getskillsbyname/' + name,
urlc = '/getcountbyname/' + name;
grandPromiseArray.push(
$q.all([$http.get(urls), $http.get(urlc)])
.then(function (response) {
people.push({
name: name,
skills: response[0].data,
count: response[1].data
});
})
);
});
return $q.all(grandPromiseArray).then(function () {
return people
});
}
});
You need to return the promise from compilePeople() in order for the people to be passed into the next .then() handler. so close ;)
getPeople()
.then(function (response) {
//You were missing this return
return dataService.compilePeople(response)
})
.then(function (people) {
console.log(people);
$scope.people = people;
});

Angular JS: Increment a Counter in a Service From Controller

I'm trying to fetch a value from an API inside my service for a counter, and then increment on that counter inside my controller:
service
angular.module('app')
.factory('MyService', MyService)
function MyService($http) {
var url = 'URL;
return {
fetchTotal: function(p) {
return $http.get(url, { params: p })
.then(function(response) {
var total = response.data.meta.total;
return total;
}, function(error) {
console.log("error occured");
})
},
incrementCounter: function(){
total++;
}
}
}
controller
app.controller('Controller1', function ($scope, MyService) {
var params = {
...
}
MyService.fetchTotal(params).then(function(response) {
$scope.counter = response;
});
$scope.incrementCounter = function(){
MyService.incrementCounter();
}
});
view
<div ng-controller="Controller1">
{{ counter }}
<span ng-click="incrementCounter()">increment</span>
</div>
I can't work out how increment on that total I get back from the API with an ng-click. Is this possible?
Any help is appreciated. Thanks in advance!
Angularjs services are singletons, so you can create variables and share it among controllers.
Notice below how variable total is initialized in the service. Same variable is used for interaction with the controllers by using the getCounter() method.
Also notice the init() method. Your controller can call MyService.init first thing to initialize the total variable.
angular.module('app')
.factory('MyService', MyService)
function MyService($http) {
var url = 'URL';
var total = 0; /* or some other initial value*/
return {
init: function(){
$http.get(url, { params: p })
.then(function(response) {
total = response.data.meta.total;
}, function(error) {
console.log("error occured");
});
},
incrementCounter: function(){
total++;
},
getCounter: function(){
return total;
}
}
}
See plunker for demo: http://plnkr.co/edit/idSxgm0axk43ydThTbJF?p=preview
Looks like you are doing everything right, except the variable total.
You should initialize it in an upper level so that it is also visible from incrementCounter function:
function MyService($http) {
var url = 'URL;
var total = 0;
return {
fetchTotal: function(p) {
return $http.get(url, { params: p })
.then(function(response) {
total = response.data.meta.total;
return total;
}, function(error) {
console.log("error occured");
})
},
incrementCounter: function(){
total++;
}
}
}
Hope it helps

angularjs ng-repeat not updating view when new data comes in

I have a service which will make a call to the server and returns the data. I am binding service to a variable on scope.
Example:
Let the service be DataModelService
in the controller : $scope.data = DataModelService
in the view <div ng-repeat="value in data.persons">{{value.name}}</div>
My Code :
This is how my code looks like:
/**DataModelService**/
factory('DataModelService', [
'DataService',
function (DataService) {
var service;
service = {
changeState: function (params) {
DataService.changePersonState(params)
.then(function (response) {
service.loadData(response.data);
});
},
loadData: function (responseData) {
service.persons = responseData.persons;
}
}
return service;
}
]);
/**DataService**/
factory('DataService', ['$http',
function ($http) {
return {
changePersonState: function (params) {
return $http.post("url", params);
}
}
}
]);
/**DataController**/
.controller('DataController', ['DataModelService',
function (DataModelService) {
$scope.data = DataModelService;
}
]);
/view/
<div ng-repeat = "person in data.persons" >{{person.name}} </div>
On the view I am doing a ng-repeat on a key in data i.e. ng-repeat="value in data.persons"
and also I have an option to change the state of person to active or inactive, so whenver i make a change to the state of the person, a call is sent to the server and data is set into the Service and as it is binded to the view, it should automatically update the data. But whats happening in my case, ng-repeat is not removing old data and instead it is appending new data to the old data.
For me its not good approach to write promise callback (then) into service. Because in your case, DataModelService returns data with some delay but not promise. And we don't know when.
So the way to make it work to add basic $timeout and fetch data from service by using other method.
So my suggestion is Demo
and your fixed example: Demo2
If we will take your example, it should be like:
JS
var fessmodule = angular.module('myModule', ['ngResource']);
fessmodule.controller('fessCntrl', function ($scope, DataModelService, $timeout) {
$scope.alertSwap = function () {
DataModelService.changeState('ff');
$timeout(function(){
$scope.data = DataModelService.getResponse();
}, 10);
}
});
fessmodule.$inject = ['$scope', 'Data', '$timeout'];
/**DataModelService**/
fessmodule.factory('DataModelService', [ 'DataService',function (DataService) {
var value = [];
var service = {
changeState: function (params) {
DataService.changePersonState(params)
.then(function (response) {
value = response.persons;
});
},
getResponse : function(){
return value;
}
}
return service;
}
]);
/**DataService**/
fessmodule.factory('DataService', ['$q',function ($q) {
var data = { // dummy
persons: [{
name: "Bob"
}, {
name: "Mark"
}, {
name: "Kelly"
}]
};
var factory = {
changePersonState: function (selectedSubject) {
var deferred = $q.defer();
deferred.resolve(data);
return deferred.promise;
}
}
return factory;
} //function
]);

Categories