how to loop through an array to hide items - javascript

i have an html form in which the user clicks on the plus sign and it shows you a list of items, if you click on a minus sign it will hide those items
as follows:
<div repeat.for="categoryGrouping of categoryDepartment">
<div class="row">
<div class="col s12 m3 l3">
<div class="card blue-grey darken-1">
<div class="card-content" style="padding:10px">
<span class="card-title white-text truncate">${categoryGrouping.name}</span>
<a if.bind="categoryGrouping.hideDetails" class="btn-floating halfway-fab waves-effect waves-light" click.delegate="Activate(user, categoryGrouping)"><i class="material-icons">add</i></a>
<a if.bind="!categoryGrouping.hideDetails" class="btn-floating halfway-fab waves-effect waves-light" click.delegate="DeActivate(user, categoryGrouping)"><i class="material-icons">remove</i></a>
</div>
</div>
</div>
</div>
<div repeat.for="categoryGroupingTypes of categoryDepartmentTypes">
<div class="row" if.bind="!categoryGrouping.hideDetails">
<div repeat.for="user of categoryGroupingTypes.users" class="col s12 m3 l3 ">
<div class="card blue-grey darken-1" click.delegate="GetCrewProfiles(user)">
<div class="card-content">
<span class="card-title white-text truncate">${user.firstName} ${user.lastName}</span>
<p>${user.emailAddress || 'mock#email.com'}<br /></p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
in type script
async Activate(user: userListModel[], department: any) {
this.categoryDepartment.forEach(x => {
x.hideDetails = true;
});
this.categoryDepartmentTypes = await this.getTypes(user, department.name);
department.hideDetails = false;
}
async DeActivate(user: userListModel[], department: any) {
department.hideDetails = true;
}
this.categoryDepartment stores my array as follows:
0:{hideDetails:true, name:"Hello"}
1:{hideDetails:false,name:"Test"}
so the above looks as the following image
so now for example if i had to click on the "Hello" plus sign while the details for "Test" is open it would close "Test".
How can i loop through the departments to have it remain open but only when i click the minus then it should close.
I need to pass in the department name to this await this.getTypes(user, department.name); method, so only the item i click on should pass that name in and not all the department names.
does anyone know how i can achieve this?
i tried doing the following
if (this.categoryDepartment.find(x => x.name == department.name)){
this.categoryDepartmentTypes = await this.getTypes(user, department.name);
department.hideDetails = false;
}
but it still does the same thing, it closes on the next item i click on

You hide everything in this loop:
this.categoryDepartment.forEach(x => {
x.hideDetails = true;
});
So you can remove it.

Related

How to look for child elements in a collection

