Show specific Div inside ngFor - javascript

I have a bunch of cards using ngFor with user's information. There is a button inside the card that will display travel information for that specific user.
I created a div with the initial state display: none. I thought about using ngStyle to bind the display: block property upon click, but the problem is that all the divs are getting displayed and not only the specific one I need.
Any suggestions on how I can do this?
html
<div class="col s12 m6 l4" *ngFor="let student of students; let i = index">
<i class="material-icons" (click)="showBox()">access_alarm</i>
<div class="travel-info" [ngStyle]="{'display': boxDisplay == true ? 'block' : 'none'}" >
<app-travel-info ></app-travel-info>
</div>
</div>
ts
boxDisplay = false;
showBox() {
this.boxDisplay = true;
}

The problem is you only have one variable that is set for all of them.
You will want to refactor showBox to be something like this.
TS
this.boxDisplay = this.students.map(s => false);
showBox(index) {
this.boxDisplay[index] = true;
}
HTML
<div class="col s12 m6 l4" *ngFor="let student of students; let i = index">
<i class="material-icons" (click)="showBox(i)">access_alarm</i>
<div class="travel-info" [ngStyle]="{'display': boxDisplay[i] == true ? 'block' : 'none'}" >
<app-travel-info ></app-travel-info>
</div>
</div>
If it were me I would use ngIf instead of the ngStyle.
<div class="col s12 m6 l4" *ngFor="let student of students; let i = index">
<i class="material-icons" (click)="showBox(i)">access_alarm</i>
<div class="travel-info" *ngIf="boxDisplay[i]" >
<app-travel-info ></app-travel-info>
</div>
</div>

You need to have a property display with each elements of the students array and then enable/disable based on the index,
<div class="col s12 m6 l4" *ngFor="let student of students; let i = index">
<i class="material-icons" (click)="showBox(student)">access_alarm</i>
<div class="travel-info" [ngStyle]"{'display': student.boxDisplay == true ? 'block' : 'none'}" >
<app-travel-info ></app-travel-info>
</div>
</div>
showBox(student:any) {
student.boxDisplay = true;
}

You can use #ViewChildren. I give you this example:
Your HTML:
<div class="student-container" *ngFor="let s of students; let i = index">
<span>Name: {{s.name}} - Index: {{i}}</span>
<div class="student-box" #boxes>
<span>Hey! I'm opened.</span>
<span>My Secret Number is: {{s.secretNumber}}</span>
</div>
<button (click)="toggleBox(i)">Click Me!</button>
</div>
Your Component:
import { Component, ViewChildren, QueryList, ElementRef } from "#angular/core";
#Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
#ViewChildren("boxes") private boxes: QueryList<ElementRef>;
students = [
{ name: "Peter", secretNumber: "88" },
{ name: "Laura", secretNumber: "45" },
{ name: "Paul", secretNumber: "13" }
];
toggleBox(index) {
let nativeElement = this.boxes.toArray()[index].nativeElement;
nativeElement.style.display =
nativeElement.style.display === "none" || !nativeElement.style.display
? "block"
: "none";
}
}
Your CSS:
.student-container {
background: lightblue;
margin: 10px;
padding: 10px;
}
.student-box {
display: none;
}
https://codesandbox.io/s/zwqoxp583x

Related

How can I make the first item in my *ngFor active by default (i.e. border-color of active item should be differnt from other items by deafult)

Currently, I'm working on the Angular 8 Application. And I wanted to
make my first item of loop active by default. Active in the sense that its
border-color got changed. To further clarify my question I've also
attached a picture. In the below picture you can see that the first
category is 'Chicken'. So it must be selected by default. Any Help would be very much appreciated.
Thank You!
My HTML
<div class="category-slider swiper-container">
<div class="swiper-wrapper">
<div class="swiper-slide" style="margin-right: 100px;" *ngFor="let i = index; let category of brand_Categories">
<a href="javascript:void(0);" (click)="getSubCategories(category.CategoryId,category?.CategoryTitle)"
class="categories">
<div>
<div class="icon text-custom-white bg-light-green" id="myDIV" (click)="toggle(i)" [ngClass]="(selectedID == i) ? 'selected' : 'not-selected' ">
<img
src="{{category?.CategoryImage || 'https://giftclubimagestorage.blob.core.windows.net/images/biryani.jpg'}}"
class="rounded-circle"
alt="{{category?.CategoryTitle || ''}}" class="imgrr">
</div>
</div>
<span class="text-light-black cat-name">{{category?.CategoryTitle || ''}}</span>
</a>
</div>
</div>
Here is an example I tried out.
html
<div fxLayout="row" fxLayoutGap="20px">
<div *ngFor="let item of items; let i = index;">
<div class="item" [ngClass]="{'selected-item': selectedID == i}" (click)="selectItem(i)">{{item}}</div>
</div>
</div>
css
.item {
padding: 10px;
border: solid 1px grey;
}
.selected-item {
border: solid 2px green;
}
ts
public items = ['Item1', 'Item2', 'Item3', 'Item4'];
public selectedID = 0;
selectItem(i){
this.selectedID = i;
}
How about using first variable provided in *ngFor context?
<div *ngFor="let category of brand_Categories; let i = index; let first = first">
<div [ngClass]="(selectedID == i || !selectedID && first) ? 'selected' : 'not-selected' ">
</div>
Optionally
<div *ngFor="let category of brand_Categories; let i = index;>
<div [ngClass]="(selectedID == i || !selectedID && i == 0) ? 'selected' : 'not-selected' ">
</div>
in addition consider setting the selectedId to zere in the Component.

