how to use ng-show to display specific divs inside an ng-repeat? - javascript

I have an ng-repeat div to dynamically display a set of div tags based on a dynamic list of items, that each have a unique Id , a type and a "title" value which are both strings and I have a click function assigned to each of them. When I want to click on one of these divs , I want to display an separate div associated to that clicked div and I want to do that using an ng-show which at the moment has a condition where the id of this item/div should be equal/equivalent to a
scope variable I've defined in a controller associated with the html for this new div to be displayed.
The problem I'm getting is that every one of these separate divs is showing and assuming that all the ng-shows are true which shouldn't be the case and I'm not sure why this is happening as all the id's for the items are unique. I've printed out to the console and can't see anything wrong with the assigning of the variable but not sure if I've missed something with regards to the ng-show condition.
Here is what I have so far in html with 2 div examples (don't worry about replicating all the styling, i just want to figure out what is going on with the html/angular stuff):
<div class="col-md-12 col-sm-12 col-xs-12 " ng-repeat="item in items track by item.id" ng-click="onClick(item.id)">
//first type div that is clickable
<div class="row">
<div>
<header class="text">
<h1 data-ng-if="item.title" ng-bind-html="item.title"></h1>
</header>
</div>
</div>
//div to be displayed after associated first div type is clicked
<div class=" col-sm-11 row" ng-show="displayMessage===item.id" >
<header class="col-sm-12 text">
<h1 data-ng-if="item.title" ng-bind-html="item.title"></h1>
</header>
<p class="col-sm-12" > text about {{item.id]} </p>
</div>
//2nd type of div
<div class="row" style=" background: linear-gradient(135deg, #156570, #1e9d8b);">
<h1 data-ng-if="item.title" ng-bind-html="item.title"></h1>
<i class="ms-icon ms-icon-heart-rate"></i>
<div class="clearfix"></div>
</div>
//div to be displayed after associated second div is clicked
<div class="col-md-11 col-sm-11 col-xs-11" ng-show="displayMessage===item.id">
<div style="background: linear-gradient(135deg, #156570, #1e9d8b);"></div>
<h1 class="col-sm-12 col-xs-12" data-ng-if="item.title" ng-bind-html="item.title" style="color:#000"></h1>
<p class="col-sm-12 col-xs-12 "> text associated with {{item.id}}
</p>
</div>
</div>
And here is the simple click function I have . $scope.displayMessage is defined earlier on during the controller setup where its set to an empty string:
$scope.onClick = function (itemId) {
$scope.displayMessage = itemId;
}
Let me know if I need to add more code.

It could be done in a simpler way without adding property to the items varible you have in scope.
Let the Controller be
app.controller('MainCtrl', function($scope) {
$scope.div_=[];
$scope.items = [
{
id: 1,
title: "first item"
},
{
id: 2,
title: "second item",
},
{
id: 3,
title: "third item",
}
];
$scope.onClick=function(index,row){
$scope.div_[index+'_'+row]=true;
}
});
HTML will be like
<body ng-controller="MainCtrl">
<div ng-repeat="item in items">
<div style="color:red" ng-click="onClick($index,0)">DIV {{$index}}.0---click to show DIV {{$index}}.0_CHILD</div>
<div style="color:blue" ng-show="div_[$index+'_0']">DIV {{$index}}.0_CLILD</div>
<br>
<div style="color:red" ng-click="onClick($index,1)">DIV {{$index}}.1---click to show DIV {{$index}}.1_CHILD</div>
<div style="color:blue" ng-show="div_[$index+'_1']">DIV {{$index}}.1_CLILD</div>
<hr>
</div>
</body>
Here an array named 'div_' is maintained to trace all ng-show value of all div.
Working plunker https://plnkr.co/edit/uhFdCXkmS4gB95c9bjTR?p=preview

This will be easier for you to accomplish if you had properties on each item to key off of.
For example,
$scope.items = [
{
id: 1,
title: "first item",
isFirstDivSelected: false,
isSecondDivSelected: false
},
{
id: 2,
title: "second item",
isFirstDivSelected: false,
isSecondDivSelected: false
}
{
id: 3,
title: "third item",
isFirstDivSelected: false,
isSecondDivSelected: false
}
];
This will allow you to add an ng-click to your children items.
<div class="col-md-12 col-sm-12 col-xs-12 " ng-repeat="item in items track by item.id" ng-click="onClick(item.id)">
//first type div that is clickable
<div class="row" ng-click="item.isFirstDivSelected = true;">
<div>
<header class="text">
<h1 data-ng-if="item.title" ng-bind-html="item.title"></h1>
</header>
</div>
</div>
//div to be displayed after associated first div is clicked
<div class=" col-sm-11 row" ng-show="item.isFirstDivSelected" >
<header class="col-sm-12 text">
<h1 data-ng-if="item.title" ng-bind-html="item.title"></h1>
</header>
<p class="col-sm-12" > text about {{item.id]} </p>
</div>
//more divs.........
Right now, there is no good way to for your application to know that any of your children divs have been clicked. There are other ways you can do this, but in my opinion, adding properties that are well defined and straightforward is the best way.

if you want to show hide rows under loop
<div *ngFor="let client_obj of dashboard_data.data.items ;let i = index" >
<input type="checkbox" [(ngModel)]="div_['level_1_'+i]">
<div [class.hide]="div_['level_1_'+i]" >
{{client_obj.value}}
</div>
</div>

Related

How to stop all items from being opened when editing item in ngFor loop

I have an array of objects and you can edit the name of each one but then I click to edit one all of the names of the items open, I am wondering how do to fix this.
<div *ngFor="let stop of fave; let i = index" attr.data="{{stop.Type}}">
<div class="card m-1">
<div class="card-body">
<div class="card-text">
<div class="row">
<label class="name" *ngIf="!toggleName" (click)="toggleName = true">{{stop.Name}}</label>
<div class="md-form" *ngIf="toggleName">
<input (keydown.enter)="updateStopName(i, stop.id); toggleName = false" placeholder="Chnage Stop Name" [(ngModel)]="stopName" required mdbInput type="text"
id="form1" class="form-control">
</div>
</div>
<div class="custom">
<img *ngIf="stop.Type === 'Train'" class="train-icon" style="width: 40px; height:40px"
src="assets/img/icon_trian.png" />
<img *ngIf="stop.Type === 'bus'" style="width: 40px; height:40px" src="assets/img/icon_bus.png" />
<img *ngIf="stop.Type === 'Luas'" style="width: 40px; height:40px"
src="assets/img/icon_tram.png" />
</div>
<label class="col-4 custom-label">Stop</label>
<label class="col-5 custom-service-label">Service</label>
<div class="row">
<span class="col-5 stop"> {{stop.StopNo}}</span>
<span style="padding-left:31%;" class="col-6 stop"> {{stop.Type | titlecase}}</span>
</div>
<hr />
<div class="row">
<div class="panel col-7" (click)="getRealtimeInfo({stop: stop.StopNo, type: stop.Type})">
<img class="panel-realtime" src="assets/img/icon_view.png" />
</div>
<div class="panel col-5" (click)="deleteFav(stop.id, i)">
<img class="panel-remove" src="assets/img/icon_remove.png" />
</div>
</div>
</div>
</div>
</div>
</div>
I know its something to do with the index but I am not sure how to write the code to only open the one I clicked on
As you can see at the moment all of them open any help is very much appreciated
If you want to open one at a time, you can use the index and of the item and a boolean. When clicked, set the index value to toggl if it's not already assigned, else assign it null (so that we can close the opened div on same click), and then show the content you want, when toggl === i. Something like:
<div *ngFor="let stop of fave; let i = index">
<label (click)="toggl === i ? toggl = null : toggl = i">Stuff!</label>
<div *ngIf="toggl === i">
<!-- ... -->
</div>
</div>
DEMO: StackBlitz
In your component declare one array
hideme=[];
In your html
<div *ngFor="let stop of fave; let i = index" attr.data="{{stop.Type}}">
<a (click)="hideme[i] = !hideme[i]">show/hide</a>
<div [hidden]="hideme[i]">The content will show/hide</div>
</div>
You have a unique id value inside your array, then you can do it like this:
<div *ngFor="let row of myDataList">
<div [attr.id]="row.myId">{{ row.myValue }}</div>
</div>
Assign an id to your input fields and they will work fine. Right now all of them have same id.
Use this code below as an example:
In your component, create a mapping like so:
itemStates: { [uniqueId: string]: boolean } = {};
Within your on click function:
itemClicked(uniqueId: string) {
let opened: boolean = this.itemStates[uniqueId];
if (opened !== undefined) {
opened = !opened; // Invert the result
} else {
opened = true;
}
}
In your HTML:
<div *ngFor="let item of items">
<h1 (click)="itemClicked(item.uniqueId)">{{ item.name }}</h1>
<div *ngIf="itemStates[item.uniqueId] == true">
<p>This item is open!</p>
</div>
</div>
Essentially, each item in your array should have a unique identifier. The itemStates object acts as a dictionary, with each unique ID having an associated true/false value indicating whether or not the item is open.
Edit: The accepted answer to this question is very simple and works great but this example may suit those who need to have the ability to have more than one item open at once.

Ionic + AngularJS: ng-repeat filter not working on grid layout but works in list

I am currently learning building apps with ionic v1 and angular.
Here's the scenario, I'm trying to make a grid of cards and was able to do it by following the tutorial on this link.
I was able to populate the grid with cards using json data. However, I also need to add a search box in order to filter the results. I used angularJS ng-repeat filter on the view but it is not filtering it correctly.
here's my code:
angular.module('starter')
.factory('Disasters', function() {
var disasters= [{
id: 0,
name: 'Earthquake',
face: 'img/earthquake/thumbs.PNG',
}, {
id: 1,
name: 'Flash Floods',
face: 'img/flood/thumbs.png',
}, {
id: 2,
name: 'Typhoon',
face: 'img/typhoon/thumbs.png',
}, {
id: 3,
name: 'Avalanche',
face: 'img/avalanche/thumbs.png',
}];
return {
all: function() {
return disasters;
},
remove: function(disaster) {
disasters.splice(disasters.indexOf(disaster), 1);
},
get: function(disasterID) {
for (var i = 0; i < disasters.length; i++) {
if (disasters[i].id === parseInt(disasterID)) {
return disasters[i];
}
}
return null;
}
};
})
.controller('DisasterCtrl', function($scope,$state,$ionicSideMenuDelegate,Chats,Facts){
$scope.disasters = Disasters.all();
})
and here's the code for my view
<ion-content>
<input type="text" ng-model="search">
<div ng-repeat="disaster in disasters | filter: search" ng-if="$index % 2 === 0">
<div class="row">
<div class="col col-50 no-padding" ng-if="$index < disasters.length">
<div class="list card card-cell">
<div class="item item-image background-image" back-img="{{disasters[$index].slider[0].image}}">
</div>
<div class="item item-avatar" ng-click="href({{disasters[$index].id}})">
<p class="bold uppercase font12">{{disasters[$index].name}}</p>
</div>
</div>
</div>
<div class="col col-50 no-padding" ng-if="$index < disasters.length">
<div class="list card card-cell">
<div class="item item-image background-image" back-img="{{disasters[$index + 1].slider[0].image}}">
</div>
<div class="item item-avatar" ng-click="href({{disasters[$index + 1].id}})">
<p class="bold uppercase font12">{{disasters[$index + 1].name}}</p>
</div>
</div>
</div>
</div>
</div>
</ion-content>
when I type something on the search text box it always shows the first two object in the JSON data.
If I change the view into a list the filter works, however it is required to apply the grid styling on the view.
Thanks!
Pluker link of the code : plnk code link
The problem with your grid layouting is that you are using the ng-repeat in a row instead of the columns so when the search item is among a particular it returns the full row because thats where your item is located. so i created a code to help you sort it in plnk where i used the ng-repeat in a column instead of the row and added a style to the row to make it break which is this
class="row" style="flex-wrap: wrap;"
Input: <input type="text" ng-model="search.name" style="border:1px solid #ccc">
<br>
<div class="row" style="flex-wrap: wrap;" >
<div class="col col-50 no-padding" ng-repeat="disaster in disasters | filter: search" >
{{$index}}
<div class="list card card-cell">
<div class="item item-image background-image" back-img="{{disaster.slider[0].image}}">
</div>
<div class="item item-avatar" ng-click="href({{disaster.id}})">
<p class="bold uppercase font12">{{disaster.name}}</p>
</div>
</div>
</div>
</div>
That should work for you as needed. here is the demo running demo
reference from this answer

Using AngularJS to show divs based on select value

I'm trying to use an HTML select element's value to show or hide certain div tags with AngularJS. Here is my code:
<body ng-app="kiosk" id="ng-app" >
<div class="page" ng-controller="kiosk-controller" ng-cloak="true">
<section class="signin">
<div class="intro">
<h1 id="service-desk-name">{{servicedeskname}}</h1><br></br>
<h4 id="welcome">{{welcome}}</h4>
</div>
<hr></hr>
<form id="form" name="form">
<div>
<label>Do you have an ID?</label><br></br>
<select type="select"
ng-model="user.affiliated"
ng-required="true"
ng-options="opt as opt.name for opt in affiliate.affiliateOptions">
<option value="">--Select an answer--</option>
</select>
</div>
<div ng-switch="user.affiliated">
<div ng-switch-when="Yes">
<!--><-->
</div>
<div ng-switch-when="No">
<!--><-->
</div>
</div>
And here is the Javascript snippet:
var kiosk = angular.module('kiosk',[]);
kiosk.controller('kiosk-controller', ['$scope', function($scope, $user) {
$scope.servicedeskname = 'Service Desk';
$scope.welcome = 'Please sign in and a consultant will be with you shortly.';
$scope.affiliate = {affiliateOptions:[]};
$scope.affiliate.affiliateOptions = [
{ name: 'Yes' },
{ name: 'No' }
];
/*other stuff*/
};
I can't figure out the proper way to reference the select options in the HTML tags using ng directives. Unless it's not super clear, I want to be able to show one div if the select value is "Yes", and show another if the select value is "No".
I think the switch statement you use need small modification
<div ng-switch on="user.affiliated">
<div ng-switch-when="Yes">
<!--><-->
</div>
<div ng-switch-when="No">
<!--><-->
</div>
</div>
check this ng-switch documentation for more help
I believe you're looking for ng-show and ng-hide.
<div ng-show="user.affiliated === 'Yes'"></div>
<div ng-show="user.affiliated === 'No'"></div>
And try changing this line to this:
ng-options="opt.name as opt.name for opt in affiliate.affiliateOptions">
You forgot the name property in the switch statement.
- <div ng-switch="user.affiliated">
+ <div ng-switch="user.affiliated.name">
As can be viewed in jsfiddle
you just have a little mistake. you must have user.affiliated.name instead of user.affiliated
like this
<div ng-switch="user.affiliated.name">
<div ng-switch-when="Yes">
yes
</div>
<div ng-switch-when="No">
no
</div>
</div>

Hide All But One Item In A Div Upon Tree Selection

I am loading all content on a page at once. Upon a selection of the tree I want to show only 1 grid. What my code is doing is not totally getting rid of the hidden grid nor moving the selected grid to the top (probably because the hidden grid is not totally gone I assume)
The code below seems to hide everything then keep "gridWrapper2" visible but the table in it is hidden.
How do I modify the following line of code to hide all the contents but whats in the specified id. In other words dont hide any tag thats nested in "gridWrapper2"
$('#contents div').not("#gridWrapper2").hide();
Here is where I am using the code
$(function() {
$("span.dynatree-edit-icon").live("click", function(e) {
alert("Edit " + $.ui.dynatree.getNode(e.target));
});
$("#tree").dynatree({
onActivate: function(node) {
$('#contents div').not("#gridWrapper2").hide();
$("#info").text("You activated " + node);
},
children: [{
title: "Item 1"
}, {
title: "Folder 2",
isFolder: true,
children: [{
title: "grid2"
}, {
title: "grid"
}]
}, {
title: "Item 3"
}]
});
<body>
<div class="container-fluid text-left">
<div class="row content">
<div class="col-sm-2 sidenav" id="tree">
</div>
<div class="col-sm-8" id="contents">
<div id="gridWrapper"> <table id="grid"></table> </div>
<div id="gridWrapper2"> <table id="grid2"></table> </div>
</div>
<div id="info"> </div>
</div>
</div>
</div>
</body>
</html>
Make sure contents inside table are in proper/valid html. See below example for more info. display:none will be applied to table tag & only valid html inside it could be hidden everything else would come out of it if not valid.
$(function() {
$("#tree").click(function() {
$('#contents table').not("#grid2").not("#grid4").hide();
});
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container-fluid text-left">
<div class="row content">
</div>
<div class="col-sm-8" id="contents">
<table id="grid">
<tr>
<td>grid</td>
</tr>
</table>
<table id="grid2">
<tr>
<td>grid2</td>
</tr>
</table>
<table id="grid3">grid3</table>
<table id="grid4">grid4</table>
</div>
<div id="info"></div>
</div>
</div>
<button id="tree">Tree</button>
This is the solution
$("#contents > *").css('display','none');
$("#gridWrapper2").show();

Inner ng-repeat is skipping the first item of my array, and putting them into the last html element

I have a JSON array that I'm trying to convert and display as HTML using angular's ng-repeat.
My JSON is of the form:
data:{
thing_one:[{
id:1,
fields:[{ .... }]
},
{
id:2,
fields:[{ .... }]
}],
separate_thing:[{
id:1,
fields:[{ .... }]
},
{
id:2,
fields:[{ .... }]
}]
}
And in my controller for that page:
I have:
MockFields.get(function(data){
$scope.thing_one = data.thing_one;
$scope.separate_thing = data.separate_thing;
}
And a stripped down version of my html is:
<div class="details">
<div class="my_header">
<h2>Thing 1</h2>
</div>
<div class="thing_one">
<dynamic-form fields="thing_1[0].fields"><dynamic-form>
</div>
</div>
<div class="details">
<div class="my_header">
<h2>Separate Thing</h2>
</div>
<div class="separate_thing" ng-repeat="thing in separate_thing>
<dynamic-form fields="thing.fields"><dynamic-form>
</div>
</div>
Where <dynamic-form> is a custom directive, where I repeat over my fields, and use an ng-switch on=... to pick a field type. It looks a little something like this (again cut down)
<div ng-repeat='field in fields track by field.order'>
<label>{{field.display}}</label>
<span ng-switch on='field.fieldtype'>
<span ng-switch-when="text">
<input type="text"/>
</span>
<span ng-switch-when="....">
<input type="...."/>
</span>
.
.
.
.
</span>
</div>
What I'm seeing however is that all of my fields from both things in separate_thing are in the last <dynamic-form> and the first one is empty.
This is what I see when I inspect that element/section of the DOM:
<div class="details">
<div class="my_header">
<h2>Separate Thing</h2>
</div>
<div class="separate_thing" ng-repeat="thing in separate_thing></div>
<div class="separate_thing" ng-repeat="thing in separate_thing>
<div>
<label>Foo from separate thing 1</label>
<input type="text">
.....
</div>
<div>
<label>Foo from separate thing 2</label>
<input type="text">
.....
</div>
</div>
</div>
Is this an optimization from angular? Or have I done something wrong? I'm a little lost as I've not seen any errors from my code.
Why is it pushing all my sub-objects (fields) to the last element?
This might have been a bug.
Angular directives fail to compile when they have multiple elements without a containing element, which my previous set up had (via ng-repeat)
Instead inside my directive, I've now wrapped the div with ng-repeat inside an out div.
So I now have:
<div>
<div ng-repeat='field in fields track by field.order'>
<label>{{field.display}}</label>
<span ng-switch on='field.fieldtype'>
<span ng-switch-when="text">
<input type="text"/>
</span>
<span ng-switch-when="....">
<input type="...."/>
</span>
.
.
.
.
</span>
</div>
</div>
And it all seems happy.

Categories

Resources