Good way to collapse certain Table/ cells when using ngFor - javascript

This is a Angular project v7 or so! im including angular tag because i dont know if there is any angular helpers for this.
I have multiple tables based on NG for, for each table i have a collapse/expand button. i want the individual table (button clicked) to collapse the table, my current solution isnt working properly because im using one bool and if you click expand/collapse on another table the boolean is incorrect for the others.
Im new to this, so i may of greatly over complicated the attmepted solution. :)
added a class to collapsable panels, then loop through elemens to add/remove classes based on bool.
viewDetailOption(event) {
console.log(this.show)
this.show = !this.show;
console.log(this.show)
// Toggle the Arrows
event.srcElement.classList.toggle('fa-angle-double-right');
event.srcElement.classList.toggle('fa-angle-double-left');
const parentNode = event.srcElement.parentNode.parentNode;
const childNodes = parentNode.querySelectorAll('.extendedViewOnly')
if (this.show) {
for (let i = 0; i < childNodes.length; i++) {
childNodes[i].classList.remove('extendedViewOnly');
// show is not used in CSS, is added to identify when a hidden field is shown
childNodes[i].classList.add('show');
}
} else {
const hiddenNodes = parentNode.querySelectorAll('.show')
for (let i = 0; i < hiddenNodes.length; i++) {
hiddenNodes[i].classList.remove('show');
hiddenNodes[i].classList.add('extendedViewOnly');
}
}
}
<div class="container-fluid">
<app-tab-selector [contactDescription]='contactDescription' [contactId]="id" [data]="contactDetails"></app-tab-selector>
<br>
<a class="btn btn-raised btn-primary">Add</a>
<section id="striped-light">
<div class="row text-left">
<div class="col-sm-12">
<div class="card" *ngFor="let contact of contactDetails; let i = index" [attr.id]="'fcard' + i">
<div class="card-header">
<p class="card-title">{{contact.name}} </p>
<span class="pull-right fa fa-2x fa-angle-double-right" (click)="viewDetailOption($event)" placement="top" ngbTooltip="Collapse / Expand"></span>
</div>
<div class="card-content">
<div class="card-body">
<table class="table table-striped ">
<thead>
<tr [attr.id]="'fr' + i">
<th> No.</th>
<th>Capacity</th>
<th>T/A</th>
<th>SType</th>
<th>SLayout</th>
<th class="extendedViewOnly">RS</th>
<th class="extendedViewOnly">TA</th>
<th class="extendedViewOnly">SA</th>
<th class="extendedViewOnly">DR</th>
<th class="extendedViewOnly">FE</th>
<th class="extendedViewOnly">NB</th>
<th class="extendedViewOnly">LT</th>
<th class="">Operations</th>
</tr>
</thead>
<tbody >
<tr *ngFor="let item of contact.ph">
<td>{{item2.no}}</td>
<td>{{item2.cap}}</td>
<td>{{item2.ta}}</td>
<td>{{item2.st}}</td>
<td>{{item2.}}</td>
<td class="extendedViewOnly">{{item2.}}</td>
<td class="extendedViewOnly">{{item2.}}</td>
<td class="extendedViewOnly">{{item2.}}</td>
<td class="extendedViewOnly">1200m</td>
<td class="extendedViewOnly">{{item2.}}</td>
<td class="extendedViewOnly">{{item2.}}</td>
<td class="extendedViewOnly">{{item2.}}</td>
<td class="extendedViewOnly">{{item2.}}</td>
<td class="extendedViewOnly">{{item2.}}</td>
<td><i class="fa fa-edit fa-2x"></i></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</section>
To be able to expand and collapse multiple tables, a state for each, is there a way to do this properly or maybe array of states?

You can add a boolean property displayInfo to your contact, and toggle this instead of trying to manage the HTML inside the JavaScript. With this property set, you can use ngClass to assign the correct class (double-right, double-left, show)...
It is always better to leave the HTML handling for Angular, and only care about the object states.

The problem that I see is that you have a collection of items and you are using a single boolean to hide or display those items.
So what you can do is to store the ID of the items you want to display in a hashtable.
The solution may be like this:
The template:
...
<div class="card" *ngFor="let contact of contactDetails; let i = index" [attr.id]="'fcard' + i">
<div class="card-header">
<p class="card-title">{{contact.name}} </p>
<span class="pull-right fa fa-2x fa-angle-double-right" (click)="viewDetailOption($event, contact)" placement="top" ngbTooltip="Collapse / Expand"></span> </div>
...
The TS file:
// conmponent class
itemsToShow = {};
viewDetailOption(event, contact) {
console.log(this.itemsToShow[constact.id])
this.itemsToShow[constact.id] = !this.itemsToShow[constact.id];
// ... the rest of your logic here
}