How to achieve responsive api in Angular?

I need to achieve the responsive page as shown below in the image.
I have created something like this. But my mat-card gets cut or hidden in the tile , when I reduce the browser size. I managed to create this:
Where am I going wrong? Below is my code.
header.component.html
<div fxLayout="column" fxLayout.xs="column">
<mat-toolbar color="primary" >
<button mat-button (click)="side_nav.open()">
<mat-icon>menu</mat-icon>
</button>
<img src="../assets/weather_images/logo.png" style="width: 35px;">
<span> Minimus </span>
<span class="header_text"></span>
<span>TODAY</span>
<span class="header_text"></span>
<span>light</span>
<mat-slide-toggle [checked]="isDarkTheme | async" (change)="toggleDarkTheme($event.checked)"></mat-slide-toggle>
<span>dark</span>
</mat-toolbar>
<mat-sidenav-container [ngClass]="{'dark-theme': isDarkTheme | async}" >
<mat-sidenav #side_nav style="height: 100%;width: 200px;">
<br><br><div class="m"><span routerLink='' (click)="side_nav.close();" > Home </span></div><br><br><br>
<div class="m"><span routerLink='/search-cities' (click)="side_nav.close();">Add City</span></div>
</mat-sidenav>
<mat-sidenav-content><br>
<div [#fade] = "prepareRoute(o)" [#flyInOut] = "prepareRoute(o)">
<router-outlet #o="outlet"></router-outlet>
</div>
</mat-sidenav-content>
</mat-sidenav-container>
</div>
addciyt .component .html
<div>
<mat-grid-list [cols]="breakpoint" (window:resize)="onResize($event)" gutterSize="30px" [ngClass]="{'dark-theme': isDarkTheme | async}">
<mat-grid-tile>
<mat-card class="card_style" [ngClass]="{'dark-theme': isDarkTheme | async}" routerLink="/search-cities" #slideInOut>
<mat-card-title class="card_title" [ngClass]="{'dark-theme': isDarkTheme | async}">ADD CITY</mat-card-title><br>
<img src="../assets/weather_images/plus.png" alt="Plus image" width="150px" class="image_style">
<img mat-card-image src="../assets/weather_images/1.png" height="260px">
</mat-card>
</mat-grid-tile>
<mat-grid-tile *ngFor = "let v of value">
<mat-card class="card_style" [routerLink]="['/details',v.city_name]" #fade>
<mat-card-title class="card_title">
{{value ? (v.city_name).toUpperCase() : ''}}
</mat-card-title><br>
<div *ngIf = "v.des === 'Clouds'" style="text-align: center;">
<img src= "../assets/weather_images/cloudy.png" alt="cloudy" class="image">
</div>
<div *ngIf = "v.des === 'Rain'" style="text-align: center;">
<img src= "../assets/weather_images/raining.png" alt="raining" class="image">
</div>
<div *ngIf = "v.des === 'Clear'" style="text-align: center;">
<img src= "../assets/weather_images/sunny.png" alt="clear" class="image">
</div>
<div *ngIf = "v.des === 'Strom'" style="text-align: center;">
<img src= "../assets/weather_images/strom.png" alt="strom" class="image">
</div>
<div *ngIf = "v.des === 'Snow'" style="text-align: center;">
<img src= "../assets/weather_images/snowing.png" alt="strom" class="image">
</div>
<mat-card-content>
<div>
<p class="temp">{{value ? (v.temp - 273.15).toFixed(2) : ''}}℃</p>
</div>
<p class="main">{{value ? (v.des).toUpperCase() : ''}}</p>
<p style="margin-left: 35px; font-size: 16px;">Temp_min Temp_max</p>
<p class="temp_min">{{value ? (v.temp_min - 273.15).toFixed(2) : ''}}℃<span class="temp_max">{{value ? (v.temp_max - 273.15).toFixed(2) : ''}}℃</span></p>
</mat-card-content>
</mat-card>
</mat-grid-tile>
</mat-grid-list>
</div>
addcity.component.ts
import { Component, OnInit } from "#angular/core";
import { Observable } from "rxjs";
import { ThemeService } from "../services/theme.service";
import { WeatherService } from "../services/weather.service";
import { AngularFireDatabase } from "angularfire2/database";
import { slideInOut, fade } from "../animation";
import { FormControl } from '#angular/forms';
import { map, startWith } from 'rxjs/operators';
#Component({
selector: "app-addcity",
templateUrl: "./addcity.component.html",
styleUrls: ["./addcity.component.scss"],
animations: [
slideInOut,
fade
]
})
export class AddcityComponent implements OnInit {
value: Array<any> = [];
cities: Array<any>;
isDarkTheme: Observable<boolean>;
myControl = new FormControl();
options: string[] = ['One', 'Two', 'Three'];
filteredOptions: Observable<string[]>;
control = new FormControl();
streets: string[] = ['Champs-Élysées', 'Lombard Street', 'Abbey Road', 'Fifth Avenue', 's','d','f','y','u','h','l'];
filteredStreets: Observable<string[]>;
name = '';
breakpoint:number;
constructor(
private themeService: ThemeService,
private db: AngularFireDatabase,
private weatherService: WeatherService
) {
db.list("/user/cities")
.valueChanges()
.subscribe((cities) => {
this.cities = cities;
for (let c of cities) {
this.weatherService.getcities(c['name']).subscribe(value => {
const temp = value["main"].temp;
const temp_min = value["main"].temp_min;
const temp_max = value["main"].temp_max;
const des = value["weather"][0].main;
const city_name = c['name'];
const a = {
temp,
temp_min,
temp_max,
des,
city_name
};
this.value.push(a);
});
}
});
console.log(this.name);
}
ngOnInit() {
this.isDarkTheme = this.themeService.isDarkTheme;
this.breakpoint = (window.innerWidth <= 400) ? 1 : 2;
}
onResize(event) {
this.breakpoint = (event.target.innerWidth <= 400) ? 1 : 2;
}
}
I tried a lot but nothing seems to work.
#DHEERAJ to make your page responsive, you can use different CSS techniques, you can also use the #angular/flex-layout in combination with Angular Material
Find here the Details of #angular/flex-Layout and FlexLayout Examples

