How to change display by document.getElementById inAngular - javascript

I have a scenario where I am generating dynamic elements with the data from backend some what like
<tr *ngFor="let data of datas" (click)="display(data.id)">
<div id="'full'+data.id" [style.display]=" active? 'block': 'none'">
</div>
</tr>
My Ts File
export class Component{
active=false;
display(id)
{
document.getElementById(`full${id}`).display="block";
}
}
What I want to do is something like above. I tried something like below but that doesn't work it throws error
Property 'display' does not exist on type 'HTMLInputElement'
import { DOCUMENT } from '#angular/common';
import { Inject } from '#angular/core';
export class Component{
active=false;
constructor(#Inject(DOCUMENT) document) {
}
display(id)
{
document.getElementById(`full${id}`).display="block";
}
}
any suggestions ,how to do this .Thanks

Property 'display' does not exist on type 'HTMLInputElement'
That's because HTML elements do not have a property display. What you're looking for is:
document.getElementById(`full${id}`).style.display='block';

Rather than directly manipulating the DOM, the more Angular way of doing this would be to track the visibility state of each row and drive visibility through NgIf
NgIf: A structural directive that conditionally includes a template based on the value of an expression coerced to Boolean. When the expression evaluates to true, Angular renders the template provided in a then clause, and when false or null, Angular renders the template provided in an optional else clause. The default template for the else clause is blank.
Here is an example with a single boolean driving the toggle of a single div, but you could do something similar with a map on your data.
#Component({
selector: 'ng-if-simple',
template: `
<button (click)="show = !show">{{show ? 'hide' : 'show'}}</button>
show = {{show}}
<br>
<div *ngIf="show">Text to show</div>
`
})
export class NgIfSimple {
show: boolean = true;
}

I've solve problem like this:
let fullId = document.getElementById(`full${id}`) as HTMLElement;
fullId.style.display='block';

Related

DOM undefined when the element is inside a *ngIf container

my problem is that i cant acces to a particular DOM element and their properties when the element is a children of a *ngIf container.
My case is: I a have a mat-table inside a div, the div have the *ngIf directive, and then i try to call mytable.renderRows() when my datasource changed, but i got an undefined value. I see this problem happens when the element is inside the ngIf directive, in other case i can access without problem.
<div *ngIf="!hasPermission" >
<table mat-table #myTable [dataSource]="myDataSource">
and i have this on the .ts file:
export class MyComponent {
hasPermission = true
#ViewChild('myTable',{static:true}) myTable: MatTable<any>;
constructor(){
if(checkSomething == true){
this.hasPermission = false
this.myFunctionIfNotHavePermsissions()
}
}
myFunctionIfNotHavePermsissions(){
this.myTable.renderRows();
// console.log(this.myTable); *NOTE: This output: undefined*
}
}
For the moment, I fixed this problem, hidding the div using css, but i think this not the best solution, thanks in advance for your comments.
<div *ngIf="!hasPermission" >
to
<div [ngClass]="{ 'nodisplay': !hasPermission}" >
.nodisplay{display:none!important;}
I may don't know the actual reason behind it, but I think angular need a little time to first render whatever inside the ngIf element and then make available to DOM.
You can fix your issue by changing static to false here
#ViewChild('myTable', {static: false}) myTable: MatTable<any>;
and calling this.myFunctionIfNotHavePermsissions() inside a setTimeout
constructor(){
if(checkSomething == true){
this.hasPermission = false
setTimeout(()=> this.myFunctionIfNotHavePermsissions());
}
}
in constructor you the template is not ready and mat-table is not rendered.
add your logic in ngOnInit
export class MyComponent implements OnInit {
hasPermission = true
#ViewChild('myTable',{static:true}) myTable: MatTable<any>;
constructor() {}
ngOnInit() {
if(checkSomething == true){
this.hasPermission = false
this.myFunctionIfNotHavePermsissions()
}
}
myFunctionIfNotHavePermsissions(){
this.myTable.renderRows();
// console.log(this.myTable); *NOTE: This output: undefined*
}
}

