share data between controllers angular js - javascript

Why $scope.items in First and Second controllers still have value First why it doesnot change to value From controller after invoking Load() function?
HomeController:
namespace MvcApplication6.Controllers
{
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
public JsonResult GetData()
{
string data = "From controller";
return Json(data, JsonRequestBehavior.AllowGet);
}
}
}
Index.cshtml
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap-theme.min.css" rel="stylesheet" />
<script src="~/Scripts/jquery-1.9.1.min.js"></script>
<script src="~/Scripts/angular.js"></script>
<script src="~/Scripts/MyScript/Custom.js"></script>
<script src="~/Scripts/angular-animate/angular-animate.min.js"></script>
<script src="~/Scripts/angular-ui/ui-bootstrap.min.js"></script>
<script src="~/Scripts/angular-ui/ui-bootstrap-tpls.min.js"></script>
<div ng-controller="LoadData" id="data">
</div>
<div ng-controller="First">
{{items}}
</div>
<div ng-controller="Second">
{{items}}
</div>
<script>
$(document).ready(function () {
angular.element(document.getElementById('data')).scope().Load();
});
</script>
Custom.js
var app = angular.module('MyApp', ['ngAnimate', 'ui.bootstrap']);
app.controller('First', function ($scope, sharedProperties) {
$scope.items = sharedProperties.getProperty();
console.log("First controller",sharedProperties.getProperty());
});
app.controller('Second', function ($scope, sharedProperties) {
$scope.items = sharedProperties.getProperty();
console.log("Second controller", sharedProperties.getProperty());
});
app.controller('LoadData', function ($scope,$http, sharedProperties) {
$scope.Load = function () {
$http({ method: 'GET', url: '/Home/GetData' }).
success(function (data, status, headers, config) {
sharedProperties.setProperty(data);
console.log('Loaded data',data);
}).
error(function (data, status, headers, config) {
alert('error');
});
}
}
);
app.service('sharedProperties', function () {
var property = 'First';
return {
getProperty: function () {
return property;
},
setProperty: function (value) {
property = value;
}
};
});

The problem is that, in both First and Second controllers, the variables $scope.items are initialized when the controllers are loaded. After that moment, they are not changed any more.
The order of execution here is as follows:
controller 'First' is initialized, along with its variable $scope.items;
controller 'Second' is initialized, along with its variable $scope.items;
the callback from $http is called, but you never update the value of $scope.items on either controller.
As a possible solution, you can implement a simple callback mechanism (the standard observer pattern). For example:
app.service('sharedProperties', function () {
var property = 'First';
var callBacks = [];
return {
getProperty: function () {
return property;
},
setProperty: function (value) {
property = value;
callBacks.forEach(function(callBack) {
callBack(value);
});
},
registerCallback: function(callBack) {
callBacks.push(callBack);
}
};
});
Then you have your controllers register their callbacks:
app.controller('First', function ($scope, sharedProperties) {
sharedProperties.registerCallback(function(data) {
$scope.items = data;
});
});
Alternatively you can use Angular events to communicate across controllers.

The reason is that you are working with strings. It means that when you calling:
$scope.items = sharedProperties.getProperty();
You're getting the copy of your string. To share the data between controllers, you can modify your controllers as following:
html:
...
<div ng-controller="First">
{{items.getProperty()}}
</div>
...
js:
...
app.controller('First', function ($scope, sharedProperties) {
$scope.items = sharedProperties;
});
...
or modify your service:
app.service('sharedProperties', function () {
// having the 'object' property inside
var property = {value:'First'};
return {
getProperty: function () {
return property;
},
setProperty: function (value) {
property.value = value;
}
}});
Both these solutions works because they're copying the reference to the object which contains the value instead of copying the value itself.

Related

Angular Datatables ng-click gives no binding (Angular way)