im very new to javascript and probably thats a silly question. What I am trying to achieve is to loop through rows of a "table", get the innerHTML of specific child nodes and multiply them together.
The html looks like this:
<div class="parent">
...
<div class="countChild">
<div class="container">
<span class="count">5</span>
</div>
</div>
<div class="valueChild">
<span class="value">30</span>
</div>
...
</div>
<div class="parent">
...
<div class="countChild">
<div class="container">
<span class="count">2</span>
</div>
</div>
<div class="valueChild">
<span class="value">30</span>
</div>
...
</div>
To be specific: I want to get both the values inside the'countChild' and the 'valueChild'. In this example those are 5 and 30 for the first row and for the second row its 2 and 30. Then perform a muiltiplication.
What I tried to do is to get all the parent nodes and then iterating through them to get the child nodes.
const parents = document.getElementsByClassName('parent');
for(var row in parents) {
var count = row.getElementsByClassName('countChild').lastChild.innerHTML;
var value = row.getElementsByClassName('valueChild').lastChild.innerHTML;
....
}
However the debugger already throws an error when im trying to get the childs. The error message is row.getElemenstByClassName is not a function. I guess the collection cannot be used like this and my understanding of how to use js to get information from the document is wrong.
Edit: This is what the tree looks like
<div class="listing-entry">
<div class="value-container d-none d-md-flex justify-content-end">
<div class="d-flex flex-column">
<div class="d-flex align-items-center justify-content-end">
<span class="font-weight-bold color-primary small text-right text-nowrap">30</span>
</div>
</div>
</div>
<div class="count-container d-none d-md-flex justify-content-end mr-3">
<span class="item-count small text-right">5</span>
</div>
</div>
You should access parents like an array (not really array but you can cast it to one). Btw, I encourage you to use querySelectorAll and querySelector instead of getElementsByClassName
const parents = document.querySelectorAll(".parent")
parents.forEach(function(row) {
var countChild = row.querySelector(".countChild")
var valueChild = row.querySelector(".valueChild")
var count = countChild ? countChild.innerText : 0
var value = valueChild ? valueChild.innerText : 0
console.log(count, value, count * value)
})
<div class="parent">
...
<div class="countChild">
<div class="container">
<span class="count">5</span>
</div>
</div>
<div class="valueChild">
<span class="value">30</span>
</div>
...
</div>
<div class="parent">
...
<div class="countChild">
<div class="container">
<span class="count">2</span>
</div>
</div>
<div class="valueChild">
<span class="value">30</span>
</div>
...
</div>
Edit: I'm using querySelector instead of getElementsByClassName, and checking if child exists before accessing its innerText property.
Edit: here's a function to get all text nodes under a specific node. Then you can combine them and trim the result to get the value you want.
function textNodesUnder(node) {
var all = [];
for (node = node.firstChild; node; node = node.nextSibling) {
if (node.nodeType == 3) {
all.push(node);
} else {
all = all.concat(this.textNodesUnder(node));
}
}
return all;
}
var nodes = textNodesUnder(document.querySelector(".listing-entry"))
var texts = nodes.map(item => item.nodeValue.trim())
console.log(texts)
<div class="listing-entry">
<div class="value-container d-none d-md-flex justify-content-end">
<div class="d-flex flex-column">
<div class="d-flex align-items-center justify-content-end">
<span class="font-weight-bold color-primary small text-right text-nowrap">30</span>
</div>
</div>
</div>
<div class="count-container d-none d-md-flex justify-content-end mr-3">
<span class="item-count small text-right">5</span>
</div>
</div>

How can I apply a saved configuration

