AngularJS : why ngrepeat not working properly not display data? - angularjs

I am making a simple demo of nested ng-repeat with some condition.it is not printing any values why ?
Actually I am trying to get below result after iteration of two objects
Expected result
ght nor
abc pqr
code pen or plunker
\
<ion-scroll scrollbar-y="true">
<div class="row rowclass" ng-repeat="column in a ">
<div class="col brd text-center collapse-sm columnCss" ng-repeat="field in column.columns" ng-show="b[$index].fieldNameOrPath===field.fieldNameOrPath">{{field.value}}</div>
</div>
</ion-scroll>
Actually I have two object ..having one key inside I need to show those value which is equal .but i am not getting expected result why ?

You need to update
ng-show="b[$index].fieldNameOrPath===field.fieldNameOrPath"
to
ng-show="b[$index-1].fieldNameOrPath===field.fieldNameOrPath"
And in your json, fieldNameOrPath property is different in a and b like in a it is "Sub" and in b it is "Subject", so either update a or b, so that property fieldNameOrPath is same at both the places.

Here is the answer with example.
Also in script.js file i have corrected some type mistakes.
ng-show="b[$index-1].fieldNameOrPath===field.fieldNameOrPath"

While it is technically possible to do this with your current data set, I'd very much recommend changing your data model to look something like this:
$scope.dataRows = [
{
ID: "00TK000000INaMXMA1",
Subject: "ght",
Priority: "normal"
},
{
ID: "00TKabc",
Subject: "abc",
Priority: "pqr"
}
];
And your HTML to something like this:
<ion-scroll scrollbar-y="true">
<div class="row rowclass" ng-repeat="row in dataRows">
<div class="col brd text-center collapse-sm columnCss">
{{row.Subject}}
</div>
<div class="col brd text-center collapse-sm columnCss">
{{row.Priority}}
</div>
</div>
</ion-scroll>
However, if for some reason this just isn't possible (data feed from some other place etc), then here is how I fixed your table issues:
1 : your fieldNameOrPath attributes didn't match (one was "Pri" and "Sub" and the other was "Priority" and "Subject").
2: You will need to add another ng-repeat unfortunately. Your indexes don't line up correctly because you have 3 fields in your data and 2 in your data columns. I've made a Plnkr fork for you that solves your problem (minus some css styling stuff) but I'd only recommend using it if you cannot change your data model.
http://plnkr.co/edit/3K3zKenGHG4hARkXmUTp?p=preview

Related

Assigning a filter to an object variable in Angular JS

I have an ng-repeat using a filter like this:
#1
<div class="contestReports" ng-repeat="contest in contests | filter:{votingOver:true}">
<contestreport></contestreport>
</div>
I want to allow the customer to be able to filter it so I have assigned the filter to a variable like this:
#2
<div ng-init="reportFilter = {votingOver:true}"></div>
<div class="contestReports" ng-repeat="contest in contests | filter:reportFilter">
<contestreport></contestreport>
</div>
Code #1 is working but Code #2 is not and I'm not sure why.
Did you also try to wrap ng-init
<div ng-init="(reportFilter = '{votingOver:true}')"></div>
However, as I've stated in my comment earlier - angularjs documentation states that using ngInit is a bad practice in mostly all cases. So that should not be a solution to your problem if possible.
And your #2 code actually works, please check this plunk - http://plnkr.co/edit/dBDyYPd3ZoUVdXngu52t?p=preview
//html
<div ng-init="reportFilter = {votingOver:false}"></div>
<div class="contestReports" ng-repeat="contest in contests | filter:reportFilter">
{{contest | json}}
</div>
</div>
//js in controller
$scope.contests = [
{id:1, title:'1', votingOver:false},
{id:2, title:'2', votingOver:true},
{id:3, title:'3', votingOver:true}
];
Using ng-init is not a good way to go with(as you can see angular docs has added warning text in red). As you know it is going to evaluate on initial rendering only(as ng-init evaluates its expression in preLink phase).
By looking at your code, it seems like reportFilter variable is going to be an common object for all contests. Correct me if I'm wrong?
I'd add this value inside a controller only once like below.
//place this inside your controller
$scope.reportFilter = { votingOver : true };
And use directly on view itself.
<div class="contestReports" ng-repeat="contest in contests | filter:reportFilter">
<contestreport></contestreport>
</div>
Better way is to save the result of applying filter to the json data in a variable before ng-repeat reads items in the jsonArray like below.
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app>
<div ng-init="contests=
[{name:'bad apple', votingOver:false},
{name:'pleasurebuilder', votingOver:true},
{votingOver:true}]">
</div>
<div ng-init="reportFilter = {votingOver:true}">
</div>
<div ng-repeat="contest in revoluza = (contests | filter: reportFilter)">
{{contest}}
</div>
<hr>
</div>
Code 2 is not working because you have to create custom filter while you are only intializing div not creating filter
Code 1 is working because you are using proper syntax for default filter that can be compared with any object just like you did

