How to share a $scope object between multiple controllers - javascript

I am facing problem while sharing a $scope object between 2 controllers.
In controller 'IssueBookCtrl',I am updating the books object element value like this.
$scope.books[i].issued = true;
Then I am using $emit service to share the $scope object with controller 'BookListCtrl_Librarian'.
$scope.$emit('update_parent_controller', $scope.books);
But when I run the view which is using the controller 'BookListCtrl_Librarian',i don't see the updated object.
controller.js
Controllers.controller('BookListCtrl_Librarian', ['$scope','$http','$location','$rootScope','BookData',
function ($scope, $http ,$location, $rootScope , BookData) {
$http.get('data/books.json').success(function(data) {
if($rootScope.books==='undefined'){
$rootScope.books = BookData.getData();
}
$scope.books = data;
});
$scope.options = ['bookId', 'cost'];
$scope.$on("update_parent_controller", function(event, books){
$scope.books = books;
});
$scope.issue = function(bookId) {
for (var i = 0, len = $scope.books.length; i < len; i++) {
if ($scope.books[i].bookId == bookId) {
$rootScope.book = $scope.books[i];
break;
}
}
$location.path('/issue/'+bookId);
}
$scope.return = function (bookId) {
for (var i = 0, len = $scope.books.length; i < len; i++) {
if ($scope.books[i].bookId == bookId) {
$rootScope.book = $scope.books[i];
break;
}
}
$location.path('/return/'+bookId);
}
$scope.backToLogin = function() {
$location.path("/main");
}
}]);
Controllers.controller('IssueBookCtrl', ['$scope','$rootScope','$http','$routeParams','$location',
function ($scope,$rootScope, $http, $routeParams, $location) {
var Id=$routeParams.bookId;
$http.get('data/books.json').success(function(data) {
$scope.books = data;
});
$scope.issue = function(Id) {
alert("issued");
for (var i = 0, len = $scope.books.length; i < len; i++) {
if ($scope.books[i].bookId === Id) {
$scope.books[i].issued = true;
$scope.$emit('update_parent_controller', $scope.books);
$location.path('/home/librarian');
break;
}
}
}
}]);
Please guide me,any help is much appreciated.
Thanks

hi u have to use rootscope instead of scope emit
$rootScope.$emit('update_parent_controller', $scope.books);
and in other controler
$rootScope.$on('update_parent_controller', function(event, books){
$scope.books = books;
});

You could try forcing a digest after altering a model:
$scope.$apply();
But I would recommended that you build a Books service to hold those shared models and logic. You can learn about creating your own custom services here.
Or you could nest your controllers (put one inside the other) so that the inner controller can reference the outer controller's models by using $parent variable.
By using either you should not have any problems with object updating as AngularJs runs dirty-checks when scope variables are changed.

Related

Unable to pass argument to http get request in angular js

I'm pretty much new in angular js. What I am trying to do is pass an integer argument to http get request in my controller. This is how my sample code looks like.
(function() {
angular
.module('myApp.directory', [])
.factory('NewsService', function($http)
{
return {
getallnews: function() {
return $http.get('get_all_news_feed.php?page='+pageNumber);
}
};
})
.factory('NewsFeed', function(directoryService) {
var NewsFeed = function() {
this.items = [];
this.busy = false;
this.pageNumber = 1;
};
NewsFeed.prototype.nextPage = function() {
if (this.busy) return;
this.busy = true;
NewsService.getallnews().success(function(data) {
var itemData = data;
for (var i = 0; i < itemData.length; i++) {
this.items.push(itemData[i]);
}
this.pageNumber++;
this.busy = false;
}.bind(this));
};
return NewsFeed;
})
.controller('MyController', function(NewsFeed, NewsService) {
var inst = this;
inst.news = new NewsFeed();
});
})();
I am building a news feed app. News is fetched from get_all_news_feed.php page and I want to pass a parameter pageNumber to it. This is while implementing infinte scrolling in angular.
I am getting undefined error. Any ideas?
Modify the factory method to accept pageNumber as parameter
getallnews: function(pageNumber) {
return $http.get('get_all_news_feed.php?page='+pageNumber);
}
Pass it when calling the method
NewsService.getallnews(this.pageNumber)