I'm working on an admin page and so far I've only worked on PHP and a bit of HTML. But now, I have run into a problem, what I would like to know is how I could apply this configuration automatically through HTML as shown in the following script:
<?php
if(isset($_SESSION['userID']))
{
$sql_result=$db->ExecuteQuery("SELECT `admin_set` FROM `users` WHERE `id`={$_SESSION['userID']}");
if($sql_result!=false)
{
//exit("Ok");
$j_settings=$sql_result->fetch_assoc()['admin_set'];
$_SESSION['admin_set']=$j_settings;
include_once("./classes/admin_settings.class.php");
$settings=new AdminSettings((string)$j_settings);
//echo '<script language="javascript" type="text/javascript" src="path/to/your/file.js"> sidebarColor('.') </script>';
}
}
function GetThemeKey(int $index) : string
{
switch($index)
{
case 0:
return "primary";
case 1:
return "dark";
case 2:
return "info";
case 3:
return "success";
case 4:
return "warning";
case 5:
return "danger";
default:
return "primary";
}
}
?>
<div class="card-body pt-sm-3 pt-0">
<!-- Sidebar Backgrounds -->
<div>
<h6 class="mb-0">Sidebar Colors</h6>
</div>
<a href="javascript:void(0)" class="switch-trigger background-color">
<div class="badge-colors my-2 text-start">
<span class="badge filter bg-gradient-primary<?=($settings->HighlightColor==0? ' active':'')?>" data-color="primary" onclick="sidebarColor(this)"></span>
<span class="badge filter bg-gradient-dark<?=($settings->HighlightColor==1? ' active':'')?>" data-color="dark" onclick="sidebarColor(this)"></span>
<span class="badge filter bg-gradient-info<?=($settings->HighlightColor==2? ' active':'')?>" data-color="info" onclick="sidebarColor(this)"></span>
<span class="badge filter bg-gradient-success<?=($settings->HighlightColor==3? ' active':'')?>" data-color="success" onclick="sidebarColor(this)"></span>
<span class="badge filter bg-gradient-warning<?=($settings->HighlightColor==4? ' active':'')?>" data-color="warning" onclick="sidebarColor(this)"></span>
<span class="badge filter bg-gradient-danger<?=($settings->HighlightColor==5? ' active':'')?>" data-color="danger" onclick="sidebarColor(this)"></span>
</div>
</a>
<!-- Sidenav Type -->
<div class="mt-3">
<h6 class="mb-0">Sidenav Type</h6>
<p class="text-sm">Choose between 2 different sidenav types.</p>
</div>
<div class="d-flex">
<button class="btn bg-gradient-dark px-3 mb-2 active" data-class="bg-gradient-dark" onclick="sidebarType(this)">Dark</button>
<button class="btn bg-gradient-dark px-3 mb-2 ms-2" data-class="bg-transparent" onclick="sidebarType(this)">Transparent</button>
<button class="btn bg-gradient-dark px-3 mb-2 ms-2" data-class="bg-white" onclick="sidebarType(this)">White</button>
</div>
<p class="text-sm d-xl-none d-block mt-2">You can change the sidenav type just on desktop view.</p>
<!-- Navbar Fixed -->
<div class="mt-3 d-flex">
<h6 class="mb-0">Navbar Fixed</h6>
<div class="form-check form-switch ps-0 ms-auto my-auto">
<input class="form-check-input mt-1 ms-auto" type="checkbox" id="navbarFixed" onclick="navbarFixed(this)">
</div>
</div>
<hr class="horizontal dark my-3">
<div class="mt-2 d-flex">
<h6 class="mb-0">Sidenav Mini</h6>
<div class="form-check form-switch ps-0 ms-auto my-auto">
<input class="form-check-input mt-1 ms-auto" type="checkbox" id="navbarMinimize" onclick="navbarMinimize(this)">
</div>
</div>
<hr class="horizontal dark my-3">
<div class="mt-2 d-flex">
<h6 class="mb-0">Light / Dark</h6>
<div class="form-check form-switch ps-0 ms-auto my-auto">
<input class="form-check-input mt-1 ms-auto" type="checkbox" id="dark-version" onclick="darkMode(this)">
</div>
</div>
Currently, what this script does is load a sidebar on the right that is displayed when a button is pressed. Specifically, my problem is that I want it to automatically apply the saved color when this element is loaded as well as make it change color when I click on one of the options and call the sidebarColor(this) function in a JavaScript file:
Pink color image
Blue color image
But if I refresh the page this color goes back to color 0 (pink). In the tests that I am doing, I have a PHP class called Admin Settings where I am setting the color 4 (orange) by default, as you can see in the previous code, I have modified the HTML so that the color is selected in the buttons based on this new default value and when I refresh the page, this button is correctly selected. But this color is not automatically applied on the page:
Problem image
So my question is: Where and how would I have to apply the sidebarColor() function so that it is called automatically when the element loads?
sidebarColor function:
function sidebarColor(a) {
var parent = a.parentElement.children;
var color = a.getAttribute("data-color");
for (var i = 0; i < parent.length; i++) {
parent[i].classList.remove('active');
}
if (!a.classList.contains('active')) {
a.classList.add('active');
} else {
a.classList.remove('active');
}
var sidebar = document.querySelector('.sidenav');
sidebar.setAttribute("data-color", color);
if (document.querySelector('#sidenavCard')) {
var sidenavCard = document.querySelector('#sidenavCard');
let sidenavCardClasses = ['card', 'card-background', 'shadow-none', 'card-background-mask-' + color];
sidenavCard.className = '';
sidenavCard.classList.add(...sidenavCardClasses);
var sidenavCardIcon = document.querySelector('#sidenavCardIcon');
let sidenavCardIconClasses = ['ni', 'ni-diamond', 'text-gradient', 'text-lg', 'top-0', 'text-' + color];
sidenavCardIcon.className = '';
sidenavCardIcon.classList.add(...sidenavCardIconClasses);
}
}

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.

Stop Modal from closing when AngularJS gets update to model