When page is loading first time, I'm getting my thingsList filled. But then I need choose option with ng-click, it triggers function doSomething() which is getting new thingsList. I can see at debug mode that there is a new list, but there's no binding and datatables still showing me the old thingsList.
I'd really like to solve this without dtOptions is it's possible.
I'm using pretty simple "angular way" with datatables:
<tr ng-repeat="thing in thingsList">
<td>{{thing.id}}</td>
<td>{{thing.name}}</td>
</tr>
and my controller looks like:
.controller('ThingsController', ['$http', '$scope', function ($http, $scope) {
this.getThing = function () {
$http.get(....).then(
function success(response) {
$scope.thingsList = response.data;
},
function error(data) {
console.log(data);
}
);
};
this.getThings();
this.doSomething = function (id) {
$http.get(....).then(
function success(response) {
$scope.thingsList = response.data;
},
function error(data) {
console.log(data);
}
);
};
}]);
Try using
$scope.thingsList.splice(0); // clears the array without losing reference
Array.prototype.push.apply($scope.thingsList, response.data); // inserts new data to existing array
instead of $scope.thingsList = response.data; in doSomething function.
So I'm guessing the reason its happening is because the getThings() function is being called every time the controller is used. You might want to modify your controller code in this way and try:
.controller('ThingsController', ['$http', '$scope', function ($http, $scope) {
$scope.getThing = function () {
$http.get(....).then(
function success(response) {
$scope.thingsList = response.data;
},
function error(data) {
console.log(data);
}
);
};
$scope.doSomething = function (id) {
$http.get(....).then(
function success(response) {
$scope.thingsList = response.data;
},
function error(data) {
console.log(data);
}
);
};
}]);
Now this will solve your problem of the list not updating when you choose your option with ng-click but your list won't get loaded by default when you load the page since getThings() isn't being called.
My suggestion for that would be to use use ng-init as described in the answer to this question:
How to execute angular controller function on page load?
or better yet use $scope.on in this manner in your code:
.controller('ThingsController', ['$http', '$scope', function ($http, $scope) {
$scope.getThing = function () {
$http.get(....).then(
function success(response) {
$scope.thingsList = response.data;
},
function error(data) {
console.log(data);
}
);
};
$scope.$on('$viewContentLoaded', function() {
$scope.getThing();
})
$scope.doSomething = function (id) {
$http.get(....).then(
function success(response) {
$scope.thingsList = response.data;
},
function error(data) {
console.log(data);
}
);
};
}]);
In case you're using routing you can change the '$viewContentLoaded' to '$routeChangeSuccess' for ng-route or '$stateChangeSuccess' if you're using ui-router. (Don't worry about this unless you're using routes to change views)

how do i get the service response data into the md dialog angularjs?

i have created the custom service like this
app.service('userService', function($http,UrlService) {
return {
init: function(callback) {
$http.get(UrlService.baseUrl +'/api/users/list').then(function(user_response) {
callback(user_response);
});
}
}
})
Inside of my project main controller i have used like this to get the angular material design modal.
$scope.replyComplaint = function(user,complaint_id) {
complaint_id=user._id;
console.log(complaint_id)
$mdDialog.show({
controller: DialogCtrl,
templateUrl: 'submodules/user_management/replydialog.html',
resolve: { complaint_id : function() {return complaint_id;} },
locals: {
users: $scope.users
},
parent: angular.element(document.body),
clickOutsideToClose: true,
})
.then(function(response) {
$scope.response = response;
console.log(response);
}, function() {
//fail
});
};
created another controller for dialog as in the angular material docs as follows
function DialogCtrl($scope, $rootScope, $mdDialog, users,complaintService, UrlService, $http) {
complaintService.init(function(complaint_response) {
$scope.complaints = complaint_response.data;
$scope.getUsers();
});
$scope.getUsers = function(complaint_id) {
console.log(complaint_id);
$scope.hide = function() {
$mdDialog.hide();
};
$scope.cancel = function() {
$mdDialog.cancel();
};
$scope.replyMail = function(complaint_id) {
console.log(complaint_id);
$http.post(UrlService.baseUrl + '/api/complaints/complaint/'+complaint_id , {
complaint: "replyText"
}, $scope)
.then(function(response) {
console.log(name);
$state.reload();
}, function(response) {
console.log(name);
});
}
}
}
Now, i need to get the user_response data in DialogController. if i put console.log('$scope.users') inside of this userservice.init function, i can get the data. but not outside of it. how to get the response data outside of the userService.init function
userService.init(function(user_response) {
$scope.users = user_response.data;
}); //this is added in DialogController
Main intension is to get the user.comlaint_id in the post request of reply mail function . that user.complaint_id is a part of the user_response
Anyone please help me. Thanks
The $http.get call returns a promise, you can just use that.
app.service('userService', function($http,UrlService) {
return {
init: function(callback) {
return $http.get(UrlService.baseUrl +'/api/users/list');
}
}
});
Controller:
function Dialog($scope,$rootScope, $mdDialog,userService,UrlService,$http) {
// console.log(userService.init());
init();
function init() {
userService.init().then(function(response) {
$scope.users = response.data;
});
}
}
This also has the advantage of easier error handling:
function Dialog($scope,$rootScope, $mdDialog,userService,UrlService,$http) {
// console.log(userService.init());
init();
function init() {
userService.init().then(function(response) {
$scope.users = response.data;
}, function(error) {
// handle error
});
}
}
You should read up on angular/javascript promises and their chaining mechanism: angular promises
Here is the solution
userService.init(function(user_response) {
$scope.users = user_response.data;
$scope.init();
});
$scope.init = function() {
You can access $scope.users here
}
Call any method instead of init() in which you require $scope.users

