How to keep track of Array index in angularJS? - javascript

Very new to Angularjs. So I have some JSON files that I am reading into my webpage that contain and array of objects that are cars. What I am trying to do is have my "button" when pressed alert me to the data specific to that button.
The ng-repeat is running 8 times so that is the length of the array, but in angularJs i'm not sure how to basically store the array index for each time the ng-repeat passes in my button function.
This is my a snippet of my .html:
<div class="carTable table-responsive text-center" ng-controller="ButtonController" >
<table class="table specTable">
<thead>
<tr>
<th>Make</th>
<th>Model</th>
<th>Year</th>
<th>Color</th>
<th>Milage</th>
<th>Doors</th>
<th class="reserve">Horsepower</th>
<th>Price</th>
<th class="reserve"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="cars in car | orderBy:'year'">
<td>{{cars.year}}</td>
<td>{{cars.model}}</td>
<td>{{cars.make}}</td>
<td>{{cars.color}}</td>
<td>{{cars.mileage | number}}</td>
<td>{{cars.doors}}</td>
<td>{{cars.horsepower}}</td>
<td>{{cars.price | number}}</td>
<td><div class="panel panel-default">Reserve</div></td>
</tr>
</tbody>
</table>
</div>
The portion in question is at the bottom where I have a Reserve "button"
I'm leaving out my JSON files, works properly there. I'm just not sure how to keep track of the array index as the ng-repeat does its thing.
Here is the angular:
(function(){
var app = angular.module("myReserveApp", []);
app.controller("ButtonController", ["$scope", "$window", function($scope, $window){
$scope.buttonPress = function(){
$window.alert(JSON.stringify($scope.car[0]));
}
}]);
var MainController = function($scope, $http, $window){
var onGatherBoatData = function(response){
$scope.boat = response.data;
};
var onError = function(reason){
$scope.error = "Could not fetch Boat Data";
};
var onGatherCarData = function(response){
$scope.car = response.data;
};
var onError = function(reason){
$scope.error = "Could not fetch Car Data";
};
var onGatherTruckData = function(response){
$scope.truck = response.data;
};
var onError = function(reason){
$scope.error = "Could not fetch Truck Data";
};
$scope.message = "Hello, Angular Here!";
};
app.controller("MainController", ["$scope", "$http", "$window", MainController]);
}());
Currently in the top portion of the code I just have it alerting object[0] but I want it to be specific to which button is pressed. Any help is appreciated, thank you.

$index refers to the index in ng-repeat. So if you want to pass your function the index in array on the button click, change buttonPress() to buttonPress($index)
you'll have to change your controller to something like the following:
$scope.buttonPress = function(index){
$window.alert(JSON.stringify($scope.car[index]));
}

To do the following, you can just pass the current data in the ngRepeat. Moreover,if you want the current index, the ngRepeat directive provide specials properties, as the $index, which is an iterator.
$scope.buttonPress = function(car, index){
//Retrieve current data of the ngRepeat loop
console.log(car);
//Current index of your data into the array
console.log(index);
}
Then you can call your function like this :
Reserve

First, thank you both for the quick responses. Both of these answers work. I found another way to do it as well before reading your posts.
<div class="panel panel-default">
Reserve:{{car.indexOf(cars)}}
</div>
Using (car.indexOf(cars)) gives me the same result
$scope.buttonPress = function(index){
$window.alert(JSON.stringify(index));
}
Now when I click on the "button" it sends me back the array index, so now I should be able to play with that data. Thank you again both, for your help.

Related

AngularJS: weird behavior with the 2-way binding (ng-repeat)