I have a web application that polls a server for data updates (based on a queue of tests being run on the server). I have a bootstrap-modal containing a log if a test fails. When the application receives an update from the server, any open modal is closed but the page is left with the modal screen preventing interaction.
I have tried pausing updates when a modal is opened and resuming them when it is closed, but that doesn't seem to have fixed the problem. I have also tried moving the modals for logs to sit directly under in the DOM. It seems the data changing causes the page to redraw everything inside the controller.
excerpt from index.html
<div class="row" ng-controller="testStatusController as testStatus">
Header Row (static html)
<div class="row test-table-data {{script.colorClass}}" ng-repeat="script in testStatus.scripts">
<div class="col-md-2">
<p class="align-middle"><span class="d-inline d-md-none">Name: </span><span>{{script.tcName}}</span></p>
</div>
<div class="col-md-6 align-middle">
<p><span class="d-inline d-md-none">Description: </span>{{script.description}}</p>
</div>
<div class="col col-md-2 align-middle">
<p><span class="d-inline d-md-none">Run Status: </span>{{script.status}}</p>
</div>
<div class="col col-md-2">
<p>
<span class="icon text-align-middle dripicons-wrong text-danger big" ng-if="!script.pass && script.status=='Complete'" data-toggle="modal" data-target=".{{script.tcName.slice(0,-4)}}"></span>
<span class="d-inline icon dripicons-checkmark text-success big" ng-if="script.pass"></span>
<span class="spinner-border text-primary" ng-if="script.status=='Running'"></span>
<span class="d-inline icon dripicons-clock text-primary big" ng-if="script.status=='Not Run'"></span>
</p>
</div>
<div class="modal fade {{script.tcName.slice(0,-4)}} log" ng-if="!script.pass && script.status == 'Complete'">
<div class="modal-dialog modal-lg" ng-focus="testStatus.pauseUpdates(true)" ng-blur="testStatus.pauseUpdates(false)">
<div class="modal-content log-modal">
<div class="modal-header">
<div class="modal-title text-monospace">
<h4>{{script.tcName}}</h4>
<h6>{{script.description}}</h6>
</div>
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button>
</div>
<div class="modal-body">
<p class="text-monospace"><span ng-repeat="log in testStatus.getLog(script.tcName).lines track by $index">{{log.line}}<br /></span></p>
</div>
</div>
</div>
</div>
</div>
<div class="row test-table-foot">
<div class="col-md-3">
<p>Passed: <span>{{testStatus.passed()}}</span></p>
</div>
<div class="col-md-3">
<p>Failed: <span>{{testStatus.failed()}}</span></p>
</div>
<div class="col-md-3">
<p>Remaining: <span>{{testStatus.remaining()}}</span></p>
</div>
<div class="col-md-3">
<p>Total: <span>{{testStatus.total()}}</span></p>
</div>
</div>
</div>
</div>
the update Function (in main.js called every 5 seconds) Comments indicate what code was removed to help keep things brief.
testList.updateResults = function () {
if (testList.running) {
$http({url:theUrl + "?cmd=json&testRun=default",
method: "GET"
}).then(function(response){
var strRes = response.data.result;
var tmp = angular.fromJson(strRes);
// update trName if bound input not focused
testList.scripts = tmp.tc;
testList.running = false;
angular.forEach(tmp.tc, function(script, index){
if(script.status != "Complete") {
testList.running = true;
script.colorClass = "text-primary";
}
else {
if(script.pass) {
script.colorClass = "text-success";
}
else {
script.colorClass = "text-danger";
if(!(script.tcName.slice(0, -4) in testList.logs)) {
testList.getLog(script.tcName);
}
}
}
});
// update start/stop button
}, function(data){
console.log(data);
});
}
};
I would expect that AngularJS would not update sections of the DOM if the data bound to them was the same, but instead everything in the controller section is redrawn. My next instinct was to separate the logs and the status into different controllers, but I suspect I will have the same problem when a new log gets appended to the list of logs.

jquery each selector and ajax not producing the right results