How I insert a same data in multiple controllers ($scope) with one call in AngularJS?

I am using a factory to be call in two controllers that are in same page, however I don't want that this call AJAX two times. I'm try use $q.defer(), but it doesn't work!
The code:
(function () {
'use strict';
var app = angular.module('globalApp', []).config([
'$interpolateProvider', '$httpProvider',
function ($interpolateProvider, $httpProvider) {
delete $httpProvider.defaults.headers.common['X-Requested-With'];
$interpolateProvider.startSymbol('<%');
$interpolateProvider.endSymbol('%>');
}
]);
// Index Store List
app.controller('IndexController', ['$scope', '$storeList',
function ($scope, $storeList) {
$scope.stores = [];
$storeList.getStoreList().then(function (stores) {
$scope.stores = stores;
});
}]);
// Footer Store List
app.controller('StoreListController', [
'$scope', '$storeList',
function ($scope, $storeList) {
$scope.stores = [];
$storeList.getStoreList().then(function (stores) {
$scope.stores = stores;
});
}
]);
// Factory call list os stores
app.factory('$storeList', function ($http, $q) {
var stores = [];
return {
getStoreList: function () {
//is a request needed?
if (stores.length > 0) {
var deferred = $q.defer();
deferred.resolve(stores);
return deferred.promise;
}
var uri = rootUrl + 'getStoreList';
var responsePromise = $http.get(uri);
return responsePromise.then(function (result) {
var data = result.data;
//modify collection of stores...
if (data.status) {
var stores = data.stores;
return stores;
}
});
}
};
});
}());
<body ng-app="globalApp">
<section>
<div ng-controller="IndexController">
<nav>
<a ng-repeat="store in stores" href="<%store.link%>"><%store.name%></a>
</nav>
</div>
</section>
<footer>
<div>
<ul ng-controller="StoreListController">
<li ng-repeat="store in stores">
<h3><%store.name%></h3>
<p>
<%store.street%>, <%store.number%>
<%store.neighborhood%>, <%store.cityName%>
<a><%store.phone%></a>
<a><%store.email%></a>
</p>
</li>
</ul>
</div>
</footer>
</body>
If both controllers load at the same time and call the function, stores will not contain any data so it's length will evaluate to 0.
If you need to do this onLoad of the page why don't you call the function from a service and store the results. You can utilize an observer pattern in the controls to listen for changes to the set and persist those changes to both controllers to set the value of $scope.stores once the promise resolves.
My solution:
(function () {
'use strict';
// My configs (optional)
var app = angular.module('globalApp', []).config([
'$interpolateProvider', '$httpProvider',
function ($interpolateProvider, $httpProvider) {
delete $httpProvider.defaults.headers.common['X-Requested-With'];
$interpolateProvider.startSymbol('<%');
$interpolateProvider.endSymbol('%>');
}
]);
// Index Store List
app.controller('IndexController', [
'$scope', '$storeList', 'StoreService',
function ($scope, $storeList, StoreService) {
$scope.stores = [];
$storeList.getStoreList().then(function (stores) {
$scope.stores = stores;
StoreService.setStores(stores);
});
}]);
// Footer Store List
app.controller('StoreListController', [
'$scope', 'StoreService',
function ($scope, StoreService) {
$scope.stores = [];
$scope.$watch(function () {
return StoreService.getStores();
}, function (stores) {
$scope.stores = stores;
}, true);
}
]);
// StoreService to share vars between the controllers
app.service('StoreService', function () {
var stores = [];
return {
setStores: function (_stores) {
stores = _stores;
},
getStores: function () {
return stores;
}
};
});
// Factory make ajax call to get list of stores
app.factory('$storeList', function ($http) {
return {
getStoreList: function () {
var uri = rootUrl + 'getStoreList';
var responsePromise = $http.get(uri);
return responsePromise.then(function (result) {
var data = result.data;
if (data.status) {
return data.stores;
}
return [];
});
}
};
});
}());
<body ng-app="globalApp">
<section>
<div ng-controller="IndexController">
<nav>
<a ng-repeat="store in stores" href="<%store.link%>"><%store.name%></a>
</nav>
</div>
</section>
<footer>
<div>
<ul ng-controller="StoreListController">
<li ng-repeat="store in stores">
<h3><%store.name%></h3>
<p>
<%store.street%>, <%store.number%>
<%store.neighborhood%>, <%store.cityName%>
<a><%store.phone%></a>
<a><%store.email%></a>
</p>
</li>
</ul>
</div>
</footer>
</body>
You don't need to have defer here. Remove this chunk:
//is a request needed?
if (stores.length > 0) {
var deferred = $q.defer();
deferred.resolve(stores);
return deferred.promise;
}
And your IndexController and StoreListController are both loading the storelist and hence 2 AJAX calls. Remove the chunk from either of the 2 controllers
$scope.stores = [];
$storeList.getStoreList().then(function (stores) {
$scope.stores = stores;
});
EDIT
I Did not realize earlier, but since you are using the storeList outside the scope of your StoreController, You should remove the .getStoreList() call from StoreController only

