Inside my controller, I have:
$scope.friends = userService.data.user.friends
However, if user is null, I obviously get an error because userService.data.user.friends cannot be accessed.
So, now I use:
if(userService.data.user){
$scope.friends = userService.data.user.friends;
}else{
$scope.friends = null;
}
Problem is: if statement is not bound to $scope, and therefore is evaluated only once. So, if userService.data.user is initally null $scope.friends is also evaluated to null, but stays null even if friends are added touserService.data.userlater on (by $http request, for example)
I need to reload the route to get the new value of userService.data.user.friends.
How did I get scope bound to userService.data.user.friends, but also handle the null case ?
Service $parse – https://docs.angularjs.org/api/ng/service/$parse
angular.module('app', [])
.controller('ctrl', function($scope, $parse, userService) {
$scope.friends = $parse('data.user.friends')(userService)
$scope.someDeepProperty = $parse('data.user.friends.data.deep')(userService) || 'we are so deep';
})
.factory('userService', function($timeout) {
var userService = {}
userService.data = {
user: {
friends: []
}
}
$timeout(function() {
userService.data.user.friends.push({name: 'foo'})
}, 2000);
return userService;
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='app'>
<div ng-controller='ctrl'>
Friends: {{ friends }} - wait 2 seconds for update <br>
Deep property: {{ someDeepProperty }}
</div>
</div>
Related
I need to pass an object from one controller to another and have used this solution but it is not working.
Here the code:
angular.module("customerApp", [])
.controller('MainCtrl', function ($scope, myService, $http, $location) {
var vm = this;
vm.pinFormCheck = function () {
vm.count++;
if (vm.pinForm.$valid && vm.details.PIN === vm.pin && vm.count <= 2) {
location.href = "http://localhost:51701/Home/MainMenu";
$scope.obj = {
'cid': 'vm.details.CID',
'name': 'vm.details.Name',
'pin': 'vm.details.PIN',
'bal': 'vm.details.Bal',
'status': 'vm.details.cardStatus'
};
console.log(vm.details.Bal);//the correct balance get displayed in console
} else {
vm.failPin = true;
}
};
})
.controller('CheckCtrl', function ($scope, myService) {
$scope.data = myService.getObj();
})
.factory('myService', function () {
var obj = null;
return {
getObj: function () {
return obj;
},
setObj: function (value) {
obj = value;
}
}
});
Here is the view from which the first object is passed:
<body ng-app="customerApp">
<div ng-controller="MainCtrl as vm">
<form name="vm.pinForm">
<input type="password" ng-model="vm.pin" ng-required="true" />
<p><button ng-disabled="vm.count >=3" ng-click="vm.pinFormCheck();" ng-init="vm.count=0">Proceed</button></p>
</form>
...
Here' the second view where I need the object
<html ng-app="customerApp">
<body ng-controller="CheckCtrl">
<div>
<h1>your balance is {{data.bal}}</h1>
....
The balance from vm.details.Bal from the first view must appear in data.bal in the second view, but nothing is appearing.
You can just save vm.details in some factory.
And then get it in CheckCtrl from this factory.
Factories in AngularJS implement singleton pattern. So saved data will be kept in until your app exists.
You tried to do next thing myService.getObj(); But you didn't save anything to the service.
Inject myService to the MainCtrl and then save details into it.
app.controller("ListController1", ['$rootScope',function($rootScope) {
$rootScope.progressBar=10;
$rootScope.$watch(
function() {
return $rootScope.progressBar;
},
function(){
alert($rootScope.progressBar);
alert("changed");
},true)
}]);
app.controller("ListController2", ['$scope','$rootScope',function($scope,$rootScope) {
$scope.save=function() {
$rootScope.progressBar=20;
}
}]);
I want progressBar value form ListController2 to be reflected back in Listcontroller1. It seems i am doing something wrong with it. Please help any one. thank u.
Rather than sharing state with $routeScope, you should consider creating a service to share the state of the progress bar - this is one of the use cases of services.
When the save button is pressed in the code below, it updates the value in progressService. The value from progressService is watched in the first controller and the view is updated accordingly.
You can add progressService to as many controllers as you'd like.
var app = angular.module("app", []);
app.factory("progressService", [function() {
var service = this;
service.progressBar = 0;
return service;
}]);
app.controller("ListController1", ["$scope", "progressService", function($scope, progressService) {
progressService.progressBar=10;
$scope.progress = progressService.progressBar;
$scope.$watch(
function() {
return progressService.progressBar;
},
function(newValue) {
$scope.progress = newValue;
});
}]);
app.controller("ListController2", ['$scope','progressService',function($scope,progressService) {
$scope.save=function() {
progressService.progressBar=20;
}
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="ListController1">
Progress: {{progress}}
</div>
<div ng-controller="ListController2">
<button ng-click="save()">Save</button>
</div>
</div>
function ComponentController() {
var vm = this;
this.$onInit = function() {
vm.link = 'http://example.com/obj/' + vm.obj.id;
}
}
function MainController($scope, $timeout) {
$scope.arrObjs = [{id: 1, name: "object1"},{id: 2, name: "object2"}];
console.log('object1\'s id is ', $scope.arrObjs[0].id);
$timeout(function() { // simulates a call to server that updates the id
$scope.arrObjs[0].id = '3';
console.log('object1\'s new id is ', $scope.arrObjs[0].id, '. Expected the link above to be updated with the new ID');
}, 1000);
}
var someComponent = {
bindings: {
obj: '='
},
template: '<div>URL: <span>{{$ctrl.link}}</span></div>',
controller: ComponentController
};
angular.module('myApp', []);
angular
.module('myApp')
.controller('MainController', MainController)
.controller('ComponentController', ComponentController)
.component('someComponent', someComponent);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="MainController">
<div ng-repeat="obj in arrObjs track by obj.id">
<some-component obj="obj"></some-component>
</div>
</div>
</div>
After the ng-repeat and its someComponent are rendered one of the objects' obj.id changes (using a $timeout in the above example). The vm.link for that object still carries the old id!
Now, I know that the $onInit() is only run once inside someComponent, but why doesn't track by re-initialise the component because the obj.id changed?
If Angular truly tracked an array by the obj.ids, it should treat an obj whose id changes as a completely different object and re-initialise it, no?
Obviously a $watch on vm.obj.id within someComponent will fix it, but is there a way without adding yet another $watch?
NOTE: previously I was using
<div ng-repeat="objID in vm.arrObjIDs track by objID" ng-init="obj = vm.fnLookUpObjByID(objID)">
<someComponent obj="obj"></someComponent>
</div>
And that works perfectly! This is exactly how I expected the track by obj.id to work. But I'm trying to move away from the ng-init pattern.
You're missing something somewhere in your code that you're not showing us.
The following snippet works.
init() is not a standard function though. You probably mean $onInit()
function ComponentController() {
var vm = this;
console.log("not in init: " + this.obj.id);
this.$onInit = function() {
console.log("in init: " + vm.obj.id);
}
}
function MainController($scope) {
$scope.arrObjs = [{id: 1, name: "object1"},{id: 2, name: "object2"}];
}
var someComponent = {
bindings: {
obj: '='
},
template: '<div>ID: <span>{{$ctrl.obj.id}}</span></div>',
controller: ComponentController
};
angular.module('myApp', []);
angular
.module('myApp')
.controller('MainController', MainController)
.controller('ComponentController', ComponentController)
.component('someComponent', someComponent);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="MainController">
<div ng-repeat="obj in arrObjs track by obj.id">
<some-component obj="obj"></some-component>
</div>
</div>
</div>
EDIT:
If you add an asynchronous function in the middle, the compiling of the code will always be faster than the return of the async function.
Using $watch is the standard way of updating the view when data changes.
There's not other way.
Note: With components you can use $onChanges() but in this particular case it won't trigger since you have to change the reference of the object for it to update. $onChanges() calls $watch in any case.
I am new to angular-js. I have two controllers (welcomeContoller,productController) and both handling the same model within the factory.
When the model getting updating by one controller(productController) it should reflect the update in another controller. (welcomeContoller)
But its not happening now.
HTML code :
<body ng-app="myApp">
<div ng-controller="welcomeContoller">
{{totalProductCnt}}
</div>
<div ng-controller="productController">
<div class="addRemoveCart">
<span class="pull-left glyphicon glyphicon-minus" ng-click="removeProduct()"></span>
<span class="pull-right glyphicon glyphicon-plus" ng-click="addProduct(1)"></span>
</div>
</div>
JS code
var myApp = angular.module("myApp", ['ui.bootstrap']);
myApp.factory("productCountFactory", function() {
return {
totalProducts:0
};
});
myApp.controller("welcomeContoller", function($scope, productCountFactory)
{
$scope.totalProductCnt = productCountFactory.totalProducts;
});
myApp.controller("productController", function($scope, productCountFactory) {
$scope.addProduct = function() {
productCountFactory.totalProducts++;
alert(productCountFactory.totalProducts);
};
$scope.removeProduct = function() {
if(productCountFactory.totalProducts >=1)
productCountFactory.totalProducts--;
alert(productCountFactory.totalProducts);
};
});
Even after the addProduct is called the totalProductCnt is displaying as zero. I want to display the value for each increment.
Plunkr Link
Put the factory object reference on scope:
myApp.controller("welcomeContoller", function($scope, productCountFactory) {
$scope.productCountFactory = productCountFactory;
});
Watch the property of the object.
{{productCountFactory.totalProducts}}
The DEMO on PLNKR.
By putting a reference on scope, on every digest cycle the watcher looks up the value of the property and updates the DOM if there is a change.
The totalProductCnt from your welcomeController isn't updated because it is assigned only once when the controller is created.
You can use several solutions to refresh the displayed value. Use a getter for your totalProducts in the factory :
myApp.factory("productCountFactory", function() {
var totalProducts = 0;
return {
getTotalProducts: function() {
return totalProducts;
},
addProduct: function() {
totalProducts++;
},
removeProduct: function() {
totalProducts--;
}
};
});
myApp.controller("welcomeContoller", function($scope, productCountFactory) {
$scope.getTotalProducts = productCountFactory.getTotalProducts;
});
myApp.controller("productController", function($scope, productCountFactory) {
$scope.addProduct = function() {
productCountFactory.addProduct();
};
$scope.removeProduct = function() {
if (productCountFactory.getTotalProducts() >= 1)
productCountFactory.removeProduct();
};
});
And update the view accordingly:
<div ng-controller="welcomeContoller">
{{getTotalProducts()}}
</div>
Plunkr Link
I have to update a parameter of the parent controller from a nested controller.
I'm able to read the parameter, but when I change it it does not update into the view (webpage)... help plz :)
This is my js:
app.controller('signalCtrl', [ '$scope', 'DB', function($scope, service) {
this.address = null;
}]);
app.controller('reportMap', ['$scope', function($scope) {
this.updateParent = function() {
$scope.$parent.tab.address = 'something';
}
}]);
And this is my HTML:
<div ng-controller="signalCtrl as signal">
<input type="text" ng-model="signal.address">
[...]
<div ng-controller="reportMap as map">
[...]
</div>
</div>
The address property in parent controller is not bound to $scope, but to this object, so you even can't reach that property that way. I suggest you to move your updating function to parent controller:
app.controller('signalCtrl', [ '$scope', 'DB', function($scope, service) {
var self = this;
self.address = null;
self.update = function(newValue) {
self.address = newValue;
}
}]);
app.controller('reportMap', ['$scope', function($scope) {
var self = this;
self.someValue = 'something';
}]);
HTML:
<div ng-controller="signalCtrl as signal">
<input type="text" ng-model="signal.address">
[...]
<div ng-controller="reportMap as map">
<button type="button" ng-click="signal.update(map.someValue)">Click!</button>
[...]
</div>
</div>
You don't call updating function in the presented code, so I added a button to show how to use it.
use signal instead of tab
app.controller('reportMap', ['$scope', function($scope) {
this.updateParent = function() {
$scope.$parent.signal.address = 'something';
}
}]);
see this link : https://plnkr.co/edit/RhQLfBy2heecJDuCcq9s?p=preview
Ok, probably the error is into te HTML:
(function () {
var app = angular.module('segnalazioni', ['filters', 'smart-table', 'smart-table-server']);
app.controller('nuovaSegnalazioneCtrl', [ '$scope', 'DB', function($scope, service) {
var self = this;
this.activatedTab = "tab_animale";
this.chipNumber = null;
this.indirizzo = 'ind';
this.setIndirizzo = function() {
self.indirizzo = "VIA ROMA";
console.log("AGGIORNAMENTO INDIRIZZO");
}
I call setIndirizzo from the child (i don't pass a value I know, but it is not the problem): into the console i read "AGGIORNAMENTO INDIRIZZO", but the value into the VIEW does not change...
this is a link to the complete html file.
https://www.dropbox.com/s/gzlm997ub8fot7r/html.txt?dl=0