I'm using AngularJS in a Firebase app and I have a function where I do some inner join to get some data. More details here. After getting the response from the firebase api I create an object and push it into an array (a scope variable). I see in the debug that the data has been retrieved and that the $scope variable is filled correctly. The problem is that it is not showing in the ng-repeat.
My function:
$scope.getMessagesByRegion = function(regionId){
console.log('Function start');
var rootRef = firebase.database().ref();
var regionMessagesRef = rootRef.child("region_messages/"+ regionId);
$scope.messages_by_region = []; // Here I reset the scope variable
regionMessagesRef.on('child_added', function(rmSnap) {
var messageRef = rootRef.child("messages/"+rmSnap.key);
messageRef.once('value').then(function(msgSnap){
var msg = {
key : msgSnap.key,
name : msgSnap.val().name,
type : $scope.getTypeName(msgSnap.val().type),
show_only_once : rmSnap.val().show_only_once,
pre_requisite_message : rmSnap.val().pre_requisite_message
}
console.log(msg); // here I see the object in the console. it is OK
$scope.messages_by_region.push(msg); // pushing the item
console.log('----------------');
console.log($scope.messages_by_region);
})
});
}
My HTML:
<table class="table">
<thead>
<tr>
<th>Message name</th>
<th>Type</th>
<th>Show only once</th>
<th>Pre requisite</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="msg in messages_by_region">
<td ng-bind="msg.name"></td>
<td ng-bind="msg.type"></td>
<td ng-bind="msg.show_only_once"></td>
<td ng-bind="msg.pre_requisite_message"></td>
</tr>
</tbody>
</table>
This is what I see in the console:
The problem is that even having an object in the array it is not shown in the view. It is like there was an empty array set to the $scope.messages_by_region variable
I'm having a hard time figuring out what I'm doing wrong. Can you see what's wrong with my function?
Thanks for any help.
try,
$scope.$apply(function(){
$scope.messages_by_region.push(msg);
});
or,
$scope.messages_by_region.push(msg);
$scope.$apply();
Since you're using async functions (Cosuming of firebase API) you should tell angular to refresh the HTML;
Use
$scope.$diggest()
More information you can find on https://www.sitepoint.com/understanding-angulars-apply-digest/
As you are performing Async calls you need to tell angular to refresh the changes in the value with $apply call you can do it with:
$scope.getMessagesByRegion = function(regionId) {
console.log('Function start');
var rootRef = firebase.database().ref();
var regionMessagesRef = rootRef.child("region_messages/" + regionId);
$scope.messages_by_region = []; // Here I reset the scope variable
regionMessagesRef.on('child_added', function(rmSnap) {
var messageRef = rootRef.child("messages/" + rmSnap.key);
messageRef.once('value').then(function(msgSnap) {
var msg = {
key: msgSnap.key,
name: msgSnap.val().name,
type: $scope.getTypeName(msgSnap.val().type),
show_only_once: rmSnap.val().show_only_once,
pre_requisite_message: rmSnap.val().pre_requisite_message
}
$scope.$apply(function() {
console.log(msg); // here I see the object in the console. it is OK
$scope.messages_by_region.push(msg); // pushing the item
console.log('----------------');
console.log($scope.messages_by_region);
});
});
});
}
For more information on this behavior you can also read article describing the problem here

Execute method in one of ng-repeat child

