AngularJS $watch not working for pagination, giving undefined error - angularjs

I'm a new bee in the angularjs world. I was trying to use some pagination logic in my code.
While using $watch, my app is throwing TypeError: undefined is not a function on $watch
.controller("EmployeeListCtrl", ["employeeResource", EmployeeListCtrl]);
function EmployeeListCtrl(employeeResource) {
var Empdata = this;
Empdata.employeePerPage = [];
employeeResource.query(function(data){
Empdata.employee = data;
});
Empdata.currentPage = 1;
Empdata.numPerPage = 10;
Empdata.maxSize = 5;
Empdata.numPages = function() {
return Math.ceil(Empdata.employee.length / Empdata.numPerPage);
};
Empdata.$watch('Empdata.currentPage + Empdata.numPerPage', function() {
var start = ((Empdata.currentPage - 1) * Empdata.numPerPage),
end = start + Empdata.numPerPage;
Empdata.employeePerPage = Empdata.employee.slice(begin, end);
});
}
and since I'm using controller as I didn't use the $scope, maybe that would be the case ?
Are there any opinions on using $scope vs controller as
and since we on this
what is recommendation
$scope or controller as

$watch is a method of $scope so that's your problem. Solution is to inject $scope so you can use the $watch function on your object. See this question and answer: Angularjs: 'controller as syntax' and $watch

Empdata is not an Angular object, it has no $watch method. You need to make it a property of the controller's $scope.
$scope.Empdata = {};
$scope.$watch('Empdata.currentPage + Empdata.numPerPage', function() { ... });

Whenever you want to use $watch when using controller as then you need explicitly bind this variable to scope using angular.bind
CODE
$scope.$watch(angular.bind(Empdata, function () {
return 'Empdata.currentPage + Empdata.numPerPage';
}), function (newVal, oldVal) {
// now we will pickup changes to newVal and oldVal
});
Thanks

Related

Using $watch in angularjs directive, why it works only with function as parameter