I want to replace text in component "x" by clicking a button from component "y"

I got two components that i want to connect. In component "x" i got some text in the template file and in component "y" i got a button that i want to click to replace/change the text in component "x".
this is "x" text i want to change:
<p> Text i want to replace </p>
this is "y" component.ts text i want to replace with:
changeTheText: string = "Text i want to change to";
showMyContainer2: boolean = false;
clearMe(){
this.showMyContainer2 = true;
this.UIS.clearStorage();
}
this is "y" component.template:
<button id="clearBtn" (click)="clearMe()">Change the text button</button>
<div *ngIf="showMyContainer2">
{{changeTheText}}
</div>
You can do this by using EventEmitters
https://angular.io/api/core/EventEmitter
Is x a direct child of y? Meaning is the HTML like this?
<y>
<x></x>
</y>
If so, you can use #Input() properties
In x component.ts, do this:
// import Output and EventEmitter from '#angular/core`;
#Input text: string;
And I assume the HTML is:
<p>{{ text }}</p>
Then in y.component.ts, do:
clearMe(){
this.showMyContainer2 = true;
this.UIS.clearStorage();
this.changeTheText = 'New Text you want'
}
And in y.html, do:
<div class="y">
<x [text]="changeTheText"></x>
</div>
You can possibly use EventEmitters like Mamosek mentioned but it depends on the heirarchy of x and y. Are they parent => child or child => parent.
If they don't have parent child relationship, you have to create a middle man Service that has a BehaviorSubject and both x and y have to inject this Service and communicate through that BehaviorSubject by doing .next to push a new value and .subscribe to listen to values.
====================== Edit ================================
Does it make sense for the text to live in component Z?
Component Z.ts
textToChange = 'Change this text';
changeTheText() {
this.textToChange = 'new text';
}
Component Z.html
<div class="z">
// feed the text down to the x component
<x [text]="textToChange"></x>
// listen to the textChanged event from component Y and every time it happens call the function changeTheText
<y (textChanged)="changeTheText()"></y>
</div>
Component X.ts
// take input of text from parent's location
#Input() text: string;
Component X.html
<div>{{ text }}</div>
Component Y.ts
#Output textChanged: EventEmitter<void> = new EventEmitter();
changeTheText() {
// emit the event so the text can change in component Z
this.textChanged.emit();
}
Component Y.html
<button (click)="changeTheText()">Change the text</button>
I freestyled all of that so there might be some errors but you get the idea.
If the text cannot live in component Z, you will have to have a centralized approach like I mentioned before this edit.
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable({
providedIn: 'root',
})
export class TextChangeService {
text = new BehaviorSubject('initial value of text');
constructor() { }
}
component X.ts (The HTML's for both components remain the same)
text: string;
constructor(textService: TextService){ }
ngOnInit() {
this.text.subscribe(text => this.text = text);
}
component Y.ts
constructor(textService: TextService){ }
changeTheText() {
this.textService.next('New text');
}
This last approach I showed you, I don't recommend because it turns into convoluted code the more you build on it. I suggest you learn the Redux pattern and use NGRX.

ngAfterViewInit not fired within ng-content

The ngAfterViewInit lifecycle hook is not being called for a Component that is transcluded into another component using <ng-content> like this:
<app-container [showContent]="showContentContainer">
<app-input></app-input>
</app-container>
However, it works fine without <ng-content>:
<app-input *ngIf="showContent"></app-input>
The container component is defined as:
#Component({
selector: 'app-container',
template: `
<ng-container *ngIf="showContent">
<ng-content></ng-content>
</ng-container>
`
})
export class AppContainerComponent {
#Input()
showContentContainer = false;
#Input()
showContent = false;
}
The input component is defined as:
#Component({
selector: 'app-input',
template: `<input type=text #inputElem />`
})
export class AppInputComponent implements AfterViewInit {
#ViewChild("inputElem")
inputElem: ElementRef<HTMLInputElement>;
ngAfterViewInit() {
console.info("ngAfterViewInit fired!");
this.inputElem.nativeElement.focus();
}
}
See a live example here: https://stackblitz.com/edit/angular-playground-vqhjuh
There are two issues at hand here:
Child components are instantiated along with the parent component, not when <ng-content> is instantiated to include them. (see https://github.com/angular/angular/issues/13921)
ngAfterViewInit does not indicate that the component has been attached to the DOM, just that the view has been instantiated. (see https://github.com/angular/angular/issues/13925)
In this case, the problem can be solved be addressing either one of them:
The container directive can be re-written as a structural directive that instantiates the content only when appropriate. See an example here: https://stackblitz.com/edit/angular-playground-mrcokp
The input directive can be re-written to react to actually being attached to the DOM. One way to do this is by writing a directive to handle this. See an example here: https://stackblitz.com/edit/angular-playground-sthnbr
In many cases, it's probably appropriate to do both.
However, option #2 is quite easy to handle with a custom directive, which I will include here for completeness:
#Directive({
selector: "[attachedToDom],[detachedFromDom]"
})
export class AppDomAttachedDirective implements AfterViewChecked, OnDestroy {
#Output()
attachedToDom = new EventEmitter();
#Output()
detachedFromDom = new EventEmitter();
constructor(
private elemRef: ElementRef<HTMLElement>
) { }
private wasAttached = false;
private update() {
const isAttached = document.contains(this.elemRef.nativeElement);
if (this.wasAttached !== isAttached) {
this.wasAttached = isAttached;
if (isAttached) {
this.attachedToDom.emit();
} else {
this.detachedFromDom.emit();
}
}
}
ngAfterViewChecked() { this.update(); }
ngOnDestroy() { this.update(); }
}
It can be used like this:
<input type=text
(attachedToDom)="inputElem.focus()"
#inputElem />
If you check the console of your stackblitz, you see that the event is fired before pressing any button.
I can only think of that everything projected as will be initialized/constructed where you declare it.
So in your example right between these lines
<app-container [showContent]="showContentContainer">
{{test()}}
<app-input></app-input>
</app-container>
If you add a test function inside the app-container, it will get called immediatly. So <app-input> will also be constructed immediatly. Since ngAfterVieWInit will only get called once (https://angular.io/guide/lifecycle-hooks), this is where it will be called already.
adding the following inside AppInputComponent is a bit weird however
ngOnDestroy() {
console.log('destroy')
}
the component will actually be destroyed right away and never initialized again (add constructor or onInit log to check).

Angular 6 Dropdown inside Material Tabs Error

I have an input drop-down component which is used in multiple places in the same app but in different tabs.
The issue I am facing is when I select a value from the drop-down in Tab 1 and an API call is done with the value, the same component in Tab 2 also does that with the selected value in Tab1.
How do I fix this as I am subscribing to the same service in the different tabs?
<ng-select
[items]="Groups"
[virtualScroll]="true"
bindLabel="bg_desc"
bindValue="bg_desc"
placeholder="Groups"
[(ngModel)]="selectedGroup"
[clearable]="false"
(change)="selectGroups()">
<ng-template
ng-notfound-tmp
let-searchTerm="searchTerm">
<div class="ng-option disabled">
No data found for "{{searchTerm}}"
</div>
</ng-template>
<ng-template
ng-option-tmp
let-item="item"
let-search="searchTerm">
<div
[ngOptionHighlight]="search"
class="text-uppercase">
{{item.bg_desc}}
</div>
</ng-template>
</ng-select>
This is in my component:
selectGroups() {
this._data.changeGroup(this.selectedGroup);
}
This is my service:
changeGroup(bg: string) {
this.changeGroupData.next(bg);
}
private changeGroupData = new BehaviorSubject<string>('');
currentChangeGroupData = this.changeGroupData.asObservable();
This is my stackbliz example: https://stackblitz.com/edit/angular-1oucud
I want individual calls on these tabs. Should I create three instances of same component with different names to achieve this?
Think about the architecture of your program? Should DropDownComponent really be updating a service after a model change or is more like a more specific input control and any binding or application logic should occur outside of it?
It seems to me that the second case is more appropriate, especially if you feel the need to reuse it. You can easily modify the DropDownComponent to have an Input and Output and have the outer component bind to it. Or you can go the extra mile and have your component extend NgModelAccessor, so you can use it properly in forms.
I'll give an example of the simpler approach below.
DropDownComponent is to changed to be completely standalone. It has an input and output that other components will bind to.
AppComponent has a model and the properties of the model are bound to instances of dropdown in the view. For no particular reason I also bound to the change event just to show you what happens. It really isn't necessary as doing the banana-in-a-box syntax will cause the Output to be bound to by convention - the Output having the same name as the input with Change appended to the end.
DropDownComponent.ts
export class DropdownComponent {
colors = colors;
#Input() selectedColor;
#Output() selectedColorChange = new EventEmitter<string>();
changeColor(e) {
this.selectedColorChange.emit(this.selectedColor);
}
}
AppComponent.ts
declare type ColorType = { color: string, value: string };
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
colors: { first?: ColorType, second?: ColorType, third?: ColorType } = {};
doSomething(colorKey: string) {
console.log(`The color changed was ${colorKey} with a value of ${this.colors[colorKey].value}.`)
}
}
AppComponent.html
<mat-tab-group>
<mat-tab label="First">
<dropdown [(selectedColor)]="colors.first" (selectedColorChange)="doSomething('first')"></dropdown>
<p>Selected Color is {{colors.first?.color}}</p>
</mat-tab>
<mat-tab label="Second">
<dropdown [(selectedColor)]="colors.second" (selectedColorChange)="doSomething('second')"></dropdown>
<p>Selected Color is {{colors.second?.color}}</p>
</mat-tab>
<mat-tab label="Third">
<dropdown [(selectedColor)]="colors.third" (selectedColorChange)="doSomething('third')"></dropdown>
<p>Selected Color is {{colors.third?.color}}</p>
</mat-tab>
</mat-tab-group>
You just need to use output to communicate to outer component. Thats it
https://stackblitz.com/edit/angular-1oucud