I'm using angular's ng-repeat to populate table with data from GitHub api and I want one of the <td> to execute method that will return data. the problem is that this makes the function execute infinitely.
HTML:
<table>
<thead>
<tr>
<th>Name</th>
<th>Languages</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="repo in repos">
<td>{{repo.name}}</td>
<td></td> <!-- Get languages for each repo here. -->
</tr>
</tbody>
</table>
Angular:
(function(){
var app = angular.module("githubViewer", []);
var MainController = function($scope, $http){
var onUserComplete = function(response){
$scope.user = response.data;
$http.get($scope.user.repos_url)
.then(onRepoComplete, onError);
};
var onRepoComplete = function(response){
$scope.repos = response.data;
};
var onError = function(reson){
$scope.error = "Could not fetch the data";
};
//search for user
$scope.search = function(username){
$http.get("https://api.github.com/users/" + username)
.then(onUserComplete, onError);
};
// These two execute infinately if executed in the ng-repeat's <td>
$scope.findLangs = function(langsUrl){
$http.get(langsUrl)
.then(onLangComplete, onError);
}
var onLangComplete = function(response){
return response.data;
};
};
app.controller("MainController", ["$scope", "$http", MainController]);
})();
I've tried using {{ findLangs(repo.languages_url) }} in the <td> but it causes me to get this error:
Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
and appears to be infinitely executing.
I don't think you can use an expression there because you don't actually return a value from the findLangs() function. That said, I don't think that would cause an infinite loop. I think you'll need to actually grab the language data for each repo within the onRepoComplete callback, then just use that data in your template:
var onRepoComplete = function(response){
$scope.repos = response.data;
$scope.repos.forEach(function(repo) {
$http.get(repo.languages_url)
.then(function(response) {
// change this to process the language data however you need to...
repo.languages = JSON.stringify(response.data);
},onError);
});
};
Then in your template you can use the languages property of the repo:
<tr ng-repeat="repo in repos">
<td>{{repo.name}}</td>
<td>{{repo.languages}}</td>
</tr>

AngularJS: ng-hide not working