Serving processed data using factory, AngularJS

I am working on an application in which I am calling a webservice and get a response. I am using that response in 2 different modules. In first module I am using as it is and in second module I am doing some formatting and using it.
I created a service for getting data as follows
angular.module('myApp').factory('getData',function($http, $q, restURLS) {
var getData= {};
getData.getTree = function () {
var deferred = $q.defer();
$http.get(restURLS.getTree).
success(function (data) {
deferred.resolve(data);
}).error(deferred.reject);
return deferred.promise;
};
return getData;
});
for Serving response I created another factory as follows
angular.module('myApp').factory('tree', function($http, $q, restURLS, getData, messages) {
var tree= {};
tree.hierarchy = {};
tree.formattedHierarchy = {};
function formatHierarchy(data) {
//some formatting on data.
tree.formattedHierarchy = data;
}
function callTree() {
getData.getTree()
.then(function (data) {
tree.hierarchy = angular.copy(data);
formatHierarchy(data);
}).catch(function () {
//error
});
}
callTree();
return tree;
});
I want to call webservice only once. if data is loaded then factory('tree') should send the data to controller. Otherwise factory('tree') should call webservice and load data.
you need something to know if you got your tree or not... try this:
(UPDATED)
var myApp = angular.module('myApp', ['ngMockE2E'])
// FAKE HTTP CALL JUST FOR EMULATE
.run(function ($httpBackend) {
var tree = [{
node1: 'abcde'
}, {
node2: 'fghi'
}];
$httpBackend.whenGET('/tree').respond(function (method, url, data) {
return [200, tree, {}];
});
})
// YOUR HTTP SERVICE
.factory('getData', function ($http, $q) {
return {
getTree: function () {
var deferred = $q.defer();
$http.get("/tree").
success(function (data) {
deferred.resolve(data);
}).error(deferred.reject);
return deferred.promise;
}
}
})
.factory('TreeFactory', function ($http, $q, getData) {
var tree = {};
var updated = false;
tree.hierarchy = {};
tree.formattedHierarchy = {};
function formatHierarchy(data) {
//some formatting on data.
tree.formattedHierarchy = data;
}
return {
callTree: function() {
if(!updated){
console.log("making http call");
return getData.getTree().then(function (data) {
tree.hierarchy = angular.copy(data);
formatHierarchy(data);
updated = true;
return tree;
}).
catch (function () {
//error
});
}else{
console.log("tree already loaded");
var deferred = $q.defer();
deferred.resolve(tree);
return deferred.promise;
}
}
};
}).controller("MyCtrl", ['$scope', 'TreeFactory', function ($scope, TreeFactory) {
$scope.updateTree = function(){
TreeFactory.callTree().then(function(data){
$scope.tree = data;
});
};
}]);
HTML
<div ng-app="myApp" ng-controller="MyCtrl" ng-init="updateTree()">tree: {{tree}} <br><button ng-click="updateTree()">UPDATE TREE</button></div>
CHECK THE FIDDLE

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