Angular 2 ngClass is not Updating on View

I'm having some difficulty getting ngClass to update my view, even though the back end is updating properly. My controller is as follows:
#Component({
selector: 'show-hide-table',
template: '
<table>
<thead>
<tr>
<th (click)="toggleHidden()">
<div>Show/Hide</div>
</th>
<th [ngClass]="{\'hidden\':isHidden}">
<div>Div is Shown</div>
</th>
</tr>
</thead>
</table>',
styleUrls: ['show-hide-table.component.css']
})
export class ShowHideTable implements OnInit, OnChanges, DoCheck {
public isHidden: boolean = false;
public toggleHidden() {
this.isHidden = !this.isHidden;
this.log.debug('Hidden', this.isHidden);
}
ngOnChanges(changes: {[propKey: string]: SimpleChange}) {
this.log.debug('Grid Changes');
}
ngDoCheck() {
this.log.debug('Grid Check');
}
}
'hidden' is a css class with display: none. When I default isHidden to true, the header is hidden, and when false, it is shown, so I believe the class is working. I simplified it for sake of asking, but this should accurately reflect my code. I click the header and I get a log showing the isHidden variable toggling, but the hidden class isn't changing to suit. Also, neither ngOnChanges nor ngDoCheck is firing when the clickable header is clicked.
You've got too many pairs of single quotes. The quotes around 'hidden' actually end the first string and start another one. You've got to escape those quotes.
<th [ngClass]="{\'hidden\':isHidden}">
I can't test this but it should work. If for some reason it doesn't you could try using Javascript encoding of the quotes. Replace the single quote with \x27.

Categories