I am trying to hide a column in my table using ng-hide. Before the user logins the column should not been shown to the user. After they login the hidden column should be shown. But now after i used the ng-hide property the whole table is hidden if the user isnt login into the system. Can i know how to solve this problem.
This is my partial html code:
<table class="table table-hover table-bordered">
<thead>
<tr>
<th>Title</th>
<th>Description</th>
<th ng-show="noteEnabled">Note</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="movie in movies | pagination: currentPage * entryLimit | limitTo: entryLimit" data-ng-class="{'selected':selectedFilm.film_id===movie.film_id}" >
<td>
{{movie.title}}
</td>
<td>
{{movie.description}}
</td>
<td data-ng-click="selectFilmDetails($event,movie)" ng-show="movie.noteEnabled" >
{{movie.comment}}
</td>
</tr>
</tbody>
</table>
This is my controller.js code:
.controller('AllMovieController',
[
'$scope',
'dataService',
'$location',
function ($scope, dataService, $location){
$scope.noteEnabled = false;
$scope.movies = [ ];
$scope.movieCount = 0;
$scope.currentPage = 0; //current page
$scope.entryLimit = 20; //max no of items to display in a page
var getAllMovie = function () {
dataService.getAllMovie().then(
function (response) {
var userName=dataService.getSessionService('user');
$scope.movieCount = response.rowCount + ' movies';
if(userName){
$scope.movies = response.data;
$scope.userLogin = dataService.getSessionService('user');
$scope.userLoginEmail = dataService.getSessionService('userEmail');
$scope.showSuccessMessage = true;
$scope.successMessage = "All movie Success";
$scope.noteEnabled = true;
}
},
function (err){
$scope.status = 'Unable to load data ' + err;
}
); // end of getStudents().then
};
$scope.numberOfPages = function(){
return Math.ceil($scope.movies.length / $scope.entryLimit);
};
//------------------
$scope.selectFilmDetails = {};
$scope.selectFilmDetails = function ($event,movie) {
$scope.selectFilmDetails = movie;
$location.path('/filmDetails/' + movie.film_id);
}
getAllMovie();
}
]
)
At first i set the noteEnabled to false and check with the session if the user is logged in then the noteEnabled will become true. Thanks in advance.
Use ng-hide="$parent.noteEnabled" instead of ng-hide="noteEnabled".
To access $scope variable from the loop (ng-repeat) use $parent
Here is a good example of how to use ng-show and ng-hide and here is the official documentation as well . Hope it helps !
In your case you have use ng-show/ ng-hide only to tag in of the column you want to show/hide. So on any scenario whole table will not be hide unless there is no data so you may is it as hidden.
Anyway as per you code, seems you have misused ng-show/hide. On controller initially you set noteEnabled to false and after you check the logging you set noteEnabled to true. as you have used ng-show/hide as follows
<td data-ng-click="selectFilmDetails($event,movie)" ng-hide="noteEnabled" >
{{movie.comment}}
</td>
the result will be; initially column will be shown and after your dataService receive userName it will hide the column. The opposite of what you want!!!. So change the directive you use from ng-hide to ng-show or change the value set to noteEnabled.
The real cause behind problem is, ng-repeat does create child scope which is prototypically inherited from parent scope, while rendering each iteration element where ng-repeat directive has placed.
And the you have used noteEnabled variable as primitive datatype, so when you use noteEnabled variable inside a ng-repeat div it does gets added inside that ng-repeat div scope.
noteEnabled property to be maintained on each element level of movies collection. Then do toggle, whenever you want.
ng-show="movie.noteEnabled"
By default it will be hidden & toggle it whenever you want to show it.
Even better approach is to follow controllerAs pattern where you don't need to care about prototypal inheritance. While dealing with such a variable access thing on UI.
I solved the problem by myself. Here is the solution for it.
$scope.noteEnabled = false;
$scope.movies = [ ];
$scope.movieCount = 0;
$scope.currentPage = 0; //current page
var getAllMovie = function () {
dataService.getAllMovie().then(
function (response) {
var userName=dataService.getSessionService('user');
$scope.movieCount = response.rowCount + ' movies';
if(userName){
$scope.movies = response.data;
$scope.userLogin = dataService.getSessionService('user');
$scope.userLoginEmail = dataService.getSessionService('userEmail');
$scope.showSuccessMessage = true;
$scope.successMessage = "All movie Success";
$scope.noteEnabled = true;
}else{
$scope.movies = response.data;
$scope.noteEnabled = false;
}

Set values after creating a JSON object Angular.js

I am having some trouble setting some values for a widget I am making. I am using Ozone widget framework, but that part is negligible to this discussion. Here us the html where I am trying to set the variable (for now just focus on {{user.user}} part.
<div class="col-lg-12">
<p>User: {{user.user}}</p>
<table class="table table-bordered table-hover table-striped">
<thead>
<th>#</th>
<th>Model</th>
<th>Score</th>
<th>Date</th>
</thead>
<tr data-ng-repeat=" item in records | orderBy : '-score' | limitTo : 10 " ng-click="">
<td>{{$index+1}}</td>
<td>{{item.model}}</td>
<td>{{item.score}}</td>
<td>{{item.date}}</td>
</tr>
</table>
</div>
And here is the Angular / owf to go with it:
angular.module('myapp', ['cgOwf'])
.controller('MainCtrl', function($scope, $http, owf) {
var records;
$scope.selectPost = '';
$scope.searchText = '';
console.debug("before IT HERE!!!");
owf.ready(function(){
console.debug("MADE IT HERE!!!");
owf.Eventing.subscribe('user-status', function(sender, msg, channel) {
console.debug('[%s] - received message %o', channel, msg);
$scope.user = msg;
});
});
$scope.search = function() {
//clear the select, go here http://jsonplaceholder.typicode.com/comments
//and display/filter emails based on the search input
$scope.selectPost = "";
$scope.selectedItem = null;
$http.get('https://api.myjson.com/bins/1jvst').success(function(data2) {
$scope.records = [];
data2.forEach(function(r) {
if (r && r.user && r.user.toLowerCase().indexOf($scope.searchText.toLowerCase()) !== -1) {
$scope.records.push(r);
}
});
});
};
});
The part I am having trouble with is $scope.user = msg;. At that point in the code, msg is a JSON object, and I am sure of that because it checks out in the js debugger in chrome. AFAIK that is how I would set the object so I could access it in the html, though something clearly doesn't work.
The owf event probably isn't triggering a $digest cycle, so the view never updates. You can run $scope.apply() to force a $digest
owf.Eventing.subscribe('user-status', function(sender, msg, channel) {
console.debug('[%s] - received message %o', channel, msg);
$scope.$apply(function() {
$scope.user = msg;
});
});

Retrieving the id from Model to Controller in Angular JS

i have a table which is having the data retrieved from an api call from my memberController which is displayed inside ng-repeat and its working fine.
I need each Business Name of the member list to link to a separate page(edit_form.html) and display the id value, so that i can pass this along with the api call to get only this particular member detail. So i have added ng-init in my edit form page which calls the function test_funct when the page loads and retrieve each persons id there. unfortunately i am unable to retrieve the id value inside the function.
HTML Template
<div class="page" data-ng-controller="memberController">
<table>
<thead >
<tr>
<th>Business Name</th>
<th>Contact Name</th>
<th>Trade Balance</th>
<th>Cash Balance</th>
<th>Telephone</th>
<th>Account Number </th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="member in details | filter:search">
<td>{{member.businessname}}</td>
<td>{{member.person}}</td>
<td>{{member.balance_trade}}</td>
<td>{{member.balance_cash}}</td>
<td>{{member.telephone}}</td>
<td>{{member.accountnumber}}</td>
</tr>
</tbody>
</table>
</div>
I have the following controller
function memberController($scope, $http, $cookieStore) {
var token = $cookieStore.get('token');
var conId = $cookieStore.get('Cont_Id');
var exId = $cookieStore.get('ex_Id');
var member_list = "http://www.vb.com/functions/member_list.html?exchangeid=" + exId +
"&contactid=" + conId + "&token=" + token;
$http.get(member_list)
.success(function(response) {
$scope.details = response;
});
$scope.test_funct = function(id) {
$scope.myid = id;
alert($scope.myid); // getting undefined in alert, i expect the id(eg:1123)
}
}
edit_form.html
<div class="page" data-ng-controller="memberController">
<div class="panel-body" ng-init="test_funct()"></div>
</div>
Please assist me on this. Thanks in advance.
There are 2 things going on here.
First, you should separate controllers for the different views, so you end up with something like this:
<div class="page" data-ng-controller="memberController">
<table>
<!-- your whole big table here -->
</table>
</div>
And your editing form as follows:
<div class="page" data-ng-controller="editController">
<div class="panel-body"></div>
</div>
Notice that you now have two distinct controllers - your "editController" and your "memberController".
The second question then becomes, how do you transfer the selected ID from the list view ("memberController") to the edit view ("editController").
There are 2 ways of doing that.
First, you could use a service shared between the controller:
.factory('SelectedId',function() {
return {};
});
And then in your "member" view, you would set it upon clicking:
{{member.businessname}}
Notice the ng-click, which then needs a function in the memberController and the injected service:
.controller('memberController',function($scope,SelectedId) {
$scope.setId = function(id) {
SelectedId.id = id;
};
});
While the editController retrieves it:
.controller('editController',function($scope,SelectedId) {
$scope.id = SelectedId.id;
});
The above option works well, especially for complex things like shopping carts. If all you are doing is passing an ID, I would just stick it in the URL:
{{member.businessname}}
So that the ID is part of the URL. You then can retrieve it in the "editController":
.controller('editController',function($scope,$routeParams) {
$scope.id = $routeParams.member;
});
assuming you are using ng-route, and your route would look like:
$routeProvider.when('/pages/edit_form/:member',{templateUrl:'/route/to/template.html',controller:'editController'});
In html do that
<td>{{member.businessname}}</td>
...
In app.js or where you define route do that
.when('/edit/:memberid',
{
templateUrl:'partials/edit.html',
controller:'editController'
})
In controller you have to take this id by doing that
app.controller("editController",function($routeParams,$scope){
$scope.memberid= $routeParams.memberid;
//Now use this member id to fetch all data
});

Categories

Resources