Insert dynamically create component after clicked row

I am working on solution in which i want to append dynamically created component after clicked row
I have table consist rows with action button on click of which i will call angular function and load component after that row.
Here is table code
<div class="row" *ngFor="let rData of reportData; let i = index;" >
<div class="col" >
<button class="btn btn-sm" (click)="loadChildComponent()">+</button>
</div>
<div class="col">Name</div>
<div class="col">Description</div>
<ng-template #dynamic></ng-template>
</div>
Code for dynamic components
Service.ts
import { DynamicComponent } from './dynamic.component'
#Injectable()
export class Service {
factoryResolver;
rootViewContainer;
constructor(#Inject(ComponentFactoryResolver) factoryResolver) {
this.factoryResolver = factoryResolver
}
setRootViewContainerRef(viewContainerRef) {
this.rootViewContainer = viewContainerRef
}
addDynamicComponent() {
const factory = this.factoryResolver
.resolveComponentFactory(DynamicComponent)
const component = factory
.create(this.rootViewContainer.parentInjector)
this.rootViewContainer.insert(component.hostView)
}
}
Here is component file.
dynamic.component.ts
import { Component } from '#angular/core'
#Component({
selector: 'dynamic-component',
template: `<div class="row" >
<div class="col">Data</div>
<div class="col">Data</div>
<div class="col">Data</div>
<div class="col">Data</div>
<div class="col">Data</div>
<div class="col">Data</div>
<div class="col">Data</div>
<ng-template #dynamic></ng-template>
</div>`
})
export class DynamicComponent { }
Functions used to render dynamic component
#ViewChild('dynamic', {
read: ViewContainerRef
}) viewContainerRef: ViewContainerRef
loadChildComponent() {
this.service.setRootViewContainerRef(this.viewContainerRef)
this.service.addDynamicComponent()
}
Right now its appending in same div for any of rows
i would like to append it after clicked row
Please help..
The ng-template in Angular acts like a ghost element i.e. it is never displayed directly. Check this link.
Update:
You are having the template inserted with first row always because you have used #ViewChild. #ViewChild looks for the first element in the template.
Try using #ViewChildren instead.
Refer the following changes:
<ng-container *ngFor="let rData of reportData; let i = index;">
<div class="row">
<div class="col" >
<button class="btn btn-sm" (click)="loadChildComponent(i)">+</button>
</div>
<div class="col">Name</div>
<div class="col">Description</div>
</div>
<div class="row">
<ng-template #dynamic></ng-template>
</div>
</ng-container>
JS changes:
#ViewChildren('dynamic', { read: ViewContainerRef }) viewContainerRef: QueryList<ViewContainerRef>
loadChildComponent(index) {
this.service.setRootViewContainerRef(this.viewContainerRef.toArray()[index])
this.service.addDynamicComponent()
}
Hope this helps :)

