AngularJS: code not working when iterating through object [duplicate] - angularjs

This question already has answers here:
'this' vs $scope in AngularJS controllers
(7 answers)
Closed 6 years ago.
I am trying to populate a list of employee objects from my controller empctrl in a template.
Here's the controller:
app.controller('employeeController', function ($scope, employeeService) {
this.employees = {};
this.populateTable = function (data) {
this.employees = data;
};
var error = function (err) {
console.log("Error: " + err);
};
// Call Service to List all Employees
console.log("Service called to populate table.");
employeeService.output().then(this.populateTable, error);
this.populateTable();
});
However, this code that I wrote isn't working:
<div ng-repeat="employee in empctrl.employees.allEmployees" class="table_row">
<div class="table_column" style="width:2%">{{ $index + 1 }}</div>
<div class="table_column" style="width:8%">{{ employee.employeeName}}</div>
<!-- 7 more columns -->
</div>
Nothing shows up in the UI.
Instead, if I write $scope.employees in the controller, it works:
<div ng-repeat="employee in employees.allEmployees" class="table_row">
Since I know how tempting it is to do $scope.<everything> in the controller, I'm trying to avoid using $scope as much as possible.
If someone could demonstrate the proper use of $scope and difference betwee alias.abc and $scope.abc (where alias is an alias of controller), I'll be thankful.
Edit: Exact same question is this: 'this' vs $scope in AngularJS controllers
Thanks for this link, PankajParkar.

The problem is this which you are accessing inside populateTable function is not this which you have there in your controller function.
Better do keep this variable inside some variable, so that by having it you will make sure you are referring to correct object.
Controller
app.controller('employeeController', function ($scope, employeeService) {
var vm = this;
vm.employees = {};
vm.populateTable = function (data) {
vm.employees = data;
};
var error = function (err) {
console.log("Error: " + err);
};
// Call Service to List all Employees
console.log("Service called to populate table.");
employeeService.output().then(vm.populateTable, error);
vm.populateTable();
});
For more detail, I'd highly recommend you to readup on this article
If you are confused with this vs scope then do read up on this answer

Add your variables to the $scope instead of this like:
$scope.customers = {};
$scope.populateTable = function (data) {
$scope.employees = data;
};
Edit: both methods work. See this article for a in depth explanation.

Substituting "this" to vm (View-Model) will solve your issue. Not polluting $scope object is a groovy thing. this is a global context and its value depends on a function call.
So, in your controller just assign,
var vm = this;
vm.empTable = function (data) {
vm.employeeList = data.data;
};
..and use the vm object elsewhere in your controller. It will be useful to keep the code clean while working with multiple controllers in a view.
Don't forget to give an alias name to the controller,
<div ng-controller="MainCtrl as main">
<div ng-repeat=" employee in main.vm.employeeList ">
{{employee.name}}
</div>
</div>

Related

AngularJS: Why people prefer factory to share data between controllers [duplicate]

This question already has answers here:
AngularJS: Service vs provider vs factory
(30 answers)
Closed 5 years ago.
i am new in angular. so trying to know how to share data between two controller and search google. i visited few pages and found most of the time people use factory to share data. i just like to know can't we do it by service instead of factory ?
1st example
<div ng-controller="FirstCtrl">
<input type="text" ng-model="data.firstName">
<br>Input is : <strong>{{data.firstName}}</strong>
</div>
<hr>
<div ng-controller="SecondCtrl">
Input should also be here: {{data.firstName}}
</div>
myApp.factory('MyService', function(){
return {
data: {
firstName: '',
lastName: ''
},
update: function(first, last) {
// Improve this method as needed
this.data.firstName = first;
this.data.lastName = last;
}
};
});
// Your controller can use the service's update method
myApp.controller('SecondCtrl', function($scope, MyService){
$scope.data = MyService.data;
$scope.updateData = function(first, last) {
MyService.update(first, last);
}
});
2nd example
var myApp = angular.module('myApp', []);
myApp.factory('Data', function(){
var service = {
FirstName: '',
setFirstName: function(name) {
// this is the trick to sync the data
// so no need for a $watch function
// call this from anywhere when you need to update FirstName
angular.copy(name, service.FirstName);
}
};
return service;
});
// Step 1 Controller
myApp.controller('FirstCtrl', function( $scope, Data ){
});
// Step 2 Controller
myApp.controller('SecondCtrl', function( $scope, Data ){
$scope.FirstName = Data.FirstName;
});
examples are taken from this url Share data between AngularJS controllers
please guide me.
Both .service() and .factory() are both singletons as you’ll only get one instance of each Service regardless of what API created it.
Remember that .service() is just a Constructor, it’s called with new, whereas .factory() is just a function that returns a value.
Using .factory() gives us much more power and flexibility, whereas a .service() is essentially the “end result” of a .factory() call. The .service() gives us the returned value by calling new on the function, which can be limiting, whereas a .factory() is one-step before this compile process as we get to choose which pattern to implement and return.

