Switch / Toggle dynamically created components - html

I have two child component in parent component which dynamically created.
My goal is to switch/toggle between this components.
<div *ngFor="let device of devices; let i = index">
<app-standart-view [value]="device.value" (click)="SWICH TO DETAIL-VIEW"></app-standart-view>
<app-detail-view [value]="device.value" (click)="SWICH TO STD-VIEW"></app-detail-view>
</div>
Scenario:
When I click <app-standart-view> it hides itself and displays <app-detail-view>. But only for that clicked component index = i. The rest should don't change.
How can I do this?
Thank you

you can use *ngIf In ts file you will have to define a variable
hide:boolean =true
switchView(){
this.hide=!this.hide
}
and in html
<app-standart-view [value]="device.value" *ngIf= "hide" (click)="switchView()"></app-standart-view>
<app-detail-view [value]="device.value" *ngIf= "!hide" (click)="switchView()"></app-detail-view>

i'd use an *ngIf='device.toShow' directiv in your <app-detail-view>
And obviously setting the proper value of toShow in your methode that you use ater a click event on <app-standart-view>

<div *ngFor="let device of devices; let i = index">
<app-standart-view [value]="device.value" *ngIf="show" (click)="swichView(i)"></app-standart-view>
<app-detail-view [value]="device.value" *ngIf="!show" (click)="swichView(i)"></app-detail-view>
</div>
For specific index you need to pass index and check index
show:boolean = true;
swichView(index:number){
if(index === 1 ){
this.show = !this.show;
}
}

Related

Angular does not update value to View when change in ngAfterViewInit

I have this in view
<span [ngStyle]="{width: optionActiveWindowSize}" class="option-active-window"></span>
<ng-container *ngFor="let op of options; let i = index">
<div [ngClass]="{active: i === activeIndex"}></div>
</ng-container>
Ts Code
ngAfterViewInit(): void {
const activeOption =
this.horizontalOptionRef.nativeElement.getElementsByClassName('active');
this.optionActiveWindowSize = activeOption[0].offsetWidth + 'px';
}
Value of optionActiveWindowSize does not change in the DOM after it was changed in afterViewInit.
Can you please tell me why and how to fix it?
There could be couple of problems.
Make sure, optionActiveWindowSize has a string value with px defined in the end like,
eg. optionActiveWindowSize = '20px'. Then,
[ngStyle]="{width: optionActiveWindowSize}"
should work.
I assume, optionActiveWindowSize has a numeric value like optionActiveWindowSize = 20, then you should change it as,
[ngStyle]="{ 'width.px': optionActiveWindowSize}"
OR
[style.width.px]="optionActiveWindowSize"
Hope, this will help !
NOTE: Forgot to mention as #M Fuat NUROGLU mentioned, span doesn't have width prop. So, you may want to change it to div.
You are binding styles after those styles are already rendered.
no event loop there to track it.
see detailed info from here : https://angular.io/api/core/AfterViewInit
If you want to imply those styles before your page is rendered.
use this :
const activeOption =
this.horizontalOptionRef.nativeElement.getElementsByClassName('active');
this.optionActiveWindowSize = activeOption[0].offsetWidth + 'px';
In your
ngOnInit()
method.
I found the reason. It's because changeDetection is ChangeDetectionStrategy.OnPush.
changeDetection: ChangeDetectionStrategy.OnPush

How to access a dynamically generated id of a HTML element with angular