Related

How to apply ngclass for a specific row in html table using angular

I'm using angular and I want to apply a css class to a specific row here's the code:
componentjs:
flag=false;
delete(){
flag=true;
}
html:
<tr *ngFor="let app of pages ; let i = index" [id]="i"
class="hvr-fade">
<td class="row-mid">{{app.nom}</td>
<td class="row-right">
<button class="delete" title="delete" (click)="delete()">
<i [ngClass]="(flag==false )?'fa fa-trash':'fa fa-spinner fa-spin'" aria-hidden="true"></i>
</button>
</td>
</tr>
my goal is to change the button icon from trash to spinner when I click on the button. this code it doesnt work
I suggest an *ngIf instead of an ngClass might resolve your issue.
Have a look at the following example:
https://www.telerik.com/blogs/how-to-use-ngif-else-angular-applications
The flag will not work as it is global and applies to all row of table. You can use row index based solution.
Component.js
currentRowNumber = 0;
delete(index){
this.currentRowNumber = index;
}
Component.html
<tr *ngFor="let app of pages ; let i = index" [id]="i"
class="hvr-fade">
<td class="row-mid">{{app.no}}</td>
<td class="row-right">
<button class="delete" title="delete" (click)="delete(i + 1)">
<i [ngClass]="(currentRowNumber != (i + 1))?'fa fa-trash':'fa fa-spinner fa-spin'" aria-hidden="true"></i>
</button>
</td>
</tr>

Show only one element of a panel using Angularjs

I am new to Angularjs and I am trying to figure out how it's different modules work. So, I am working on a project on which I wanna achieve an accordion-like style for a page, in which a table is shown when I click a panel button. The HTML code that creates(dynamically from a database) the div elements I modify is posted below.The problem is that in this panel, any number of tables can be shown,while I need to only have one opened at a time,so when one opens,the one opened before it should close.Any ideas how I can achieve this functionality?(I assume the error is because the showDB variable is local to each table scope, but I don't know how to work around it.) Thanks!' `
<div ng-repeat="(key, value) in data.services">
<div ng-show="showSection(key)" class="top_panel-section">
<button class="btn top_btn btn-header" type="button" name="showDB"
ng-click="showDB=!showDB">{{key}}</button>
<table ng-show="showDB"
class="n-table toptable table-responsive n-table-standard n-table-striped n-table-hover">
<thead class="n-table-thead">
<tr>
<th width="70%">VM Name</th>
<th width="30%">Status</th>
</tr>
</thead>
<tbody class="n-table-body">
<tr ng-repeat="vm in value.vms">
<td width="76%">{{vm.vm}}</td>
<td width="24%" ng-style="getStatusStyle(vm.status)">
{{vm.status}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
Yes, you should remove that local showDB variable to achieve what you need.
You can easily replace it with a $scope.activeKey and evaluating it by the
<button ... name="showDB" ng-click="activateKey(key)">{{key}}</button>
And in your controller:
$scope.activeKey = null;
$scope.activateKey = function (keyToBeActivated) {
$scope.activeKey = keyToBeActivated;
}
Now you can reach that exclusivity by checking:
<table ng-show="activeKey === key" ... >
Using table $index as unique field: (available from ng-repeat)
<button ... name="showDB" ng-click="activateKey($index)">{{key}}</button>
And
<table ng-show="activeKey === $index" ... >

Angularjs - how to add a row dynamically when clicked on parent row

I am new to angularjs and stuck with the below problem.
There will be a table of rows created which reads data from JSON. Say there are 6 rows displayed.But in real time number rows will vary.
Each row has an accordion(+ symbol) in its first td. If this accordian is clicked then the children rows of that row should be displayed by reading the data from one more different JSON.
Similarly for the remaining 5 rows as well it should display the children rows for the respective row's accordion is clicked.
I have created the table with the 6 rows displayed.
But the challenge I am facing is how to link child rows to the existing rows dynamically when clicked. Here is the plunkr - https://plnkr.co/edit/FTbjn9ZbAOTqc3b6j52h?p=preview
Any help is appreciated.
<html>
<head>
<script src="angular.min.js"></script>
<script src="script.js"></script>
<link rel="stylesheet" href="style.css"/>
<link rel="stylesheet" href="font-awesome.min.css"/>
<link rel="stylesheet" href="bootstrap.min.css" />
</head>
<body data-ng-app="testApp" data-ng-controller="treeTable">
<hr>
<button type="button" class="btn btn-success" data-dismiss="modal" data-ng-click="save()">SaveFilter</button>
<button type="button" class="btn btn-Default" data-dismiss="modal" data-ng-click="delete()">Delete</button>
<div class="row">
<div class="col-md-9">
<div style=" margin-left:50px;" class="tableheight">
<table class="table-nested ">
<thead>
<tr>
<!-- <th >
<input data-ng-checked="(list | selected).length == list.length" data-ng-click="toggleAllCheckboxes($event)" type="checkbox" />
</th> -->
<th>
Repository
</th>
<th >
Version
</th>
<th >
Size
</th>
<th >
Modified By
</th>
<th >
Labels
</th>
<th >
Description
</th>
</tr>
</thead>
<tbody style="font-size:12px" data-ng-repeat="item in list">
<tr >
<td ><button style="background-color: #fff;" class="accordion"></button>
{{item.name}}
</td>
<td >
{{item.Version}}
</td>
<td >
{{item.Size}}
</td>
<td >
{{item.ModifiedBy}}
</td>
<td >
{{item.Labels}}
</td>
<td >
{{item.Description}}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>
See the Plunkr solution
You want a 2nd <tr> with ng-repeat following the first:
<tr ng-if="item.Labels==child.Labels" ng-show="item.opened" ng-repeat="child in children">
<td>{{child.name}}</td>
...etc
</tr>
This will build additional rows where the bundle .Labels matches the repo .Labels and will be shown only when the repo's opened property is true. The + button will toggle the opened property for each item. You'll need two minor edits to your data to make this work:
Add children data to the $scope (for access in the 2nd ng-repeat).
Add default opened: false property to all repos (except the first, which is true).
This won't show the components, but hopefully you get the idea.
See the Plunkr solution
If you want to inset row after row clicked use this:
<style>
#tableid {
font-size: 14px;
}
#tableid .childRow {
font-size:10px;
}
#tableid .childRow td:first-of-type {
padding-left:10px;
}
</style>
<table id="tableid">
<tr onclick="AddRow(this)">
<td>abcd</td>
</tr>
</table>
<script>
function AddRow(e)
{
for (var i = 0; i < 5; i++)
{
var index = e.rowIndex;
var table = document.getElementById("tableid");
var row = table.insertRow(index + 1 + i);
row.setAttribute('class', 'childRow');
var cell = row.insertCell(0);
cell.innerHTML = "efgh";
}
}
</script>