How to retain the variable's value of JSP using AngularJS

I am new to AngularJS. I am trying learn to pass the value of a variable to the angularJS.
But for some unknown reason, I am not being able to do.
Below is the .jsp page which I think is correct:
<body >
<div ng-controller="customersController">
<label>Value</label>
<input type="text" ng-model=something></input>
<button ng-click="punchIt()">click me!</button>
<br>Obtained value : {{ value }}
</div>
</body>
In the respective .js page, value passed from .jsp file is not getting retained.The first alert function should return assumed : something's value,but it is returning assumed : undefined.
Below is the .js file:
var myApp = angular.module("getInfo", []);
myApp.controller("customersController", function($scope, $http){
$scope.punchIt = function ($scope, $http) {
var data = {Value: $scope.something};
alert("assumed : "+ $scope.something);
$http.post("http://localhost:8082/HelloWorldWS/services/HelloWorldImpl",data)
.success(function (data, status, header) {
alert("in success " + data);
$scope.value = data;
}).error(function (data) {
alert("in error method " + status);
$scope.value = "error "+ data;
});
};
});
Please suggest some way out.
Thanks.
You are expecting two variables ($scope and $http) in your punchIt() function declaration but not passing these when you call it on button click. Thus inside your punchit() function, both $scope and $http variables would be initialized to nothing (read undefined).
You actually dont need to pass these parameters to your function. Your controller already has these services injected in it via your controller declaration.
Also declare/initialize the name variable in your controller. Else, if you do not enter anything in the input field and try to access it in your $scope, you will retrive it as undefined.
Your code changes would look as below:
myApp.controller("customersController", function($scope, $http){
$scope.name=null;
//OR $scope.name='';
$scope.punchIt = function () {
...
}
}

Can't get this.var to bind correct data in AngularJs

Two Important Notes:
1. My goal is to AVOID using $scope in this case since it's my understanding that impedes the new "controller as" syntax.
2. My problem is likely a variable scope issue and so perhaps just clarifying the proper JS way might solve the problem.
Nevermind the exports, I'm working with browserify in my workflow.
I have this working code:
exports.IntroCtrl = function($scope, $http) {
$scope.introData = [];
$http.get('data/intro.json')
.success(function(res){
$scope.introData = res;
});
};
That ideally I'd like to work as something like this, for the sake of using the "controller as" syntax.
exports.IntroCtrl = function($http) {
this.introData = [];
$http.get('data/intro.json')
.success(function(res){
introData = res;
});
};
The problem is that the $http service seems to be executing before my initial this.introData declaration since I get a variable not defined error.
If tell this.introData = $http.get… then it returns an array of 5 objects that I can't access and intro.json only contains 4.
Thanks for any guidance/help.
First of all create a service for the http call. It is very convenient way to get the callback in the controller and then assign your controller as variables. Here is the factory for you:
Factory
app.factory('getDataService',function ($http) {
return {
getData:function(callback){
$http.get('data/intro.json')
.success(callback)
});
}
}
});
In your controller you get inject the getDataService and bind the data like this:
Controller:
app.controller('testController',['getDataService',function(testDataService){
this.introData = [];
testDataService.getData(function(data){
this.introData = data;
});
}]);
Here you need to bind the introData of the controller function.
You have to remember this variable that reference the controller instance, and use it later in the success callback like this:
exports.IntroCtrl = function($http) {
this.introData = [];
var ctrl = this; // remember 'this', the controller instance, to use in the success callback below
$http.get('data/intro.json')
.success(function (res) {
ctrl.introData = res;
});
};
Hope this helps.

Angularjs service to manage dataset across multiple controllers