AngularJS data-ng-hide puts blank spaces

It looks as though data-ng-hide works as expected except that it puts a space where the value would be shown. Is there a way to surpress that space so the values which should be shown are shifted to the right. For instance my loop has three people in it, but on the third person will be shown. The name for that person is to the far right rather than the first column on the left. I can't use data-ng-if as it removes values from the array.
This is the code:
<div class="row">
<label class="col-md-3" style="text-align: center">Checked Out to:</label>
<div class="col-md-8" ng-repeat="rows in chunkedData">
<div class="col-xs-4" ng-repeat="route in rows">
<span data-ng-hide="!route.checkedOut">{{route.user.firstName}} {{route.user.lastName}}</span>
</div>
</div>
</div>
This is the normal behavior: You're repeating a <div> that has the class "col-xs-4".
The ng-hide is inside it, meaning that for each route in rows, you'll display a column with or without text in it.
Try this instead :
<div class="col-xs-4" ng-repeat="route in rows | filter:{checkedOut:true}">
{{route.user.firstName}} {{route.user.lastName}}
</div>
AngularJS's documentation on ngRepeat and filters could also help you to better understand filters.

ng-class within ng-repeat when object comes from other object/array

PLUNKER
I have an app that allows users to see info about live music and favorite the shows they like. Pushing the star button does this. They can then see this information on their own music calendar page.
I'm able to save and update the favoriting data into my database just fine now. But I can't figure out how to style the star buttons (favoriting buttons) based on whether the user has favorited the item or not. They should change color/style if they are favorited or not.
There is data that comes from the music shows and data about the user (their personal info or favorites). These are separate bits of data. So when I bring the shows info in via ng-repeat, this is different than the info I have on whether the user has favorited that show.
The jist of the question, then, is how to style items/icons using ng-class within an ng-repeat when the info on whether those items have been favorited (true or false) comes from another object or array (can create either). Most of the examples with ng-repeat and ng-class are a alot simpler because the data on whether something is selected or not is within that same object.
HTML
<div class="list-group">
<div class="nav nav-stacked list-group-item" id="sidebar" ng-repeat=
"show in shows[0]">
<div>
<div class="col-md-3 text-center">
<h2>{{show.properties.price}}</h2>
<p id="tagtext">{{show.properties.tags}}</p>
</div>
<div class="col-md-6">
<h4 class="list-group-item-heading">{{show.properties.artist}}</h4>
<p class="list-group-item-text">{{show.properties.venue}}</p>
</div>
</div>
<div class="media col-md-3">
<h2 class="fa fa-star-o" ng-click="toggleStarred(show)" ng-class="I don't know">
</h2>
</div>
</div>
</div>
</body>
The pertinent part of the html is around <span class="pull-right star" ng-controller="StarredCtrl"> That's what creates the star icon.
Controller
I'll include the plunker so as not to have to jam this up.
The object where the user favorites are stored looks like this
$scope.loggedInUserFavorites = Object { $$conf: Object, $id: "favorites", $priority: null, show0: true, show1: false, show3: true, show5: false }
using Angular.foreach I pared it down to this:
Object { 0: false, 1: false, 3: true, 5: false }
How can I access those true/false properties of $scope.loggedInUserFavorites on my template to style the star icon?
PLUNKER
Actually this should work:
<h2 class="fa fa-star-o" ng-click="toggleStarred(show)" ng-class="{fillgreen:loggedInUserFavorites[show.properties.id]}">
As scopes prototypically inherit from their parents, you do actually have access to both the show model from the ng-repeat scope, and loggedInUserFavorites from the StarredCtrl scope.
Cheers!
Try this (where shows[$index].starred is your logic to check if the show has been favorited)
<div class="media col-md-3">
<h2 class="fa fa-star-o" ng-click="toggleStarred(show)" ng-class="{starred: shows[$index].starred, no-star: !shows[$index].starred}">
</h2>
</div>
I basically used both #sanghas26 and #HeberLZ's to come up with:
<h2 class="fa" ng-click="toggleStarred(show)" ng-class="{'fa-star': loggedInUserFavorites[show.properties.id], 'fa-star-o': !loggedInUserFavorites[show.properties.id]}"></h2>
So I changed the h2 to fa and then used the full and empty stars as the css classes. I really don't know why fillgreen and fillblue didn't work here. Will continue to investigate.
Anyway, it works. This took 6 weeks to figure out.

In AngularJS tutorial select only one instance, or the first instance in an array

