Using services to share state between controllers - javascript

I want to share 'state' between multiple controllers within my Angular app by using services. My code is below. The problem I'm running into is that the controller which initiates the change in state, through vm.toggleStatus(), is updated, but this state does not update in my other controller until I refresh the page.
controller1.js:
angular.module('gameApp')
.controller('PlayerActionsController', PlayerActionsController);
PlayerActionsController.$inject = ['$routeParams', 'playersService'];
function PlayerActionsController($routeParams, playersService) {
var vm = this;
var playerId = $routeParams.playerId;
var getplayerInfo = function() {
playersService.getPlayerInfo({
playerId: playerId
}).$promise.then(function(player) {
vm.player = player;
});
};
var init = function() {
getplayerInfo();
};
init();
vm.toggleStatus = function() {
playersService.toggleStatus({
playerId: playerId
}, {}).$promise.then(function() {
getplayerInfo();
});
};
}
controller2.js:
angular.module('gameApp')
.controller('HeaderController', HeaderController);
HeaderController.$inject = ['$routeParams', 'playersService'];
function HeaderController($routeParams, playersService) {
var vm = this;
vm.playerId = $routeParams.playerId;
var getPlayerInfo = function() {
playersService.getPlayerInfo({
playerId: vm.playerId
}).$promise.then(function(player) {
vm.player = player;
});
};
var init = function() {
getPlayerInfo();
};
init();
}
players.service.js:
angular.module('gameApp')
.factory('playersService', ['$resource',
function($resource) {
var base = '/api/players/:playerId/';
return $resource(base, {}, {
getPlayerInfo: {method: 'GET', url: base + 'playerInfo'},
toggleStatus: {method: 'PUT', url: base + 'toggleStatus'}
});
}]);

Services do not push data to controllers, controllers need to either pull data from the service or listen to data changes.
What you probably want to do is update your service to emit an event when data changes and then have your controllers listed to those events and update when the change occurs. Example:
//service toggleStatus
toggleStatus = function(data) {
... what it currently does.
$rootScope.$emit('StatusChangedEvent', newStatus);
... return promise;
}
// in your controllers
$rootScope.$on('StatusChangedEvent', function(evt, newStatus) {
$scope.status = newStatus;
});

Related

Using $uibModal from a factory

I've been playing around with using uibModal from a factory instead of using it from within my controller. The dialog comes up, and the field data is returned to the service when OK is clicked, but, I don't know how to get the data back to my controller, where it will be added to my model Any pointers?
Here is my factory code:
'use strict';
angular.module('ngTableScopeApp')
.factory('DialogService', function($uibModal){
var DialogService = {};
DialogService.newObj = {};
DialogService.addNewItem = function(template, $q){
this.modalInstance = $uibModal.open({
templateUrl: template,
controller: function($scope, $uibModalInstance){
$scope.ok = function () {
$uibModalInstance.close($scope);
return this.newObj;
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
return null;
};
}
});
};
return DialogService;
});
Here is the controller code:
'use strict';
/**
* #ngdoc function
* #name ngTableScopeApp.controller:MainCtrl
* #description
* # MainCtrl
* Controller of the ngTableScopeApp
*/
angular.module('ngTableScopeApp')
.controller('MainCtrl', function (NgTableParams, DummyData, DialogService) {
var self = this;
self.data = DummyData.generateData(1);
var createUsingFullOptions = function() {
var initialParams = {
count: 10 // initial page size
};
var initialSettings = {
// page size buttons (right set of buttons in demo)
counts: [5, 10, 25, 50],
// determines the pager buttons (left set of buttons in demo)
paginationMaxBlocks: 13,
paginationMinBlocks: 2,
dataset: self.data //DummyData.generateData(1)
};
return new NgTableParams(initialParams, initialSettings);
};
self.customConfigParams = createUsingFullOptions();
self.addNewItem = function(){
DialogService.addNewItem('views/addNewItem.html', self);
};
});
You could use close method available on $uibModalInstance service, in which you can pass data while closing a popup. And then you can utilize result promise object which does gets called when modal gets closed. Whatever data passed from $uibModalInstance.close method is available there. Make sure you are returning promise returned by $uibModal.open method.
Factory
DialogService.addNewItem = function(template, $q){
this.modalInstance = $uibModal.open({
templateUrl: template,
controller: function($scope, $uibModalInstance){
$scope.ok = function () {
$uibModalInstance.close({ data: 'OK Called' });
};
$scope.cancel = function () {
$uibModalInstance.close({ data: 'Cancel Called' });
};
}
});
};
return this.modalInstance;
};
Controller
DialogService.addNewItem('views/addNewItem.html', self)
.result.then(function(data) {
console.log("data", data); // print { data: 'MyCustomData' }
});
Modal Controller
$scope.cancel = function () {
$uibModalInstance.close({data: 'MyCustomData'});
};

Pass value from provider to controller in angularJs

