I have the below OverlayComponent that's serving as a processing spinner during async calls. The overlay pops up without issue but when I try and pass a message to it, the message doesn't stick.
Child Component
import {Component, OnInit} from '#angular/core';
import {OverlayComponent} from "../../shared/app.mysite.overlay.component";
#Component({
moduleId: module.id,
selector: 'tracker-component',
templateUrl: '/public/app/templates/pages/racker/mysite.tracker.component.html',
styleUrls: ['../../../scss/pages/tracker/tracker.css'],
providers: [OverlayComponent]
})
export class TrackerComponent implements OnInit{
constructor(private overlayComponent: OverlayComponent) {
}
ngOnInit(): void {
this.overlayComponent.showOverlay("Testing 123"); //<-- shows overlay but doesn't show the message in the overlay
}
}
Child Component HTML
<div id="TrackerContainer">
<div class="col-lg-12" class="container">
<div class="jumbotron jumbotron-header">
<div>
<div id="pageTitle">Tracker</div>
</div>
</div>
<div class="content-container container">
<div *ngFor="let item of tracker.activeMenu.items">
<card-component [item]="item"></card-component>
</div>
</div>
</div>
</div>
<overlay-component [message]="overlayMessage"></overlay-component>
OverlayComponent.ts
import { Component } from '#angular/core';
#Component({
selector: 'overlay-component',
templateUrl: '/public/app/templates/shared/mysite.overlay.component.html',
styleUrls: ['../../app/scss/shared/overlay.css']
})
export class OverlayComponent {
message: string;
//message: string = 'testing...'; <-- this updated the message just fine
showOverlay(msg: string) {
this.message = msg; // <-- the right value comes through in the msg variable but doesn't update in the page
$('.overlay-component-container').show();
}
hideOverlay() {
$('.overlay-component-container').hide();
this.message = '';
}
}
OverlayComponent.html
<div class="overlay-component-container">
<div class="overlay-component">
<div class="overlay-message">{{message}}</div>
<div>
<i class="fa fa-spinner fa-spin" aria-hidden="true"></i>
</div>
</div>
</div>
The issue could be caused by a few things:
You don't need this here, this is just for services, not for components
providers: [OverlayComponent]
This isn't the way to get a reference to another component, you may not have the correct instance.
constructor(private overlayComponent: OverlayComponent)
Is <overlay-component> actually inside the HTML of the Tracker component?
Also where is the overlayMessage property on the tracker component? (as used below)
<overlay-component [message]="overlayMessage"></overlay-component>
Just ensure that the over-component is inside the tracker component, remove the this.message = msg; from showOverlay() and use two-way data binding to change the message.
I.E <overlay-component [message]="overlayMessage"></overlay-component>
If you add overlayMessage: string; to your Tracker component (or parent component of the overlay) Then all you need to do is update it in your parent component and your overlay component will pick up the changes.
Related
//This is my html file ,Here i declared "#bodyText" and #truncated to access the element in the component but showing undefined in onInit() hook, however it is working fine in ngAfterView() component.
<div class="note-card-content">
<h1 class="note-card-title">{{title}}</h1>
<div #bodyText class="note-card-body">
<p>{{body}}</p>
<div #truncator class="fade-out-truncation"></div>
</div>
</div>
<div class="x-button"></div>
// This is the component
import { Component, ElementRef, Input, OnInit, Renderer2, ViewChild } from '#angular/core';
#Component({
selector: 'app-note-card',
templateUrl: './note-card.component.html',
styleUrls: ['./note-card.component.scss']
})
export class NoteCardComponent implements OnInit {
//These two elements are showing undefined when I print on console
#ViewChild('truncator') truncator:ElementRef<HTMLElement>;
#ViewChild('bodyText') bodyText:ElementRef<HTMLElement>;
#Input() title:string;
#Input() body:string
constructor(private renderer:Renderer2) { }
ngOnInit(): void {
console.log(this.truncator)
// Work out if there is a text overflow and if so,then hide
let style = window.getComputedStyle(this.bodyText.nativeElement,null);
let viewableHeight = parseInt(style.getPropertyValue("height"),10)
if(this.bodyText.nativeElement.scrollHeight > viewableHeight){
// if there is no text overflow ,show the fade out truncator
this.renderer.setStyle(this.truncator.nativeElement,'display','block')
}else{
// else (there is a text overflow)
this.renderer.setStyle(this.truncator.nativeElement,'display','none')
}
}
}
And what's the problem? :)
This is a normal behaviour. ngOnInit(){} runs before Angular initializes the component's views and child views. You shouldn't use ngOnInit for this purpose. I suggest you to take a look at Lifecycle hooks, Angular.
I am fairly new to Angular and TypeScript in general.
In my AppComponent HTML, I inject another component with
<app-listpost></app-listpost>
But in the TypeScript, I also receive this component because my AppComponent imports my ListPostComponent in order to call a function from ListPostComponent in AppComponent.
The function simply adds an object to an array from ListPostComponent
Thus, I found out that calling that function from AppComponent works but the array with which AppComponent works is from another instance than the array from the ListPostComponent instanciated with the HTML tag.
app.component.html
<div style="text-align:center">
<h1>
{{ getTitle() }}
</h1>
</div>
<app-listpost></app-listpost>
<button class="btn btn-success" (click)="addPostToList('test title from AppComponent', 'test content from AppComponent')">Add test post</button>
app.component.ts
import { Component } from '#angular/core';
import { ListpostComponent } from './listpost/listpost.component'
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
providers:[ListpostComponent]
})
export class AppComponent {
title = 'Exercise';
constructor(private listPostComp: ListpostComponent){}
public addPostToList(newTitle, newContent){
this.listPostComp.addPostToList(newTitle, newContent);
}
getTitle(){
return this.title;
}
}
listpost.component.html
<button class="btn btn-success btn-test" (click)="addPostToList('test title from ListPostComponent', 'test content from ListPostComponent')">Add test post</button>
<div class="container">
<div class="row">
<div class="col-xs-12">
<ul class="list-group">
<app-post *ngFor="let post of getListPosts()"
[Title]="post.title"
[Content]="post.content"></app-post>
</ul>
</div>
</div>
</div>
listpost.component.ts
import { Component, OnInit, ChangeDetectionStrategy } from '#angular/core';
#Component({
selector: 'app-listpost',
templateUrl: './listpost.component.html',
styleUrls: ['./listpost.component.scss'],
changeDetection: ChangeDetectionStrategy.Default
})
export class ListpostComponent implements OnInit {
private listPosts = [
{
title: 'Post example',
content: 'Content example'
}
];
constructor() { }
ngOnInit() {
}
addPostToList(newTitle, newContent){
let newPost = {
title: newTitle,
content: newContent
}
this.listPosts.push(newPost);
console.log("Added new post with title : \"" + newTitle + "\" and content : \"" + newContent + "\"");
console.log(this.listPosts); //DEBUG
}
getListPosts(){
return this.listPosts;
}
}
The ultimate goal was that cliking the button from app.component.html would call the function from listpost.component.ts, add an object to the array and that the display from listpost.component.html would refresh with the newly added object.
It works when I click the button in listpost.component.html but not from app.component.html for the reason I explained above.
Therefore, is there a way for me to specify that I want to use the instance of ListPostComponent I receive in the constructor of AppComponent in the HTML tag <app-listpost></app-listpost> ?
I think best practice would be to create an injectable service that contains a function with your addPostToList() logic. Then create another function in your ListPostComponent that calls that service function and updates the listPosts. Then in your AppComponent, call the function from your ListPostComponent.
I want to use Angular Material library and build my own library with some custom designs. But facing some problems while splitting the material components. I think the problem is with shadow DOM. Here is the code that i want to achieve.
Code
custom-tabs-group.html -parent
<div class="custom-tabs">
<mat-tab-group disableRipple>
<ng-content></ng-content>
</mat-tab-group>
</div>
custom-tabs.html -child
<custom-tabs-group [tabContent]="tabContent">
<mat-tab *ngFor="let tab of tabContent" label="{{tab.title}}">{{tab.content}} </mat-tab>
</custom-tabs-group>
is it even possible? Please let me know
the code you shared got the ng-content usage backwards... the <custom-tabs-group> will be at the parent level and <ng-content> at the child level.
I tried 2 approaches:
strategy #1: pass the content to the custom child inside the <mat-tab>... this worked
strategy #2: pass the content to the custom child where <mat-tab> is inside the child... this didn't work
you can check the demo here
Actually i figured it out with some hack i don't know if its a good approch or not
custom-tabs.component.html
<div class="custom-tabs">
<mat-tab-group disableRipple>
<mat-tab *ngFor="let tab of tabsContentList" label="{{tab.label}}">
<div [innerHTML]="tab.htmlContent"></div>
</mat-tab>
</mat-tab-group>
</div>
custom-tabs-component.ts
import { DomSanitizer } from '#angular/platform-browser';
import { Component, OnInit, ViewEncapsulation, AfterContentInit, ContentChildren, Input, ViewChild, ElementRef, QueryList } from '#angular/core';
#Component({
selector: 'il-tabs-content',
template: `
<div #content>
<ng-content></ng-content>
</div>
`
,
})
export class TabsContentComponent implements OnInit {
#Input() label: String;
#ViewChild('content') set content(content: ElementRef) {
console.log("block three", content)
this.htmlContent = content;
if (this.htmlContent) {
this.htmlContent = this.htmlContent.nativeElement.innerHTML;
}
}
htmlContent: any;
constructor() { }
ngOnInit() {
}
}
#Component({
selector: 'il-tabs-group',
templateUrl: './tabs.component.html',
styleUrls: ['./tabs.component.css'],
encapsulation: ViewEncapsulation.None
})
export class TabsGroupComponent implements OnInit, AfterContentInit {
#ContentChildren(TabsContentComponent) tabsContentList: QueryList<TabsContentComponent>;
constructor(public sanitizer: DomSanitizer) { }
ngOnInit() {
}
ngAfterContentInit() {
this.tabsContentList.forEach((tabInstance) => {
var sanEle = this.sanitizer.bypassSecurityTrustHtml(tabInstance.htmlContent)
tabInstance.htmlContent = sanEle;
return tabInstance
})
}
}
usage
<il-tabs-group>
<il-tabs-content label="hello-1">
<h1>hello-1 content</h1>
</il-tabs-content>
<il-tabs-content label="hello-2">
<h1>hello-2 content</h1>
</il-tabs-content>
<il-tabs-content label="hello-3">
<h1>hello-3 content</h1>
<h2>extra content</h2>
</il-tabs-content>
</il-tabs-group>
i defined two components 'il-tabs-content' and 'li-tabs-group'. with this now i can use my own custom tabs build over angular material tabing with dynamic tabs. Anyone with better approch are welcome to share their ideas. thanks
I am new to Angular 7 (2+) & trying my hands on #Input & #Output. However, passing data from Parent to Child component via #Input is understood & in place.
However, very basic on the other hand passing data from Child to Parent component via using #Output concept is understood & but the implementation is not getting right.
Here is what I am trying. When a button is clicked in the
Child component, a property in the parent component should be
converted to Upper case & displayed.
ChildComponent.ts
import { Component, OnInit, Input, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'app-child',
templateUrl: './child.component.html',
})
export class ChildComponent implements OnInit {
#Input('child-name') ChildName: string;
#Output() onHit: EventEmitter<string> = new EventEmitter<string>();
constructor() { }
ngOnInit() {}
handleClick(name): void {
this.onHit.emit(name);
}}
ChildComponent.html
<h1> child works! </h1>
<button (click)="handleClick('eventEmitter')"> Click me! </button>
ParentComponent.ts
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
title = 'my-dream-app';
customerName: string = "";
catchChildEvent(e) {
console.log(e);
}}
ParentComponent.html
<div style="text-align:center">
<app-child [child-name]="HelloChild"></app-child>
//Trying to bind to Custom event of child component here
<b (onHit)="catchChildEvent($event)">
<i> {{customerName}} </i>
</b>
No error in console or binding
From the above snippet, when the button in Child Component is clicked the parent Component's property CustomerName should get the value & displayed?
Example: https://stackblitz.com/edit/angular-3vgorr
(onHit)="catchChildEvent($event)" should be passed to <app-child/>
<app-child [child-name]="HelloChild" (onHit)="catchChildEvent($event)"></app-child>
You are emitting event from app-child component so attach the handler for app-child component to make it work.
<div style="text-align:center">
<app-child (onHit)="catchChildEvent($event)" [child-name]="HelloChild"></app-child>
//Trying to bind to Custom event of child component here
<b>
<i> {{customerName}} </i>
</b>
And within the ts file update value of cutomerName property.
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
title = 'my-dream-app';
customerName: string = "";
catchChildEvent(e) {
this.customerName = e;
}
}
You should move (onHit)="catchChildEvent($event)" to app-child in parent html:
<app-child [child-name]="HelloChild"
(onHit)="catchChildEvent($event)>
</app-child>
I have a problem to hide the navbar when the user is not logged in (on the public views), I check if the item currentUser exists on the localStorage and then I use
*ngIf on the html template to show/hide.
When I login at first I don't see the navbar, but after refreshing the page it's displaying, the same when I logout, at first it shows it and after refreshing the page it's gone.
There is my app.component.ts
import { Component, OnInit } from '#angular/core';
#Component({
moduleId: module.id,
selector: 'app',
templateUrl: 'app.component.html'
})
export class AppComponent implements OnInit {
userLogged = JSON.parse(localStorage.getItem('currentUser'));
ngOnInit() {
console.log(this.userLogged);
}
}
And my app.component.html
<!-- main app container -->
<div class="jumbotron">
<div class="container">
<ng-navbar *ngIf="userLogged"></ng-navbar>
<div class="col-sm-12">
<alert></alert>
<router-outlet></router-outlet>
</div>
</div>
</div>
In case you need more information just ask for it, is my first Angularjs 4 question and I don't know what to show exactly.
Try this:
<ng-navbar *ngIf="userLogged()"></ng-navbar>
userLogged() { return JSON.parse(localStorage.getItem('currentUser')) };