Angular recursive component prevent sharing properties

I have a recursive component to create a dynamically updating tree structure which can be collapsed. However, it seems that the active variable which determines the state is shared with child components. Is there any way to prevent this?
#Component({
selector: 'app-og-span',
templateUrl: './og-span.component.html',
styleUrls: ['./og-span.component.css']
})
export class OgSpanComponent {
#Input() comments;
private isActive = true;
toggleActive($event) {
console.log('wtf');
this.isActive = !this.isActive;
$event.stopPropagation();
return false;
}
}
And the HTML is:
<div class="ui accordion" style="padding-left: 15px" *ngFor="let comment of comments">
<div class="title" [ngClass]="{'active': isActive}" (click)="toggleActive($event);$event.stopPropagation()">
<i class="dropdown icon"></i>
{{comment.text}}
</div>
<div class="content" [ngClass]="{'active': isActive}">
<app-og-span [comments]="comment.comments" *ngIf="comment.comments"></app-og-span>
</div>
</div>
I know the event is fired only once from logs. There was a case of variable scopes with AngularJS (1.x) but I cannot find to find the corresponding in 6 (2+)
It seems I was wrong changing the code to following worked, because I placed the *ngFor wrongly at the top to get the view I wanted but not semantically correct one (and I assumed the AngularJS 1.x case of variable scopes):
<div class="ui styled accordion" style="padding-left: 10px">
<div class="title" (click)="click($event)" [ngClass]="{'active': active}">
<i class="dropdown icon"></i>
{{ node.name }}
</div>
<div class="content" [ngClass]="{'active': active}">
<span *ngFor="let node of node.children">
<app-og-span [node]="node"></app-og-span>
</span>
</div>
</div>
JS:
#Component({
selector: 'app-og-span',
templateUrl: './og-span.component.html',
styleUrls: ['./og-span.component.css']
})
export class OgSpanComponent {
#Input() node;
private active = true;
click($event) {
this.active = !this.active;
$event.stopPropagation();
}
}

How to change CSS class of a column based on the number of items in a JavaScript array?

I want to change a CSS class like col-md-12 based on the number of items in an array. I am pushing items to an array.
For example, when there is one item in the array, the class must be col-md-12, but when there are more items, the class must be col-md-6. There should be two columns with the class col-md-6.
<div class="row footer" *ngIf="model.component.length!=undefined">
<div class="col-md-{{getNoOfCols(model.component.length)}}" *ngFor="let item of model.component" style="margin-top:-25px;">
<all-component [model]="item" (Click1)="onComponentClick($event)" [selectedId]="targetBuilderId"></all-component>
</div>
</div>
You can ng-class
Example :
ng-class="{
'col-md-12' : (getNoOfCols(model.component.length) === 1),
'col-md-6' : (getNoOfCols(model.component.length) > 1)
}"
You can use ngIf to show divs according to your array length
<div class="row footer" *ngIf="model.component !=undefined">
<div *ngIf="model.component.length < 2; else colSix">
<div class="col-md-12">Code for col-md-12</div>
</div>
<ng-template #colSix>
<div class="col-md-6">
<div class="col-md-6">
</ng-template>
</div>
try to avoid calling a function inside your template and use ngClass to switch between the classes
import {Component, OnInit} from '#angular/core';
#Component({
selector: 'sample-app',
template: `
<div class="row footer">
<div *ngFor="let item of arr"
[ngClass]='{"col-md-12": arr.length == 1, "col-md-6": arr.length > 1}'
>
{{item}} hello
</div>
</div>
`
})
export class AppComponent implements OnInit{
arr: any[];
ngOnInit() {
this.arr = ['item1', 'item2', 'item3'];
}
}

Categories