I am trying to simplify my code but running into issues where it is not working when it place in an each loop.
Here is what I am trying to do:
- html has n parent DIV that generates a report via an AJAX call to pull data
- each div respective to report utilizes a data attribute to define which report to pull
- based no the results of the report, it should populate the html with the respective results.
HTML Code (to simplify, using n = 2):
<div class="widget-box widget-hits card no-border bg-white no-margin" data-widget-report="widget-hits">
<div class="container-xs-height full-height">
<div class="row-xs-height">
<div class="col-xs-height col-top">
<div class="card-header top-left top-right">
<div class="card-title text-black hint-text">
<span class="font-montserrat fs-11 all-caps">Weekly Hits
<i class="far fa-chevron-right p-l-5"></i>
</span>
</div>
<div class="card-controls">
<ul>
<li>
<a data-toggle="refresh" class="card-refresh text-black" href="#">
<i class="far fa-circle fa-xs"></i>
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="row-xs-height ">
<div class="col-xs-height col-top relative">
<div class="row">
<div class="col-sm-6">
<div class="p-l-20 widget-total">
<h3 class="no-margin p-b-5"></h3>
<p class="small hint-text m-t-5">
<span class="label m-r-5">%</span>
</p>
</div>
</div>
<div class="col-sm-6">
</div>
</div>
<div class="widget-chart"></div>
</div>
</div>
</div>
</div>
<div class="widget-box widget-sales card no-border bg-white no-margin" data-widget-report="widget-sales">
<div class="container-xs-height full-height">
<div class="row-xs-height">
<div class="col-xs-height col-top">
<div class="card-header top-left top-right">
<div class="card-title text-black hint-text">
<span class="font-montserrat fs-11 all-caps">Weekly Sales
<i class="far fa-chevron-right p-l-5"></i>
</span>
</div>
<div class="card-controls">
<ul>
<li>
<a data-toggle="refresh" class="card-refresh text-black" href="#">
<i class="far fa-circle fa-xs"></i>
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="row-xs-height ">
<div class="col-xs-height col-top relative">
<div class="row">
<div class="col-sm-6">
<div class="p-l-20 widget-total">
<h3 class="no-margin p-b-5"></h3>
<p class="small hint-text m-t-5">
<span class="label m-r-5">%</span>
</p>
</div>
</div>
<div class="col-sm-6">
</div>
</div>
<div class="widget-chart"></div>
</div>
</div>
</div>
</div>
JS:
$('.widget-box').each(function() {
$widget_report = $(this).data('widget-report');
if ($widget_report !== undefined) {
$.ajax({
type: 'get',
url: '/admin/reports/' + $widget_report,
dataType: 'json',
success:
function(data) {
if (data.success) {
var labelsData = [];
var seriesData = [];
var trend = data.trend * 100;
widget_class = '.' + $widget_report + ' .widget-chart';
console.log(widget_class);
$(this).find('.widget-total h3').text(data.total);
$(this).find('.widget-total p span').text(trend + '%');
trend_span = $(this).find('.widget-total p').children('span');
if(data.trend > 0) {
$(this).find('.widget-total p span.label').addClass('label-success');
$(this).find('.widget-total p').text('Higher');
}
else {
$(this).find('.widget-total p span.label').addClass('label-important');
$(this).find('.widget-total p').text('Lower');
}
$(this).find('.widget-total p').prepend(trend_span);
$.each(data.values, function(key, value){
date = new Date(value.label + 'T00:00:00');
labelsData.push(date.getMonth() + 1 + '/' + date.getDate());
seriesData.push(value.value);
});
chartData = {
labels: labelsData,
series: [seriesData]
}
alert(widget_class);
new Chartist.Bar(widget_class, chartData, {
axisX: {
showGrid: false
},
axisY: {
showGrid: false,
showLabel: false
}
});
}
}
});
}
});
Here are the problems I've encountered:
$(this).find('.widget-total h3').text is not updating the respective DIV group
widget_class for some reason is always returning the last DIV group... even if the last DIV group was returning data.success = false. Ex: above would return widget-sales twice and not widget-hits and then widget-sales.
I am scratching my head on this one... I am able to get this to work without the .each loop and distinctively create one for each... but was hoping to make this universal and allow the control rely on the data-widget-report attribute on the html.
Is this the proper approach?
Any help / direction is appreciated...
Add var in front of widget_class and $widget_report so they are scoped to the function instead of global. I think that will fix your problem. Right now $widget_report gets replaced by the last widget even before the ajax calls are completed.
As for the $(this) not working, you have to assign it to a variable before you make the ajax call.
$('.widget-box').each(function() {
var widgetBoxElement = $(this);
var $widget_report = $(this).data('widget-report');
if ($widget_report !== undefined) {
$.ajax({
type: 'get',
url: '/admin/reports/' + $widget_report,
dataType: 'json',
success:
function(data) {
if (data.success) {
// use widgetBoxElement here instead of $(this)
}
}
});
}
});

Categories

Resources