angularJS print directive losing 2 way binding

I have a print directive in a SPA that seems to lose its 2 way data binding after the first print function is ran. I am sure it has something to do with the cloning and appending of the element.
The Directive is as follows:
agency.directive("pasPrint", ['AgentModel','Dev',
function(agentModel, dev) {
var printSection = document.getElementById("printSection");
function printElement(elem) {
// clones the element you want to print
var domClone = elem.cloneNode(true);
if (!printSection) {
printSection = document.createElement("div");
printSection.id = "printSection";
document.body.appendChild(printSection);
} else {
printSection.innerHTML = "";
}
printSection.appendChild(domClone);
}
return {
restrict: 'A',
link: function($scope, $element, attrs) {
$scope.printModel = {};
$scope.today = new Date().toJSON().slice(0,10);
$element.on("click", function () {
var elemToPrint = document.getElementById(attrs.printElementId);
if (elemToPrint) {
printElement(elemToPrint);
window.print();
}
});
},
controller: ["$scope","$element", "AgentModel", function($scope, $element, agentModel) {
$scope.agentModel = agentModel;
}]
};
}]);
the model that is used in the entire module here is the agentModel.singleAgent which of course represents a single agent. this directive simply prints a cover sheet for an agents hard copy record. the first time we pull up an agents record the cover sheet prints correctly. when we load another agent the print preview show's the correctly updated agent information. in the agentModel.singleAgent 2 way binding (the same values being used in the printSection that gets printed. However the second print attempt also prints the first agent information and so does any other attempt to print any other agents, they all print the first agents data, even thought the singleAgent model has been updated properly.
the html from the print preview is below:
<div class="modal-header ng-scope">
<button type="button" class="close" aria-label="Close" data-ng- click="cancel()"><span aria-hidden="true"> × </span></button>
<h3 class="modal-title ng-binding"> Edit & Print Cover Sheet </h3>
</div>
<div class="modal-body">
<form class="inline-form">
<div class="form-group">
<label class="form-label" for="PRINT_DESC">File Description: </label>
<input class="input-medium" type="text" id="PRINT_DESC" name="PRINT_DESC" data-ng-model="printModel.PRINT_DESC" />
</div>
</form>
<div id="printPreview">
<table class="table table-cell-nowrap table-responsive">
<thead>
<tr>
<th colspan="2"><strong>Type: <em>{{printModel.PRINT_DESC}}</em></strong></th>
<th colspan="2" style="border: 1px #dadada dotted"><div class="clearfix pull-right"><strong>{{agentModel.singleAgent.AGENT_NUMBER}}</strong></div></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Agent Name: {{ agentModel.singleAgent.AGENT_LNAME }}, {{ agentModel.singleAgent.AGENT_FNAME }} </strong></td>
<td><strong>Agent #: {{ agentModel.singleAgent.AGENT_NUMBER }}</strong></td>
<td><strong>UID: {{ agentModel.singleAgent.AGENT_UID }}</strong></td>
<td></td>
</tr>
<tr><td colspan="4">Printed On: {{ today }} </td></tr>
</tbody>
</table>
</div>
<div id="printSection" class="col-md-12"><!-- place nothing in here except what should be printed. by default the content in the print section IS hidden -->
<table class="table table-cell-nowrap table-responsive printCoverFontSize">
<thead>
<tr>
<th colspan="2"><strong>Type: <em>{{printModel.PRINT_DESC}}</em></strong></th>
<th colspan="2" style="border: 1px #dadada dotted"><div class="clearfix pull-right"><strong>{{agentModel.singleAgent.AGENT_NUMBER}}</strong></div></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Agent Name: {{ agentModel.singleAgent.AGENT_LNAME }}, {{ agentModel.singleAgent.AGENT_FNAME }} </strong></td>
<td><strong>Agent #: {{ agentModel.singleAgent.AGENT_NUMBER }}</strong></td>
<td><strong>UID: {{ agentModel.singleAgent.AGENT_UID }}</strong></td>
<td></td>
</tr>
<tr><td colspan="4">Printed On: {{ today }} </td></tr>
</tbody>
</table>
</div>
</div>
<div class="modal-footer">
<div class="pull-left">
<button class="btn btn-warning" data-pas-print data-print-element-id="printSection"><i class="fa fa-print fa-lg"></i> Print </button>
</div>
<div class="pull-right">
<button class="btn btn-default" data-ng- click="cancel()">Cancel</button>
</div>
the printModel model simply takes the value from the text box and includes it to be printed, just a descriptive term.
I am at a loss as to why this is not working. I did just piece this together from other scripts I found online to work with our application. I am a bit of an angular Newb and I really appreciate any help I can get so thank you in advance. please if you need more information feel free to ask. If I had to guess I would say it has something to do with cloning and the emptying of the element if I had to guess, but I am at a loss as to how to fix it. HELP please.