I researching angularjs application, and i don't understand why $watch in directive working as expected only with function in first parameter.
class PaginationController {
constructor($scope) {
"ngInject";
this.quantity = null;
this.$scope = $scope;
$scope.$watch(() => this.quantity, function (newValue, OldValue) {
console.log('new: ' + newValue);
console.log('old: ' + OldValue);
});
}
}
If i just use this.quantity as first parameter $watch don't catch changing. But with function all work as i expected. What the difference?
You can not pass variable to $watch as you will just pass variable value as for any JS function:
a = 5; console.log(a); is same to console.log(5)
And so this.quantity = 1; $scope.$watch(this.quantity) is same to $scope.$watch(1) and makes no sense.
What you can do is pass variable scope name: $scope.$watch('$ctrl.quantity', ... (if $ctrl is ref of your controller)
For more information, see
AngularJS scope/rootScope Type API Reference - $watch

In AngularJS, why isn't a $scope variable accessible from a simple function?

My code is as shown below:
angular.module('xyz', [])
.controller('listController', ['$scope',
function($scope) {
$scope.deliveryOptions = 0;
var vm = this;
function doTransaction() {
console.log('delivery options is ' + vm.$scope.deliveryOptions);
}
}
]);
Here inside console.log(delivery options), it gives me error that it is Unable to get property 'deliveryOptions' of undefined or null reference. So how can I access that variable?
The problem is that the $scope is not defined on the vm object.
To solve this you should either define everything on the $scope object like this
angular.module('xyz', [])
.controller('listController', ['$scope',
function($scope) {
$scope.deliveryOptions = 0;
$scope.doTransaction = function() {
console.log('delivery options is ' + $scope.deliveryOptions);
}
}
]);
Or on the vm object
angular.module('xyz', [])
.controller('listController',
function() {
var vm = this;
vm.deliveryOptions = 0;
vm.doTransaction = function() {
console.log('delivery options is ' + vm.deliveryOptions);
}
}
);
Mixing vm and $scope is not the best way to go
Use either $scope or controller as, not both
The primary issue is you're mixing original AngularJS 1.x $scope usage, with the controller as design pattern that uses vm (meaning "view model"), which started with Angular 1.5.
You need to pick one or the other, and then your code will work just fine.
If you go with the cleaner and less error-prone vm and controller as design pattern, you simply use vm rather than $scope:
angular.module('xyz', [])
.controller('listController', function() {
var vm = this
vm.deliveryOptions = 0
vm.doTransaction = function() {
console.log('delivery options is ' + vm.deliveryOptions)
}
})
And then in your view, you use vm.doTransaction() to call your function and display your variable.

Auto trigger function throuigh service in angularjs

hi all i am using angulrajs passing one value from one controller to another controller using service it's work fine but my need is when service value change in controller 2 i get the service value in one scope when scope value change i need trigger the function it's called refresh function when service value change and that i need to call the refresh function here my fiddle
https://jsfiddle.net/ctawL4t3/10/
You can just $watch your value.storeObject. Though it's not best of the practices, but it suits this kind of feature.
$scope.$watch('value.storedObject', function(newVal) {
if(newVal !== '') {
refresh()
}
})
working fiddle (open console to see refresh function logging)
You can try to use angular default $emit, $broadcast, or try to do 2 simple functions in own service
angular.module('app').factory('StoreService', function() {
var listeners = {};
var emit = function(name, val) {
if(listeners[name]) {
listeners[name](val)
}
}
var on = function(name, callback) {
listeners[name] = callback;
}
return {
emit: emit,
on: on,
storedObject: ''
};
});
JSFiddle example
JSFiddle example $watch
JSFiddle example ng-change is better because, you can use easily debounce
you can use broadcast function for that
Please check this SO link to find the related answer
How to call a function from another controller in angularjs?
app.controller('One', ['$scope', '$rootScope'
function($scope) {
$rootScope.$on("CallParentMethod", function(){
$scope.parentmethod();
});
$scope.parentmethod = function() {
// task
}
}
]);
app.controller('two', ['$scope', '$rootScope'
function($scope) {
$scope.childmethod = function() {
$rootScope.$emit("CallParentMethod", {});
}
}
]);

how to update all the variable changes in the service to the $scope.var in controller

I'm trying to update a variable in my controller every time a variable is updated in my service. I'm using the $scope.$watch(), but unfortunately only the last change is being effected. Here is the code that I used. Does anyone know what is wrong with this?
Service:
rApp.factory('pService', ['$http', '$rootScope', '$sanitize',
function ($http, $rootScope, $sanitize) {
var pService = {};
//Some other code
pService.Update=function(status)
{
if(status.LastItemId!=undefined)
{
pService.disItemId = status.LastItemId;
}
}
//Some other code
return pService;
});
Controller:
rApp.controller('dController', ['$scope','$rootScope' 'pService' ,dispenseController]);
function dController($scope,$rootScope, pService) {
$scope.$watch(function () { return pService.disItemId }, function (newVal, oldVal) {
if (newVal != oldVal) {
$scope.lastItemId = pService.disItemId;
}
})
});
In your specific case :
You don't need to use $watch. Actually you don't have any use of $watch in standard angular application.
Just do this in your controller :
$scope.lastItem = pService;
And then use the var like this :
$scope.lastItem.disItemId;
This will always point to the updated disItemId.
Problem
If you bind your var like this :
Service
[...]
service.serviceVar = 1;
return service
[...]
This will create a "1" var with a reference.
Controller
[...]
$scope.myvar = Service.serviceVar;
[...]
This will bind $scope.myvar to the "1" reference.
If you do this in your service or in an other controller :
service.serviceVar = 2;
You will create a new var "2" with a new reference and you will assign this reference to service.serviceVar. Badly all your old references to the old 1 var will not update.
Solution
To avoid that do it like this :
Service
[...]
service.servicevar = {};
service.servicevar.value = 1;
return service
[...]
You create an object with a new reference and assign it to servicevar. You create a var "1" and assign it to servicevar.value.
Controller
[...]
$scope.myvar = Service.servicevar;
[...]
You assign the servicevar reference to your scope var.
view
{{myvar.value}}
You can use the value by using the property of your var.
Updating the var doing this :
service.servicevar.value = 2;
You will create a new var "2" with a new reference and replace the old reference by this one.
BUT this time you keep all your references to servicevar in your controllers.
I hope i was clear and it solve you issue.
EDIT
My answer was only a partial answer on this question.
Here is the update plunker
First you had a type on the controller definition. You're closing the braces too early. Here is the good definition
app.controller('MainCtrl',['$scope','pService',
function MainCtrl($scope,pService) {
$scope.serviceVar=pService.myVar;
$scope.val = $scope.serviceVar;
}]);
Then you were using windows.interval in angular you need to use the $interval service instead
Example :
app.factory('pService',function($interval){
var pService={};
pService.myVar={};
pService.myVar.count=1;
$interval(function(){
pService.myVar.count++;
},1000);
return pService;
});
Then my problem was occuring. You were binding the value instead of the object in your controller. It looks like this now :
$scope.val = $scope.serviceVar;
Hope it helped you

AngularJS access scope from outside js function

I'm trying to see if there's a simple way to access the internal scope of a controller through an external javascript function (completely irrelevant to the target controller)
I've seen on a couple of other questions here that
angular.element("#scope").scope();
would retrieve the scope from a DOM element, but my attempts are currently yielding no proper results.
Here's the jsfiddle: http://jsfiddle.net/sXkjc/5/
I'm currently going through a transition from plain JS to Angular. The main reason I'm trying to achieve this is to keep my original library code intact as much as possible; saving the need for me to add each function to the controller.
Any ideas on how I could go about achieving this? Comments on the above fiddle are also welcome.
You need to use $scope.$apply() if you want to make any changes to a scope value from outside the control of angularjs like a jquery/javascript event handler.
function change() {
alert("a");
var scope = angular.element($("#outer")).scope();
scope.$apply(function(){
scope.msg = 'Superhero';
})
}
Demo: Fiddle
It's been a while since I posted this question, but considering the views this still seems to get, here's another solution I've come upon during these last few months:
$scope.safeApply = function( fn ) {
var phase = this.$root.$$phase;
if(phase == '$apply' || phase == '$digest') {
if(fn) {
fn();
}
} else {
this.$apply(fn);
}
};
The above code basically creates a function called safeApply that calles the $apply function (as stated in Arun's answer) if and only Angular currently isn't going through the $digest stage. On the other hand, if Angular is currently digesting things, it will just execute the function as it is, since that will be enough to signal to Angular to make the changes.
Numerous errors occur when trying to use the $apply function while AngularJs is currently in its $digest stage. The safeApply code above is a safe wrapper to prevent such errors.
(note: I personally like to chuck in safeApply as a function of $rootScope for convenience purposes)
Example:
function change() {
alert("a");
var scope = angular.element($("#outer")).scope();
scope.safeApply(function(){
scope.msg = 'Superhero';
})
}
Demo: http://jsfiddle.net/sXkjc/227/
Another way to do that is:
var extScope;
var app = angular.module('myApp', []);
app.controller('myController',function($scope, $http){
extScope = $scope;
})
//below you do what you want to do with $scope as extScope
extScope.$apply(function(){
extScope.test = 'Hello world';
})
we can call it after loaded
http://jsfiddle.net/gentletech/s3qtv/3/
<div id="wrap" ng-controller="Ctrl">
{{message}}<br>
{{info}}
</div>
<a onClick="hi()">click me </a>
function Ctrl($scope) {
$scope.message = "hi robi";
$scope.updateMessage = function(_s){
$scope.message = _s;
};
}
function hi(){
var scope = angular.element(document.getElementById("wrap")).scope();
scope.$apply(function() {
scope.info = "nami";
scope.updateMessage("i am new fans like nami");
});
}
It's been a long time since I asked this question, but here's an answer that doesn't require jquery:
function change() {
var scope = angular.element(document.querySelector('#outside')).scope();
scope.$apply(function(){
scope.msg = 'Superhero';
})
}
Here's a reusable solution: http://jsfiddle.net/flobar/r28b0gmq/
function accessScope(node, func) {
var scope = angular.element(document.querySelector(node)).scope();
scope.$apply(func);
}
window.onload = function () {
accessScope('#outer', function (scope) {
// change any property inside the scope
scope.name = 'John';
scope.sname = 'Doe';
scope.msg = 'Superhero';
});
};
You can also try:
function change() {
var scope = angular.element( document.getElementById('outer') ).scope();
scope.$apply(function(){
scope.msg = 'Superhero';
})
}
The accepted answer is great. I wanted to look at what happens to the Angular scope in the context of ng-repeat. The thing is, Angular will create a sub-scope for each repeated item. When calling into a method defined on the original $scope, that retains its original value (due to javascript closure). However, the this refers the calling scope/object. This works out well, so long as you're clear on when $scope and this are the same and when they are different. hth
Here is a fiddle that illustrates the difference: https://jsfiddle.net/creitzel/oxsxjcyc/
I'm newbie, so sorry if is a bad practice. Based on the chosen answer, I did this function:
function x_apply(selector, variable, value) {
var scope = angular.element( $(selector) ).scope();
scope.$apply(function(){
scope[variable] = value;
});
}
I'm using it this way:
x_apply('#fileuploader', 'thereisfiles', true);
By the way, sorry for my english
<input type="text" class="form-control timepicker2" ng-model='programRow.StationAuxiliaryTime.ST88' />
accessing scope value
assume that programRow.StationAuxiliaryTime is an array of object
$('.timepicker2').on('click', function ()
{
var currentElement = $(this);
var scopeValues = angular.element(currentElement).scope();
var model = currentElement.attr('ng-model');
var stationNumber = model.split('.')[2];
var val = '';
if (model.indexOf("StationWaterTime") > 0) {
val = scopeValues.programRow.StationWaterTime[stationNumber];
}
else {
val = scopeValues.programRow.StationAuxiliaryTime[stationNumber];
}
currentElement.timepicker('setTime', val);
});
We need to use Angular Js built in function $apply to acsess scope variables or functions outside the controller function.
This can be done in two ways :
|*| Method 1 : Using Id :
<div id="nameNgsDivUid" ng-app="">
<a onclick="actNgsFnc()"> Activate Angular Scope</a><br><br>
{{ nameNgsVar }}
</div>
<script type="text/javascript">
var nameNgsDivVar = document.getElementById('nameNgsDivUid')
function actNgsFnc()
{
var scopeNgsVar = angular.element(nameNgsDivVar).scope();
scopeNgsVar.$apply(function()
{
scopeNgsVar.nameNgsVar = "Tst Txt";
})
}
</script>
|*| Method 2 : Using init of ng-controller :
<div ng-app="nameNgsApp" ng-controller="nameNgsCtl">
<a onclick="actNgsFnc()"> Activate Angular Scope</a><br><br>
{{ nameNgsVar }}
</div>
<script type="text/javascript">
var scopeNgsVar;
var nameNgsAppVar=angular.module("nameNgsApp",[])
nameNgsAppVar.controller("nameNgsCtl",function($scope)
{
scopeNgsVar=$scope;
})
function actNgsFnc()
{
scopeNgsVar.$apply(function()
{
scopeNgsVar.nameNgsVar = "Tst Txt";
})
}
</script>
This is how I did for my CRUDManager class initialized in Angular controller, which later passed over to jQuery button-click event defined outside the controller:
In Angular Controller:
// Note that I can even pass over the $scope to my CRUDManager's constructor.
var crudManager = new CRUDManager($scope, contextData, opMode);
crudManager.initialize()
.then(() => {
crudManager.dataBind();
$scope.crudManager = crudManager;
$scope.$apply();
})
.catch(error => {
alert(error);
});
In jQuery Save button click event outside the controller:
$(document).on("click", "#ElementWithNgControllerDefined #btnSave", function () {
var ngScope = angular.element($("#ElementWithNgControllerDefined")).scope();
var crudManager = ngScope.crudManager;
crudManager.saveData()
.then(finalData => {
alert("Successfully saved!");
})
.catch(error => {
alert("Failed to save.");
});
});
This is particularly important and useful when your jQuery events need to be placed OUTSIDE OF CONTROLLER in order to prevent it from firing twice.

Resources