Enrich angularjs json data with additional view logic properties

When I load my json data from the server I need to have additional view only properties on json data. But thats not possible.
Then I thought about creating angularjs models from factories like:
'use strict';
angular.module('TGB').factory('TestViewModel', function () {
function TestViewModel(test) {
this.id = test.id;
this.schoolclassCode = test.schoolclassCode;
this.testType = test.type;
this.creationDate = test.date;
this.number = test.number;
this.isExpanded = false;
}
return (TestViewModel);
});
Where do you create these viewmodels in angular?
At the moment I have this method in my Controller , call it there and assign the result to $scope.testViewModels = toTestListViewModel(tests)
function toTestListViewModel(tests)
{
var testListViewModel = [];
for (var i = 0; i < tests.length; i++) {
var testViewModel = new TestViewModel(tests[i]);
testListViewModel.push(testViewModel);
}
return testListViewModel;
}
Controller is a proper place to create your 'view model' that can be accessed using $scope.
.controller('sampleCtrl', function ($scope, testViewModel) {
$scope.vM = testViewModel;
}

Dynamically set data on Angular-chart.js

To be honest I am a bit new to angularjs, so this may be problem with my fundamental understanding of angular, rather than angular-charts.
I have two controllers (PieItemsCtrl and PieCtrl) and I would like to communicate between them by using a factory service (called pieItems)
On the one hand the pieItems works as designed in the PieItemsCtrl.
ie:
$scope.slices = pieItems.list();
Whenever something changes in the pieItems service (ie another element is added), then the HTML is automatically updated via a repeater :
<div ng-repeat="(key, val) in slices">
However in the PieCtrl I have this line, and i would expect the pie chart to update automatically :
$scope.labels = pieItems.labelsItems();
$scope.data = pieItems.data();
It seems to set these data values upon loading/initialisation of the PieCtrl and that's it. Whenever the pieItems data changes these scope values are not updated.
The source of the two controller and factory object are below. And I also made an unworkable fiddle, incase that helps
PieItemsCtrl :
app.controller('PieItemsCtrl', function($scope, $http, $rootScope, pieItems) {
$scope.slices = pieItems.list();
$scope.buttonClick = function($event) {
pieItems.add(
{
Name: $scope.newSliceName,
Percent: $scope.newSlicePercent,
Color: $scope.newSliceColor
}
)
}
$scope.deleteClick = function(item, $event) {
pieItems.delete(item);
}
}
)
PieCtrl :
app.controller("PieCtrl", function ($scope, $timeout, pieItems) {
$scope.labels = pieItems.labelsItems();
$scope.data = pieItems.data();
});
pieItems :
app.factory('pieItems', function() {
var items = [];
var itemsService = {};
itemsService.add = function(item) {
items.push(item);
};
itemsService.delete = function(item) {
for (i = 0; i < items.length; i++) {
if (items[i].Name === item.Name) {
items.splice(i, 1);
}
}
};
itemsService.list = function() {
return items;
};
itemsService.labelsItems = function() {
var a = ['x', 'y'];
for (i = 0; i < items.length; i++) {
a.push(items[i].Name);
}
return a;
};
itemsService.data = function() {
var a = [50,50];
for (i = 0; i < items.length; i++) {
a.push(items[i].Percent);
}
return a;
};
return itemsService;
});
The controller doesn't notice when the value in your factory changes. To include your item-Array in an Angular digest-cycle, tell Angular to $watch that Array.
If you don't want to expose the Array, create a getter:
itemsService.get = function() { return items; }
Then you can include that getter in your $watch expression in your controller:
$scope.$watch(getItems, watcherFunction, true);
function getItems() {
return pieItems.get();
}
The getItems-Function gets called on digest cycle and fires the watcherFunction if the value changed and has the newData as argument. true as 3rd argument creates a deep watch.
function watcherFunction(newData) {
console.log(newData);
// do sth if array was changed
}
For more complex objets, you can use a $watchCollection.

Why am I getting ngRepeat:dupes when sending data from an Array into a differently named Array?

Error: ngRepeat:dupes Duplicate Key in Repeater
http://plnkr.co/edit/hZtIXkPM7dhpf4P7rd6W?p=preview
I have an array which ng-repeats a list of tags on the page. Next I have an ng-click which sends the tag data into the scope of another controller whois job it is to display those selected tags in another list.
It's easier to see the code in action in the plnkr above, but the basics are:
the first tags Array is in the cnt controller
when you click on a tag, it gets stored in the TagDetailsFactory service
I then broadcast an event to the view controller to then call the getTagDetails function in TagDetailsFactory to retrieve the saved tags and store them into the viewTags array in the view controller.
This is where I'm getting the ngDupes error :(
However, the array in cnt is named $scope.tags = [];
and the array in view is $scope.viewTags = [];
// Code goes here
angular.module('app', [])
.directive('tagDetails', function() {
return {
restrict: "E",
link: function($scope, el, attrs) {
// console.debug($scope, attrs);
},
scope:{
tag:'=ngModel'
},
template: '<div ng-show="tag.showDetails">{{tag.details}}</div>'
};
})
.controller('cnt', ['$scope',
'$rootScope',
'TagDetailsFactory',
function($scope,
$rootScope,
TagDetailsFactory) {
$scope.tags = [];
for(var i = 0; i < 100; i++) {
$scope.tags.push(
{ name: 'Foo Bar ' + i, details: 'Details' + i }
);
}
$scope.showTagDetails = function(t) {
t.showDetails = true;
}
$scope.leaveTag = function(t) {
t.showDetails = false;
}
$scope.sendTag = function(t) {
TagDetailsFactory.saveTagDetails(t);
$rootScope.$broadcast('updateView');
}
}])
.factory('TagDetailsFactory', function() {
var savedTags = [];
var saveTagDetails = function(tag) {
savedTags.push(tag);
}
var getTagDetails = function() {
return savedTags;
}
return {
saveTagDetails : saveTagDetails,
getTagDetails : getTagDetails
};
})
.controller('view', ['$scope',
'$rootScope',
'TagDetailsFactory',
function($scope,
$rootScope,
TagDetailsFactory) {
$scope.viewTags = [];
$scope.$on('updateView', function() {
console.log('updateView');
var tags = TagDetailsFactory.getTagDetails();
console.log(tags);
$scope.viewTags.push(tags);
});
// $scope.showTagDetails = function(t) {
// t.showDetails = true;
// }
// $scope.leaveTag = function(t) {
// t.showDetails = false;
// }
}]);
ng-repeat does not allow duplicate items (otherwise how can it keep track of them, if you wanted to updated something, for example?).
"
In order to deal with this problem, you can add track by $index to your ng-repeat value:
ng-repeat="data in dataset track by $index"
You can have it track by other values in your data also, but it needs to be unique.
Your Plunker

AngularJS - Dynamic ngModel with "key.key.key..value"

I know this looks like a duplicate question, but I wasn't able to find anything pertaining this?
I have a variable such as "settings.page.header.title"
I would like to set the ngModel of an input to the above dynamically, as it will change.
I've tried the following without success:
1)
$scope.getDynamicModel = function(str) {
var levels = str.split(".");
var model = $scope;
for (var i = 0, i < levels.length; i++) {
var level = levels[i];
model = model[level];
}
return model;
}
.
<input ng-model="getDynamicModel('settings.page.header.title')">
2)
<input ng-model="{{ 'settings.page.header.title' }}">
Any ideas?
Wrote a directive that might help:
app.directive('myModel', function($compile){
return {
link: function(scope,elm,attrs){
elm.removeAttr('my-model');
scope.$watch(attrs.myModel, function(value){
if (value)
$compile(elm.attr('ng-model',value))(scope)
})
}
}
})
This will add a watcher that will update the ng-model for the element, and recompile. Removing my-model is done to avoid compiling the element infinitely.
DEMO
html
<input ng-model="settings.page.header.title" type="text">
js
$scope.settings = {
page: {
header: {
title: 'myTitle'
}
}
}
http://plnkr.co/edit/mrcBUNrq90awHaqPYOq4?p=preview
A little modifications to prevent failing:
$scope.getDynamicModel = function(str) {
var levels = str.split(".");
var model = $scope;
for (var i = 0; i < levels.length; i++) {
var level = levels[i];
model = model[level];
if (model == null) {
return model;
}
}
return model;
}
See fiddle

Categories