How to make a custom HTML id by evaluating an expression

I try to make a version of "tic tac toe" with AngularJS and be as minimalistic as possible. The only solution for my problem is to assign every button a unique ID (f+i).
HTML
<table>
<tr ng-repeat="f in [5,10,15]">
<!-- numbers chosen for unique combos-->
<td ng-repeat="i in [0,1,2]">
<button ng-click="toTrue()" >
<div >
{{getXO()}}
</div>
</button>
</td>
</tr>
</table>
JavaScript
$scope.XObool=false;
$scope.toTrue = function() {
if(!$scope.XObool){
$scope.XObool=true;
}
else if($scope.XObool) {
$scope.XObool=false;
}
};
$scope.getXO = function(){
if($scope.XObool){
return 'X';
}
else {
return 'O';
}
};
ng-repeat gives you several variables to work with, namely $index. In your case you'll want something like:
<button id="{{$index}}" ...>
More info on the ng-repeat docs.
Second Option
Use the f and i variables to create unique IDs.
<table ng-app>
<tr ng-repeat="f in [5,10,15]" data-id="{{$index}}">
<td ng-repeat="i in [0,1,2]">
<button id={{'id' + (i+f)}} ng-click="toTrue()">
{{'id'+(i+f)}}
</button>
</td>
</tr>
</table>
Here's a demo.
You don't need to assign each button a unique ID.
Instead, you can pass your f and i variables into your functions to track the board state:
<table>
<tr ng-repeat="f in [0,1,2]">
<!-- numbers chosen for unique combos-->
<td ng-repeat="i in [0,1,2]">
<button ng-click="setState(f, i)">
<div >
{{ getXO(f,i) }}
</div>
</button>
</td>
</tr>
</table>
Working fiddle here: http://jsfiddle.net/m76w3kf5/
try markup binding with {{}} or $index
<div id="{{someObject.id}}" class="some-class" ng-repeat="f in [ array ]">
..
</div>
or a slightly extended example with $index, listing thumbnails in array with clicks to reference position by index
<tr ng-repeat="array in thumbnails track by $index">
<td ng-repeat="object in array track by object.id"
ng-click="tableClickHandler($index, object)">
<img class="user-thumbnail" src="{{object.src}}">
</td>
</tr>

Categories