Basically the core of my app centers around a set of data retrieved from the server via a $http request. Once the data is available to the client (as an array of objects) I require it for multiple views and would like to maintain it's state between them, for example, if it has been filtered I would like only the filtered data to be available in the other views.
Currently I have a basic service retrieving the data and am then managing the state of the data (array) in an app-wide controller (see below). This works Ok but it is beginning to become a mess as I try to maintain the array length, filtered status, visible / hidden objects across controllers for each view as I have to keep a track of currentVenue etc in the app-wide controller. Note: I am using ng-repeat in each view to show and filter the data (another reason I would like to just have it filtered in a central spot).
Obviously this is not optimal. I assume I should be using a service to maintain the array of venue objects, so it would contain the current venue, current page, be responsible for filtering the array etc. and just inject it into each controller. My question is, how can I set up a service to have this functionality (including loading the data from the server on start; this would be a good start tbh) such that I can achieve this an then bind the results to the scope. ie: something $scope.venues = venues.getVenues and $scope.current = venues.currentVenue in each views controller.
services.factory('venues', function ($http, $q) {
var getVenues = function() {
var delay = $q.defer();
$http.get('/api/venues', {
cache: true
}).success(function (venues) {
delay.resolve(venues);
});
return delay.promise;
}
return {
getVenues: getVenues
}
});
controllers.controller('AppCtrl', function (venues, $scope) {
$scope.venuesPerPage = 3;
venues.getVenues().then(function (venues) {
$scope.venues = venues;
$scope.numVenues = $scope.venues.length;
$scope.currentPage = 0;
$scope.currentVenue = 0;
$scope.numPages = Math.ceil($scope.numVenues / $scope.venuesPerPage) - 1;
}
});
Sorry for the long wording, not sure how to specify it exactly. Thanks in advance.
The tactic is to take advantage of object references. If you move your shared data to an object, then set that object to $scope, any change on $scope is directly changing the service object since they are the same thing ($scope is referencing the service).
Here's a live sample demonstrating this technique (click).
<div ng-controller="controller-one">
<h3>Controller One</h3>
<input type="text" ng-model="serv.foo">
<input type="text" ng-model="serv.bar">
</div>
<div ng-controller="controller-two">
<h3>Controller Two</h3>
<input type="text" ng-model="serv.foo">
<input type="text" ng-model="serv.bar">
</div>
js:
var app = angular.module('myApp', []);
app.factory('myService', function() {
var myService = {
foo: 'abc',
bar: '123'
};
return myService;
});
app.controller('controller-one', function($scope, myService) {
$scope.serv = myService;
});
app.controller('controller-two', function($scope, myService) {
$scope.serv = myService;
});
I threw this together quickly as a starting point. You can restructure factory any way you want. The general idea is all data in scope has now been moved to an object in factory service.
Instead of resolving the $http with just the response array, resolve it with a much bigger object that includes the array from server. Since all data is now in an object it can be updated from any controller
services.factory('venues', function ($http, $q) {
var getVenues = function(callback) {
var delay = $q.defer();
$http.get('/api/venues', {
cache: true
}).then(function (response) {
/* update data object*/
venueData.venues=response.data;
venueData.processVenueData();
/* resolve with data object*/
delay.resolve(venueData);
}).then(callback);
return delay.promise;
}
var processVenueData=function(){
/* do some data manipulation here*/
venueData.updateNumPages();
}
var venueData={
venuesPerPage : 3,
numVenues:null,
currentVenue:0,
numPages:null,
venues:[],
updateNumPages:function(){
venueData.numPages = Math.ceil(venueData.numVenues / venueData.venuesPerPage) - 1;
},
/* create some common methods used by all controllers*/
addVenue: function( newVenue){
venueData.venues.push( newVenue)
}
}
return {
getVenues: getVenues
}
});
controllers.controller('AppCtrl', function (venues, $scope) {
venues.getVenues(function (venueData) {
/* now have much bigger object instead of multiple variables in each controller*/
$scope.venueData=venueData;
})
});
Now in markup reference venueData.venues or venueData.numPages
By sharing methods across controllers you can now simply bind a form object with ng-model's to a button that has ng-click="venueData.addVenue( formModel)" (or use ng-submit) and you can add a new venue from any controller/directive without adding a bit of code to the controller

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