<div *ngFor="let link of links; let i = index;">
<p>{{link.full}}</p>
<button [id]='i' (click)="copyToClipboard(copy) ">Copy</button>
</div>
How do I access the [id]="i" in my typescript file
Have a look at this demo:
[https://stackblitz.com/edit/angular-irxzeg?file=src%2Fapp%2Fapp.component.ts]
It includes suggestions from #Sam Herrmann
And with Renderer, to add Class
[https://stackblitz.com/edit/angular-mjvvp1?file=src%2Fapp%2Fapp.component.ts]
You don't need to add an id or to change 'manually' the label of button.
You can add a flag to know if link has been copied or not, inside your existing model.
For example:
export type Link = {
full: string,
copied: boolean
}
Then, inside your code you can set this flag to true:
copyToClipboard(link: Link) {
link.copied = true;
...
}
In template, just use this flag to adapt the label of button, and also to set a specific class on button (or elsewhere):
<div *ngFor="let link of links">
<p>{{link.full}}</p>
<button (click)="copyToClipboard(link)" [class.copied]="link.copied">
{{ link.copied ? 'Copied' : 'Copy' }}</button>
</div>
.copied {
background-color: green
}

How to display progress bar while the service is still fetching response

I have a modal and I want a progress bar to be shown while the data for modal display is being fetched by the service call. But in this case, progress bar is being fetched first and then the modal which makes the progress bar to be displayed under the modal. How to fix this ?
this.service.searchMembers(memSearchJson).subscribe((response: any) => {
// some function
}
<modal id="custom-modal-2">
<div class="modal">
</div>
<div id="memberSearchBar" class="class-hide">
<mat-spinner></mat-spinner>
Finding Member IDs..
</div>
</modal>
If document.getElementById("memberSearchBar").className = 'loading-div'; is called before the service call, it throws error as className null. Where should I call this to display progress bar on modal?
I would recommend using *ngIf, as mentioned above. For a better understanding, I add a link to the perfect tutorial.
Loading spinner
For example: in ts:
showSpinner: boolean = true;
ngOnInit() {
this.spinnerShow();
}
if you need show spinner before you get data like from service.
spinnerShow(){
this.workflowService.getData().subscribe(()=>
this.showSpinner = false);
}
And in HTML
<div *ngIf="showSpinner"></div>
Sorry for my terrible english, but i hope this will help you. :)
Instead of doing document.getElementById("memberSearchBar").className = 'loading-div' you can take advantage of Angular's *ngIf and [class.class-name]="expression"
Try something like this instead
displayModalAndSearchMembers(){
// using only isLoading should be sufficient, but I wanted to show how you could use [class.classname]="expression" in the html template as well.
this.isLoading = true;
this.showSpinner = true;
this.displayModal = true;
this.service.searchMembers(memSearchJson).subscribe((response: any) => {
// some function
this.isLoading = false;
this.showSpinner = false;
}
}
<modal id="custom-modal-2">
<div *ngIf="displayModal" class="modal"> <!-- added *ngIf -->
</div>
<div id="memberSearchBar" *ngIf="showSpinner" [class.loading-div]="isLoading">
<mat-spinner></mat-spinner>
Finding Member IDs..
</div>
</modal>
EDIT
You could also use *ngIf="displayModal" on the modal as well, in case you have some display: none on that one as well.
I added some ts code as well. I am not 100% sure what the desired behaviour OP wants, but I am assuming that she wants to show/hide the spinner, and set a class name on it.
This can be simplified by using only *ngIf="isLoading":
<div id="memberSearchBar" class="loading-div" *ngIf="showSpinner">

Displaying hidden Div element at specific index using Angular Material table

I have a mat-table which has the following functionality: -
(1) Add / Remove rows
(2) Add data into a row using various controls (combo-box, text box, etc).
One of the controls (Addition Information) is a text box that when a ? is entered displays a hidden 'div' element that will eventually be used to hold a list of data.
The issue I have is that if I add say 3 rows and enter a ? into the 3rd row the hidden 'div' display on all 3 rows.
I need a way to somehow index each row added to the table and only display the 'div' element of that row.
Unfortunately my knowledge of HTML is limited and I am fairly new to Angular as well.
I have created a StackBlitz solution demoing my issue. demo
HERE'S A WORKING STACKBLITZ I created an array expandedCols : boolean[] = []; that keeps track of the state (expanded or not) of your div, when you add a row, I also add one to that array with default value false, when you put ? I just change the value at index i to true.
<ng-container matColumnDef="additionalCode" class="parent" >
<th mat-header-cell *matHeaderCellDef>Additional Code</th>
<td mat-cell *matCellDef="let element; let i = index" class="parent" >
<mat-form-field class="type">
<input matInput (keyup)="toggleLookup($event, i)" autocomplete="off" (keydown.ArrowDown)="onDown()" placeholder="Enter ?">
</mat-form-field>
<div *ngIf="expandedCols[i] == true" class="child">Yah it expanded
<button (click)="expanded = false">Close</button>
</div>
</td>
</ng-container>
TS:
addRow() {
this.doAddRow();
this.expanded = false;
this.expandedCols.push(false);
}
toggleLookup(event: any, i): void {
if (event.target.value !== '?') {
return;
}
event.target.value = '';
this.expanded = true;
this.expandedCols[i] = true;
}
You should also pay attention to removing rows, do splice, you get the idea.
The proper way to solve your issue is to use row local state - so put your attribute inside column collection (new column is hidden). This way you will have an opportunity to manage expanded property of specific row. Here is your updated stackblitz.

How to set value of *ngFor dynamically in Angular2

I want to set the value of '*ngFor' dynamically.
I have a div like : <div *ngFor="let section of anyArr; let i = index">
I want upper statement like this :
<div *ngFor="let section of {{anyArr}}; let i = index">`
I want 'dummyArr' as anyArr = myArr[0].Control[2] from .ts file.
Is this possible in Angular 2 ???
You could do this using getter in your component like this:
get anyArr() {
return myArr[0].Control[2];
}
Then in template use:
<div *ngFor="let section of anyArr; let i = index">`