I have a scope variable, when it returns true, i need to trigger some events or do something. I my case, the every first time, the scope variable returns undefined and later it returns true. In this case i used $watch method to get the expected funcionality. Is there any alternative approach to do the same instead using $watch ?
scope.$watch () ->
scope.initiateChild
, (value) ->
if value is true
$timeout ->
scope.buildOnboarding()
, 1000
You can try using AngularJS $on(), $emit() and $broadcast().
Here is an example: http://www.binaryintellect.net/articles/5d8be0b6-e294-457e-82b0-ba7cc10cae0e.aspx
You can use JavaScript getters and setters without any expense of using $watch.
Write code in the setter to do what you want when angular changes the your model's value you are using in scope. It gets null or an a State object as user types. Useful for working with type ahead text boxes that have dependencies on each other. Like list of counties after typing state without user selecting anything.
Here is some pseudo style code to get the idea.
<input ng-model="searchStuff.stateSearchText" />
<div>{{searchStuff.stateObject.counties.length}}</div>
<div>{{searchStuff.stateObject.population}}</div>
$scope.searchStuff=new function(){var me=this;};
$scope.searchStuff.stateObject = null;
$scope.searchStuff.getStateObjectFromSearchText = function(search){
// get set object from search then
return stateObject;
};
$scope.searchStuff._stateSearchText= "";
Object.defineProperty($scope.searchStuff, 'stateSearchText', {
get: function () {
return me._stateSearchText;
},
set: function (value) {
me,_stateSearchText = value;
me.stateObject = getStateObjectFromSearchText (value);
}
});
See this fiddle: http://jsfiddle.net/simpulton/XqDxG/
Also watch the following video: Communicating Between Controllers
A sample example is given below
Html:
<div ng-controller="ControllerZero">
<input ng-model="message" >
<button ng-click="handleClick(message);">LOG</button>
</div>
<div ng-controller="ControllerOne">
<input ng-model="message" >
</div>
<div ng-controller="ControllerTwo">
<input ng-model="message" >
</div>
javascript:
var myModule = angular.module('myModule', []);
myModule.factory('mySharedService', function($rootScope) {
var sharedService = {};
sharedService.message = '';
sharedService.prepForBroadcast = function(msg) {
this.message = msg;
this.broadcastItem();
};
sharedService.broadcastItem = function() {
$rootScope.$broadcast('handleBroadcast');
};
return sharedService;
});
function ControllerZero($scope, sharedService) {
$scope.handleClick = function(msg) {
sharedService.prepForBroadcast(msg);
};
$scope.$on('handleBroadcast', function() {
$scope.message = sharedService.message;
});
}
function ControllerOne($scope, sharedService) {
$scope.$on('handleBroadcast', function() {
$scope.message = 'ONE: ' + sharedService.message;
});
}
function ControllerTwo($scope, sharedService) {
$scope.$on('handleBroadcast', function() {
$scope.message = 'TWO: ' + sharedService.message;
});
}
ControllerZero.$inject = ['$scope', 'mySharedService'];
ControllerOne.$inject = ['$scope', 'mySharedService'];
ControllerTwo.$inject = ['$scope', 'mySharedService'];
Related
I need to reflect some changes to controller B (inside some event) when I make change at controller A. For that I am using a service.
When I am changing service value from FirstCtrl, ng-change is not firing at SecondCtrl. Is there anything I have missed or need to change?
Please note that I am using angular 1.5.6. and don't want to use watch or even scope.
Below is my code.
var myApp = angular.module('myApp', []);
myApp.factory('Data', function() {
return {
FirstName: ''
};
});
myApp.controller('FirstCtrl', ['Data',
function(Data) {
var self = this;
debugger
self.changeM = function() {
debugger
Data.FirstName = self.FirstName;
};
}
]);
myApp.controller('SecondCtrl', ['Data',
function(Data) {
var self = this;
self.FirstName = Data;
self.changeM = function() {
alert(1);
};
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="FirstCtrl as c">
<input type="text" ng-model="c.FirstName" data-ng-change="c.changeM()">
<br>Input is : <strong>{{c.FirstName}}</strong>
<div ng-controller="SecondCtrl as c1">
Input should also be here: {{c1.FirstName}}
<input type="text" ng-model="c1.FirstName" data-ng-change="c1.changeM()">
</div>
</div>
<hr>
</div>
As you dont want to use $scope trying modifying the code in order to use $emit and $on feature in angular js to communicate between two controllers. You can refer this link.
var myApp = angular.module('myApp', []);
myApp.factory('Data', function() {
return {
FirstName: ''
};
});
myApp.controller('FirstCtrl', ['Data',
function(Data) {
var self = this;
debugger
self.changeM = function() {
debugger
//Data.FirstName = self.FirstName;
Data.$on('emitData',function(event,args){
Data.FirstName=args.message
document.write(Data.FirstName)
})
};
}
]);
myApp.controller('SecondCtrl', ['Data',
function(Data) {
var self = this;
self.FirstName = Data;
self.changeM = function() {
Data.$emit('emitData',{
message:Data.FirstName
})
};
}
]);
The only way then is to directly copy the reference of the data object within the controller. Note that you don't need ng-change to update the value then.
If you want something else, either wrap the FirstName in a sub object of Data and do the same i did :
Data = {foo:'FirstName'};
Or use $watch since it's the whole purpose of that function.
Here is a working code with copying the Data object in the controller.
var myApp = angular.module('myApp', []);
myApp.factory('Data', function() {
return {
FirstName: ''
};
});
myApp.controller('FirstCtrl', ['Data',
function(Data) {
var self = this;
self.Data=Data;
debugger
self.changeM = function() {
debugger
};
}
]);
myApp.controller('SecondCtrl', ['Data',
function(Data) {
var self = this;
self.Data = Data;
self.changeM = function() {
alert(1);
};
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="FirstCtrl as c">
<input type="text" ng-model="c.Data.FirstName" data-ng-change="c.changeM()">
<br>Input is : <strong>{{c.Data.FirstName}}</strong>
<div ng-controller="SecondCtrl as c1">
Input should also be here: {{c1.Data.FirstName}}
<input type="text" ng-model="c1.Data.FirstName" data-ng-change="c1.changeM()">
</div>
</div>
<hr>
</div>
The only way I know to solve the problem is using watch, unfortunately. (I am new to angular.)
From the ngChange document (https://docs.angularjs.org/api/ng/directive/ngChange):
The ngChange expression is only evaluated when a change in the input value causes a new value to be committed to the model.
It will not be evaluated:
if the value returned from the $parsers transformation pipeline has not changed
if the input has continued to be invalid since the model will stay null
**if the model is changed programmatically and not by a change to the input value**
I have a angular service that gets the currentuser object that is resolved as a promise. I have a partial that is filled by an object userdetails which is invoked inside a method call, but unfortunately the method call is not firing when called after the promise is resolved.
.controller('AccountCtrl', [
'$scope', 'userService', '$compile', '$http', 'utility', 'cloudService', function ($scope, userService, $compile, $http, utility, cloudService) {
$scope.userdetails = {};
$scope.downloadPageChk = $scope.paymentHistoryPageChk = $scope.manageGroupsPageChk = "hide";
$scope.getUserAttribute = function (param, x) {
return userService.getAttribute(param, x);
};
cloudService.fetchCurrentUser().then(function (newCurrentuser)
{
if (newCurrentuser)
{
$scope.currentUser = newCurrentuser;
$scope.getUserDetails = function()
{
if (userService && userService.isLoggedIn())
{
$scope.userdetails = JSON.parse(JSON.stringify(currentUser));
}
};
if (newCurrentuser == 'member') {
if (newCurrentuser.features.download) $scope.downloadPageChk = "show";
if (newCurrentuser.features.paymenthistory) $scope.paymentHistoryPageChk = "show";
if (newCurrentuser.group.enabled) $scope.manageGroupsPageChk = "show";
}
}
}
])
partial
<div data-ng-controller="AccountCtrl">
<div data-ng-init="getUserDetails()">
<input type="text" class="form-control pull-left" id="FirstName" name="FirstName" data-placeholder-attr="First Name" data-ng-model="userdetails.firstname" required>
<input type="text" class="form-control pull-left" id="LastName" name="LastName" data-placeholder-attr="Last Name" data-ng-model="userdetails.lastname" required>
</div>
</div>
please do let me know what am I missing. I am banging my head for 10 hours now, if I move user details out if that cloudservice.fetchuser().then() it atleast calls the function, not sure what is happening when i put it inside.
Similar plunk created here
http://plnkr.co/edit/pLAB4VpLU7uhlSibHzXP?p=preview
Thanks
If you only want the data to be initializied - you don't need the $scope.getUserDetails function and the data-ng-init="getUserDetails()".
Angular will execute the service fetchCurrentUser function and will populate $scope.userdetails on loading.
cloudService.fetchCurrentUser().then(function (newCurrentuser)
{
if (newCurrentuser)
{
$scope.currentUser = newCurrentuser;
if (userService && userService.isLoggedIn())
{
$scope.userdetails = JSON.parse(JSON.stringify($scope.currentUser));
}
if (newCurrentuser == 'member') {
if (newCurrentuser.features.download) $scope.downloadPageChk = "show";
if (newCurrentuser.features.paymenthistory) $scope.paymentHistoryPageChk = "show";
if (newCurrentuser.group.enabled) $scope.manageGroupsPageChk = "show";
}
}
}
}
See plunker.
EDIT:
If you want the getUserDetails function to still be triggerable after initialization you should put it outside the service method:
$scope.getUserDetails = function(){
if (userService && userService.isLoggedIn()){
$scope.userdetails = JSON.parse(JSON.stringify($scope.currentUser));
}
};
And trigger it however you want (via ng-click="getUserDetails()" from the view or $scope.getUserDetails() from the controller).
You should move the function outside the cloudservice.fetchuser().then() method. The getUserDetails method is not available to the $scope object until you call cloudservice.fetchuser().then(). But you do not do this in your partial.
.controller('AccountCtrl', [
'$scope', 'userService', '$compile', '$http', 'utility', 'cloudService',
function ($scope, userService, $compile, $http, utility, cloudService) {
$scope.userdetails = {};
$scope.getUserDetails = function(service, user) {
if (service && service.isLoggedIn()) {
$scope.userdetails = JSON.parse(JSON.stringify(user));
}
};
$scope.downloadPageChk = $scope.paymentHistoryPageChk = $scope.manageGroupsPageChk = "hide";
$scope.getUserAttribute = function (param, x) {
return userService.getAttribute(param, x);
};
cloudService.fetchCurrentUser().then(function (newCurrentuser) {
if (newCurrentuser) {
$scope.currentUser = newCurrentuser;
$scope.getUserDetails(userService, newCurrentuser);
if (newCurrentuser == 'member') {
if (newCurrentuser.features.download) $scope.downloadPageChk = "show";
if (newCurrentuser.features.paymenthistory) $scope.paymentHistoryPageChk = "show";
if (newCurrentuser.group.enabled) $scope.manageGroupsPageChk = "show";
}
}
});
});
]);
I don't see any use of ng-init directive and $scope.getUserDetails method, angular two way data binding will handle the promises. I have updated the plunker for same. http://plnkr.co/edit/IzjNqpdk0zvnHYyCnKJi?p=preview
You are running ng-init before $scope.getUserDetails has been added to $scope, because that only happens in the callback, and that's why you are having the problems.
i think you may be following some of the basic examples a little too closely. As you are getting data asynchronously you don't need ng-init
You let you controller ask the service to download the data, and when it has arrived you assign it to $scope.getUserDetails and angular will put it on the screen.
You can see the updated plnkr
Let me know if I'm missing something significant
I am in learning phase of Angularjs and am stuck in a problem for last two days. I have seen lots of answer but don't know how to adapt those solutions in my case. What I want to do is update the input field via buttons using angularjs.
// html
<body ng-controller="Controller">
<input type="number" ng-model="data" update-view>
<br>
<label for="data">{{data}}</label>
<button name="btn1" ng-click='updateInput(1)'>1</button>
</body>
// js
var app = angular.module('calculator',[]);
app.controller('Controller', function($scope, $timeout){
$scope.data = 0;
var val = '';
$scope.updateInput = function(param) {
val += String(param);
$scope.data = val;
// val = param;
// $scope.data = val;
}
});
The expressions gets evaluated but the input field is not updating. I have seen other updating views with $setViewValue and $render but I don't know how to use them here.
app.directive('updateView', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
element.bind('change', function () {
// console.log(ngModel);
scope.$apply(setAnotherValue);
});
function setAnotherValue() {
ngModel.$setViewValue(scope.data);
ngModel.$render();
}
}
};
});
Any help would be appreciated. Thanks
You don't need a directive for updating.
You seem to be setting a string value to $scope.data, which throws an error, because the input type is number.
angular.module('calculator', [])
.controller('Controller', function($scope){
$scope.data = 0;
var val = '';
$scope.updateInput = function(n){
val = n;
$scope.data = val;
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
<body ng-app="calculator" ng-controller="Controller">
<input type="number" ng-model="data">
<button ng-click="updateInput(1)">1</button>
</body>
I was noting that without converting the parameter into string, the input field would update with the changed model but as soon as I would change it into String, it would not update the input field. Also there was error thrown in console. So I just, on hit and trial basis, converted it back to int by changing only one piece of line $scope.data = val; into $scope.data = parseInt(val, 10); and hurrrayyy the input field is all updating just like I wanted. And as #cither suggested, I don't need to directive for this. Following is my working code
var app = angular.module('calculator',[]);
app.controller('Controller', function($scope, $timeout){
$scope.data = 0;
var val = '';
$scope.updateInput = function(param) {
val += String(param);
$scope.data = parseInt(val, 10);
}
});
I made a service that stores a variable I use in two controllers.
function CommonVariables() {
var number = 3;
return {
setNumber: function(num) {
number = num;
},
getNumber: function() {
return number;
}
}
};
The problem is that I can get this variable like this:
this.number = CommonVariables.getNumber();
I want it to be changed like this:
<input class="numInput" ng-model="Ctrl.number">
in Js:
function Controller(CommonVariables) {
this.number = CommonVariables.getNumber();
CommonVariables.setNumber(this.number);
}
But I can't and don't understand why
You need to not only update the variable in the controller, but also send that update back to the service, from which it can be shared to any other part of your application.
=====================================================
edit: working code for me - check your console logs as you run it:
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"> </script>
<body>
<div ng-app="myApp" ng-controller="Ctrl">
<input class="numInput" ng-model="number" ng-change="changeNumber()">
</div>
</body>
<script type="text/javascript">
var app = angular.module('myApp', []);
app.controller("Ctrl", function(CommonVariables, $scope){
//this will initially set it as the same number as is stored in CommonVariables
$scope.number = CommonVariables.getNumber();
//this is the function that will actually send that number back to the service to update it there
$scope.changeNumber = function() {
console.log("Number set:", $scope.number);
CommonVariables.setNumber($scope.number)
console.log("Number retrieved after setting:", CommonVariables.getNumber());
}
})
.factory("CommonVariables", function(){
var number = 3;
return {
setNumber: function(num) {
number = num;
},
getNumber: function() {
return number;
}
}
})
</script>
You need to $watch the property number in the controller scope.
Inject $scope and use
function Controller(CommonVariables, $scope) {
$scope.$watch(angular.bind(this, function () {
return this.number;
}), function (newVal) {
CommonVariables.setNumber(newVal);
});
...
By looking at your code assuming you are using controller as syntax
I have a form with two input fields (session.email and session.psw) bound to the LoginController.session attribute. When I click the reset button, I call the LoginController.reset() function.
I would like make it clear the session attribute, utilizing the variable sessionDefault (empty). However it works just one time, if I reset two times the form, sessionDefault is undefined.
How could I make it as a constant attribute of the controller?
app.controller('LoginController', function ($scope)
{
this.session={};
var sessionDefault=
{
email : "",
psw: ""
};
this.reset = function()
{ this.session = sessionDefault; };
});
Try out this out
for reset function just reset it with sessionDefault copy like as shown below
vm.reset = function () {
vm.session = angular.copy(sessionDefault);
};
here this refers to the controller instance
Notice that I use var vm = this; and then I decorate vm with the members that should be exposed and data-bindable to to the View. vm simply denotes view modal
This does 3 things for me.
Provides a consistent and readable method of creating bindings in my controllers
Removes any issues of dealing with this scoping or binding (i.e. closures in nested functions)
Removes $scope from the controller unless I explicitly need it for something else
Working Demo
script
var app = angular.module('myApp', []);
app.controller('LoginController', function ($scope) {
var vm = this;
vm.session = {};
var sessionDefault = {
email: "",
psw: ""
};
vm.reset = function () {
vm.session = angular.copy(sessionDefault);
};
});
html
<div ng-app='myApp' ng-controller="LoginController as login">
Email:<input type="text" ng-model="login.session.email"/>{{login.session.email}}
<br>
Psw:<input type="text" ng-model="login.session.psw"/>{{login.session.psw}}
<br>
<button ng-click="login.reset()">Reset</button>
</div>
Take a look at this beautiful stuff.
AngularJS’s Controller As and the vm Variable
this.reset = function()
{ this.session = sessionDefault; };
The this in this context refers to the function (reset). If you want to access the 'original' this you need to store it in a variable.
app.controller('LoginController', function ($scope)
{
this.session={};
var sessionDefault=
{
email : "",
psw: ""
};
var self = this;
this.reset = function()
{ self.session = angular.clone( sessionDefault); };
});