I'm trying get data from db to UI. Url given via provider is getting the data.
Controller in controller DetailsProvider.getDashboardDetails() is getting null.
var appmod = angular.module('project.DetailDashboardController', []);
appmod.controller("DetailDashboardController", ['$rootScope', '$scope', '$state', 'DetailsProvider',function($rootScope, $scope, $state,DetailsProvider) {
console.log("DetailDashboardController --- ");
$scope.DetList= DetailsProvider.getDashboardDetails()
}]);
})(window, window.angular);
provider which will call the list
(function(angular) {
var appmod = angular.module('project.DetailsServiceProvider', []);
appmod.provider('DetailsProvider', function() {
this.$get = ['_$rest', function DetailServiceFactory(_$rest) {
return new DetailsProvider(_$rest);
}];
});
function DetailsProvider(_$rest) {
this._$rest = _$rest,
this.getDashboardDetails = function(_callback, _data) {
var newData = null;
_$rest.post({
url: window.localStorage.getItem('contextPath') +'home/listdetail',
data: {} ,
onSuccess:_callback
}
});
}
};
})(window.angular);
Thanks in advance for any kind of reply!
You should return promise from your service method and do thenable in your controller.
Root Cause : your are returning the newData which will initalized later after completing the ajax call.Before completing it,you are returning the same variable which will be always null.
In provider,
(function(angular) {
var appmod = angular.module('project.DetailsServiceProvider', []);
appmod.provider('DetailsProvider', function() {
this.$get = ['_$rest', function DetailServiceFactory(_$rest) {
return new DetailsProvider(_$rest);
}];
});
function DetailsProvider(_$rest) {
this._$rest = _$rest,
this.getDashboardDetails = function(_callback, _data) {
var newData = null;
_$rest.post({
url: window.localStorage.getItem('contextPath') +'home/listdetail',
data: {} ,
onSuccess:_callback
}
});
}
};
})(window.angular);
and in controller,
$scope.list = function() {
DetailsService.getDashboardDetails(function(data){
varr holdIt = data.data.DList;
});
};

Watch service's variable from another module inside a directive

I have two modules "core" and "ui".
The ui module depends on core. This is the code for my core.js :
var core = angular.module('core', [ 'ngRoute' ]);
//Services
core.service('httpInformationService', function() {
this.requestCount = 0;
this.responseCount = 0;
this.incrementRequest = function() {
this.requestCount++;
console.log('incrementRequest:' + this.requestCount);
};
this.incrementReponse = function() {
this.responseCount++;
}
this.decrementRequest = function() {
this.requestCount--;
console.log('decrementRequest:' + this.requestCount);
};
this.decrementResponse = function() {
responseCount--;
}
this.getRequestCount = function() {
return requestCount;
}
this.getResponseCount = function() {
return responseCount;
}
});
//Service provider
core.provider("httpServiceInformationProvider", function() {
var provider = {};
provider.$get = ['httpInformationService', function( service ) {
return service;
}];
return provider;
});
//HTTP Interceptor
core.factory('coreHttpInterceptor' ,function( httpInformationService ){
var coreHttpInterceptor = {
request: function(config) {
httpInformationService.incrementRequest();
return config;
},
response: function(response) {
httpInformationService.decrementRequest();
return response;
}
}
return coreHttpInterceptor;
});
var config = {
base_url: enviromnent_url,
}
core.value('config', config);
core.config(function( $interpolateProvider ) {
$interpolateProvider.startSymbol( "[[" ).endSymbol( "]]" );
});
core.config(function( $httpProvider ) {
$httpProvider.interceptors.push('coreHttpInterceptor');
});
This is my ui.js code:
var ui = angular.module('ui',[ 'core' , 'ui.bootstrap' ]);
ui.directive( "shLoadify" , function( httpServiceInformationProvider ){
return {
restrict: "AE",
link: function(scope, element, attrs) {
element.bind( "click", function() {
element.text("Loading...");
element.prop( "disabled", true );
});
},
controller: function($scope) {
$scope.$watch('httpServiceInformationProvider', function(oldValue, newValue){
console.log(oldValue + ' ' + newValue);
}, true);
}
}
});
As you can see i am trying to access requestCount property of httpInfomationService from within my controller using $scope.watch.
The problem is newValue and oldValue is always null. Why is that so?
Approach 1
If you want to perform some action whenever your requestCount variable gets changed which is part of service, you need to broadcast/emit which then you can listen through on. But in this case you need to pass the scope in your service which is not recommended.
var app = angular.module('app',['app1']);
app.service('myService',function($rootScope){
this.requestCount=1
this.incrementRequestCount=function(){
this.requestCount++
$rootScope.$broadcast('requestCountChanged', { message: this.requestCount });
}.bind(this)
})
app.controller('myController',['$scope','myService',function($scope,myService){
$scope.$on('requestCountChanged', function(event, args) {
// You will find the updated requestCount in args
});
$scope.click= myService.incrementRequestCount;
}])
var app1 = angular.module('app1',[]);
app1.controller('mySecondController',['$scope','myService',function($scope,myService){
$scope.$on('requestCountChanged', function(event, args) {
// You will find the updated requestCount in args
});
}])
Approach 2
Without passing scope in the service
var app = angular.module('app',['app1']);
app.service('myService',function(){
this.requestCount=1
this.incrementRequestCount=function(){
debugger;
this.requestCount++
}.bind(this)
})
app.controller('myController',['$scope','myService','$rootScope',function($scope,myService,$rootScope){
$scope.click=function(){
myService.incrementRequestCount();
$rootScope.$broadcast('requestCountChanged', { message: myService.requestCount });
}
}])
var app1 = angular.module('app1',[]);
app1.controller('mySecondController',['$scope','myService',function($scope,myService){
$scope.$on('requestCountChanged', function(event, args) {
// You will find the updated requestCount in args
});
}])
Approach 3
You can only attach watch to those properties which are actually in the scope otherwise you cannot have watch for those properties. So just add requestCount on you scope than you can easily detect its changes using watch and then use broadcast/emit approach.
var app = angular.module('app',['app1']);
app.service('myService',function(){
this.requestCount=1
this.incrementRequestCount=function(){
debugger;
this.requestCount++
}.bind(this)
})
app.controller('myController',['$scope','myService','$rootScope',function($scope,myService,$rootScope){
$scope.requestCount=myService.requestCount
$scope.$watch('requestCount',function(n,o){
debugger;
if(n!=o)
{
$rootScope.$broadcast('requestCountChanged', { message: n });
}
})
$scope.click=function(){
myService.incrementRequestCount();
$scope.requestCount=myService.requestCount
}
}])
var app1 = angular.module('app1',[]);
app1.controller('mySecondController',['$scope','myService',function($scope,myService){
$scope.$on('requestCountChanged', function(event, args) {
// You will find the updated requestCount in args
});
}])