I want to select only the first instance of an array in angular instead of using ng-repeat. I was thinking something like ng-instance, or ng-specificinstance but they don't exist. Also how should I define my javascript variable and what is the standard for referencing it in my html: "phoneinfos" or "phoneinfo" if its only one entity. Also should all things with lots of items should it always end in an s?
Current HTML:
<div id="searchtab-1" style="height:1.5em;" ng-controller="PhoneListCtrl">
<label ng-repeat="phoneinfo in phoneinfos">{{phoneinfo.count}}</label>
</div>
HTML Attempt:
<div id="searchtab-1" style="height:1.5em;" ng-controller="PhoneListCtrl">
<label ng-instance="phoneinfo">{{phoneinfo.count}}</label>
</div>
javascript:
Defined variable:
$scope.phoneinfo = [{ 'count': '555' }, {'type': 'mobile'}];
What about:
<div id="searchtab-1" style="height:1.5em;" ng-controller="PhoneListCtrl">
<label>{{phoneinfo.count}}</label>
</div>
Javascript:
$scope.phoneinfo = {
count: '555',
type: 'mobile'
};
If you actually need the data to stay exactly as is, you could do this as well (but it doesn't look very pretty to me):
<div id="searchtab-1" style="height:1.5em;" ng-controller="PhoneListCtrl">
<label>{{phoneinfos[0].count}}</label>
</div>
As for the names ending in s, I find it a good practice for the exact reason that you can write item in items, and it also signals that it's an array, but that is a matter of personal taste.

AngularJS - hide parent element if children loop is empty (filtered)

I have a case in which I have nested loops in which the child one is constructed by a filter function that takes parent as the argument. I also have another filter that just does a text comparison. Here is the example
<div ng-repeat="group in groups">
{{group.name}}
<div ng-repeat="material in materials | filter:filterByGroup(group) | filter:search ">
{{material.name}}
</div>
</div>
Now, my problem is that when filter:search is applied and it filters out all the results in specific group, I would like to hide the group (and not leave the empty group.name hanging without child elements).
I don't have the materials in the group it self, so I don't have that information in the parent ng-repeat scope. The question is if there is a way I can access the nested ng-repeat and see its count from the parent and hide the parent if that count is 0.
UPDATE
Here is a fiddle that better explains the situation: fiddle
The main problem is that I don't want to associate my materials with groups. I could do that if nothing else works, but it sounds like an overload (since I would then need to basically filter the results twice) if I could do it by just checking the nested loop.
Thanks
A much cleaner solution was suggested HERE.
What you need to do is wrap the relevant area with an ng-show / ng-if based on an expression that applies the filter on the data structure and extracts length. Here is how it works in your example:
<div ng-show="(materials | filter:filterByGroup(group)).length">
<div ng-repeat="group in groups">
{{group.name}}
<div ng-repeat="material in materials | filter:filterByGroup(group) | filter:search ">
{{material.name}}
</div>
</div>
</div>
This allows you to hide complex structures once they are empty due to filtering, e.g. a table of results.
I've seen this use-case a few times, here's my solution:
<div ng-repeat="group in groups">
<div ng-repeat="material in materials | filter:filterByGroup(group) | filter:search ">
<span ng-show="$first">
{{group.name}}<br/>
</span>
{{material.name}}
</div>
</div>
You can use $first or $last within the scope of the ng-repeat to show only for the first and last of each group. if there is no $first, it won't show the group name.
I just implemented this on my blog and updated your fiddle here: http://jsfiddle.net/ke793/1/
I'm not sure if this is the most elegant solution, but it seems fairly simple and it works. I'd love to see how others solved this.
Update: just realized you can use ng-if to prevent the group name from hitting the dom at all outside of the $first element. Little bit cleaner than ng-hide/ng-show, which sets display: none to the extra header every time.
If you can ask for the materials by group and you expect to do that for each group anyway, why not do that right away when you initialize the view and build a model with groups that have materials? If you can do that you can use ng-show to only hide groups that have materials.
You seem to need to know that a group has materials or not somehow? Here's a fiddle without knowing much of the background story:
<div ng-controller="MyCtrl">
<div ng-repeat="group in groups">
<div ng-show="groupHasMaterials(group)">{{group.name}}</div>
<div ng-repeat="material in materialsByGroup(group)">
<div>{{material.name}}</div>
</div>
</div>
</div>
<script>
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
var groups = [
{'name':'group one'},
{'name':'group two'}
];
var materials = [
{'name':'material1'},
{'name':'material2'},
{'name':'material3'}
];
$scope.groups = groups;
$scope.materials = materials;
$scope.groupHasMaterials = function(group){
return $scope.materialsByGroup(group).length > 0;
}
$scope.materialsByGroup = function(group){
return group.name === 'group one'
? [materials[0], materials[1]]
: [];
}
}
</script>
fiddle with groups

Resources