Angular - pass info from array from one controller to another

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

two button on two different views calling the same function, angularjs

I have a SPA with two different views one for subjects and one for student,
in subject view I have a save button in app/views/subject/subject.html:
<button type="button" class="btn btn-warning" ng-click="saveInfo()">
Save
</button>
I want to add the same function in the student views , saveInfo() pass the data into a service in the app factory which save the data in DB through fill_table.php.
the app factory in app/javascript/services.js:
var app = angular.module('myApp');
app.factory("services", ['$http', function($http) {
var serviceBase = 'http://localhost/php/';
var obj = {};
document.title = "myApp on " + serviceBase;
obj.postData = function (user, data) {
return $http.post(serviceBase + 'fill_table.php', { "data": data, "user": {'username': user.name, 'password': user.password }).then(function (results) {
return results.data;
});
};
saveInfo() is in app/views/subject/subject.js:
$scope.saveInfo = function() {
console.log("saveInfo");
$scope.loadingInstance = $modal.open({
animation: $scope.animationsEnabled,
templateUrl: 'modalLoading.html',
size: "l",
});
return getChanges( $indexedDB, $q).then( function(responsesArray) {
var jsonData = {};
$scope.errorInstance = undefined;
for (var i=0; i < DB_TABLES.length; i++) {
var table = DB_TABLES[i];
var items = responsesArray[i]
if (items.length > 0){
jsonData[table] = items;
}
}
console.log(JSON.stringify(jsonData));
return services.postData($scope.selectedUser, jsonData);
})
}
I want to add the mentioned button into app/views/student/student.html. i tried and copied the code from the subject.js into Student but for some reason it does not work eventhough i checked everything was correct so is there a way to only that function from subject.js into Student.html
note 1 getChanges() is another function get the inserted info and pass it into saveinfo().
note 2 right now I can save the info inserted student view by pressing save button in subject view
If I understand you correctly, you have two html files and two controller (student and subject). To share data/functions between these, you could use a service or factory to handle all your http request. This is reusable and accessible from all your controllers.
app.factory("services", ['$http', function($http) {
var postStudent = function (student) {
return $http.post("api/Student/Post", student);
};
var getChanges = function (){
return $http.get("api/Student/Changes", student);
};
return {
postStudent : postStudent,
getChanges : getChanges
};
}])
Now you can use can call the services from your controller as you see fit.
app.controller('StudentController', ['service', function(service){
service.postStudent(student).then(function successCallback(response) {
console.log('success');
}, function errorCallback(response) {
console.log('error ' + response);
});
service.getChanges().then(function successCallback(response) {
console.log('success');
}, function errorCallback(response) {
console.log('error ' + response);
});
}]);
app.controller('SubjectController', ['service', function(service){
service.postStudent(student).then(functionsuccessCallback(response){
},
function errorCallback(response) {
});
service.getChanges().then(function successCallback(response) {
},
function errorCallback(response) {
});
}]);
Note that the above has not been implemented, but should provide you with an outline.

Categories