I want to the elementReference of mat-card from parent component i.e AppComponent. I have written a directive and attached it to the host component. If I try to access elem.nativeElement.childNodes[0] , it gives undefined. How to access mat-card element reference from the parent component.
Here's the demo.
Thanks
Here's my directive
import { Directive, ElementRef, Renderer2 } from '#angular/core';
#Directive({ selector: '[displayStyle]' })
export class DisplayStyleDirective {
constructor(elem: ElementRef, renderer: Renderer2) {
console.log(elem.nativeElement.childNodes[0])
}
}
In console i get undefined .
App.Component.html
<div>
<h3>Recognized Images</h3>
<div>
<card-overview-example displayStyle *ngFor="let user of existingUserInfo" [User]="user"></card-overview-example>
</div>
</div>
App.component.ts
import {
Component,
OnInit,
AfterViewInit,
ElementRef,
Renderer2,
ViewChild
} from "#angular/core";
#Component({
selector: "app-root",
templateUrl: "./app.component.html",
})
export class AppComponent implements OnInit,AfterViewInit {
constructor(private elementRef: ElementRef,
private renderer: Renderer2) { }
ngOnInit() { }
existingUserInfo:Array<userDetails> = [
{userID:"121",filename:"https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Angular_full_color_logo.svg/250px-Angular_full_color_logo.svg.png"},
{userID:"122",filename:"https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Angular_full_color_logo.svg/250px-Angular_full_color_logo.svg.png"},
{userID:"123",filename:"https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Angular_full_color_logo.svg/250px-Angular_full_color_logo.svg.png"}
]
ngAfterViewInit() {
}
}
export interface userDetails {
userID: string;
filename: string;
}
Card-overview-example.html
<mat-card class="example-card">
<img [src]="userDetails.filename" alt="Photo of {{userDetails.userID}}">
<p >{{userDetails.userID}}</p>
</mat-card>
card-overview-example.ts
import {Component,Input} from '#angular/core';
/**
* #title Basic cards
*/
#Component({
selector: 'card-overview-example',
templateUrl: 'card-overview-example.html'
})
export class CardOverviewExample {
#Input("User") userDetails: userDetails;
}
export interface userDetails {
userID: string;
filename: string;
}
Here's the demo.
Try elem.nativeElement.children[0] instead of elem.nativeElement.childNodes[0]
Related
I need help about angular component comunication.
I have Parent component and children.
In parent component i have list. I would like to click of item list and move data {name, text} to children component and set it to children component where is froala editor.
In Angular, there are two ways you can do implement component intercommunication.
Pass data from parent to child with input binding
#Component({
selector: 'app-parent',
template: `
<app-child [childMessage]="parentMessage"></app-child>
`,
styleUrls: ['./parent.component.css']
})
export class ParentComponent{
parentMessage = "message from parent"
constructor() { }
}
#Component({
selector: 'app-child',
template: `
Say {{ message }}
`,
styleUrls: ['./child.component.css']
})
export class ChildComponent {
#Input() childMessage: string;
constructor() { }
}
Using Angular Services(This method is used when the components communicating are directly related ). But from you are describing you can do it better with angular service. When you click the item list, you can call the function of services that pushes the data to the BehaviourSubject. After that, all the component that are subscribed to that Behaviour subject will get the data you passed.
#Injectable()
export class DataService {
private messageSource = new BehaviorSubject('default message');
currentMessage = this.messageSource.asObservable();
constructor() { }
changeMessage(message: string) {
this.messageSource.next(message)
}
}
import { Component, OnInit } from '#angular/core';
import { DataService } from "../data.service";
#Component({
selector: 'app-parent',
template: `
{{message}}
`,
styleUrls: ['./sibling.component.css']
})
export class ParentComponent implements OnInit {
message:string;
constructor(private data: DataService) { }
ngOnInit() {
this.data.currentMessage.subscribe(message => this.message = message)
}
}
import { Component, OnInit } from '#angular/core';
import { DataService } from "../data.service";
#Component({
selector: 'app-sibling',
template: `
{{message}}
<button (click)="newMessage()">New Message</button>
`,
styleUrls: ['./sibling.component.css']
})
export class SiblingComponent implements OnInit {
message:string;
constructor(private data: DataService) { }
ngOnInit() {
this.data.currentMessage.subscribe(message => this.message = message)
}
newMessage() {
this.data.changeMessage("Hello from Sibling")
}
}
I'm using a service to pass data between child and parent with the purpose being to update the parent ui with child specific info depending on what child is loaded.
Service:
import { Subject } from "rxjs/Subject";
export class ChildDataService {
private childDetails = new Subject<{}>();
childLoaded$ = this.childDetails.asObservable();
changedComponentName(option: {}){
this.childDetails.next(option);
}
}
Parent Component:
import { Component, OnInit, ViewEncapsulation } from '#angular/core';
import { ChildDataService } from "../core/helpers/child-data.service";
import { Subscription } from "rxjs/Subscription";
#Component({
selector: 'app-parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class ParentComponent implements OnInit {
childDetails: {title?: string};
private subscription:Subscription;
constructor( private childDataService: ChildDataService) {
this.childDataService.childLoaded$.subscribe(
newChildDetails => {
console.log(newChildDetails);
this.childDetails = newChildDetails
});
}
}
Example Child Component:
import { Component, OnInit, ViewEncapsulation } from '#angular/core';
import { ChildDataService } from "../../core/helpers/child-data.service";
#Component({
selector: 'app-child-dashboard',
templateUrl: './child-dashboard.component.html',
styleUrls: ['./child-dashboard.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class ChildDashboardComponent implements OnInit {
constructor(private childDataService: ChildDataService) { }
public ngOnInit() {
this.childDataService.changedComponentName({
title: 'Dashboard'
});
}
}
Parent HTML:
<div class="subheader">
<h1>{{ childDetails?.title }}</h1>
<button *ngIf="childDetails?.title == 'Dashboard'">dashboard</button>
<button *ngIf="childDetails?.title == 'SecondChild'">Second Child</button>
</div>
With this setup I am getting the error "ExpressionChangedAfterItHasBeenCheckedError" when i click on a routerLink, now if i click on the same link a second time, the error persists but the correct button becomes visible. Spent all weekend getting nowhere with this, so any help would be greatly appreciated.
public ngOnInit() {
setTimeout(() => {
this.childDataService.changedComponentName({
title: 'Dashboard'
}), {});
}
it should work.
async ngOnInit() {
await this.childDataService.changedComponentName({
title: 'Dashboard'
});
}
So im doing loop and display inputs and i want on click on select to add focus on first input element. Any suggestion how can i select that first element and add him autofoccus?
Here is how to do. Write a directive :
import {Directive, Renderer, ElementRef, OnInit, AfterViewInit, Input} from '#angular/core';
#Directive({
moduleId: module.id,
selector: '[focusOnInit]'
})
export class FocusOnInitDirective implements OnInit, AfterViewInit {
#Input() focusOnInit ;
static instances: FocusOnInitDirective[] = [];
constructor(public renderer: Renderer, public elementRef: ElementRef) {
}
ngOnInit(): void {
FocusOnInitDirective.instances.push(this)
}
ngAfterViewInit(): void {
setTimeout(() => {
FocusOnInitDirective.instances.splice(FocusOnInitDirective.instances.indexOf(this), 1);
});
if (FocusOnInitDirective.instances.every((i) => i.focusOnInit===0)) {
this.renderer.invokeElementMethod(
this.elementRef.nativeElement, 'focus', []);
}
}
}
in your component:
import { Component } from '#angular/core';
#Component({
moduleId: module.id,
selector: 'app',
template: `
<div *ngFor="let input of [1,2,3,4]; let i=index">
<input type="text" [focusOnInit] = i >
</div>
`
})
export class AppComponent {
}
The corresponding plunker, adapted from this
I want to add some text in showlist.component.html. My code is given below, I am not aware how to use document.body in Angular 2.
import { Component, OnInit } from '#angular/core';
import { DOCUMENT } from '#angular/platform-browser';
#Component({
selector: 'app-showlist',
templateUrl: './showlist.component.html',
styleUrls: ['./showlist.component.css']
})
export class ShowlistComponent implements OnInit {
public myname = "saurabh";
constructor() { }
ngOnInit() {
//this.myname.appendTo(document.body);
document.body(this.myname);
//document.write(this.myname);
}
}
you do the following to access the document.body
import { Component, OnInit, Inject } from '#angular/core';
import { DOCUMENT } from '#angular/common';
#Component({
selector: 'app-showlist',
templateUrl: './showlist.component.html',
styleUrls: ['./showlist.component.css']
})
export class ShowlistComponent implements OnInit {
public myname = "saurabh";
constructor(#Inject(DOCUMENT) private document: Document) {}
ngOnInit() {
this.document.body.innerHTML = this.myname;
}
}
I am trying to implement angular2-virtual-scroll in an angular4 project. Firstly, can anyone confirm if I would have a problem implementing it in NG4. I don't think I should and I haven't seen any notices to the contrary. Secondly I am using an observable instead of a promise shown in the NPM angular2-virtual-scroll docs. However, when I applied the virtual-scroll tag there is no change in my output...no scroll bar ..the data from the observable is displayed but no scrolling. The following are the relevant code segments:
Home.component.html
<h1 style="color: #76323f">
{{title}}
</h1>
<h4>
{{description}}
</h4>
<h2>Coming Soon....</h2>
<app-events-list [upcomingEvents]="upcomingEvents"></app-events-list>
home.component.ts
import {Component, OnInit,OnDestroy } from '#angular/core';
import {UpcomingEvent} from './../../interface/upcomingevent';
import {EventService} from './../../services/event.service';
import {Subscription} from 'rxjs/Subscription';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit, OnDestroy {
upcomingEvents: Array<UpcomingEvent>;
title = 'Welcome....';
description='Events promotions....';
eventServiceSub: Subscription;
constructor(private eventService: EventService){
this.upcomingEvents = [];
}
ngOnInit() {
this.eventServiceSub=this.eventService.getEvents().subscribe(upcomingEvents=>{this.upcomingEvents=upcomingEvents.slice(0,25);
});
}
ngOnDestroy(){
if (this.eventServiceSub){
this.eventServiceSub.unsubscribe();
}
}
}
event-list-component.html
<virtual-scroll [items]="items" (update)="upcomingEvents = $event"
(change)="onlistChange($event)">
<app-upcomingevent *ngFor="let upcomingEvent of upcomingEvents"[upcomingEvent]="upcomingEvent" [eventItemCss]="'event-item'"></app-upcomingevent>
<div *ngIf="loading" class="loader">Loading.....</div>
</virtual-scroll>
event-list-component.ts
import { Component, Input, OnDestroy, OnInit } from '#angular/core';
import { UpcomingEvent } from './../../interface/upcomingevent';
import { trigger,state,style,transition,animate,keyframes } from '#angular/animations';
import { ChangeEvent } from 'angular2-virtual-scroll';
import {EventService} from './../../services/event.service';
import {Subscription} from 'rxjs/Subscription';
#Component({
selector: 'app-events-list',
templateUrl: './events-list.component.html',
styleUrls: ['./events-list.component.css'],
animations: []
})
export class EventsListComponent implements OnInit, OnDestroy {
#Input()
upcomingEvents: Array<UpcomingEvent>;
items=this.upcomingEvents;
protected buffer: Array<UpcomingEvent> =[];
protected loading: boolean;
eventServiceSub: Subscription;
constructor(private eventService: EventService) {
this.upcomingEvents=[];
}
ngOnInit() {
}
ngOnDestroy(){
if (this.eventServiceSub){
this.eventServiceSub.unsubscribe();
}
}
protected onlistChange(event: ChangeEvent){
if (event.end !==this.buffer.length) return;
this.loading=true;
this.eventServiceSub=this.eventService.getEvents().subscribe(upcomingEvents=>{
this.buffer=upcomingEvents.slice(this.buffer.length,25);
this.loading=false;
}, ()=> this.loading=false);
}
}
eventService.ts
import { Injectable } from '#angular/core';
import {UpcomingEvent} from './../interface/upcomingevent'
import {Http} from '#angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
#Injectable()
export class EventService {
constructor(private http: Http) { }
getEvents(): Observable<UpcomingEvent[]>
{
return this.http
.get(`https://jsonplaceholder.typicode.com/posts`)
.map(response => response.json() as UpcomingEvent[]);
}
}
upcomingEvents.html
<md-card [ngClass]="'event-item'">
<h4 [ngClass]="'event-title'" [ngStyle]="{'color':'purple'}"> {{upcomingEvent.title}}</h4>
<md-card-actions>
<button md-button>LIKE</button>
<button md-button>SHARE</button>
</md-card-actions>
</md-card>
upcomingEvents.component.ts
import { Component, Input,OnInit } from '#angular/core';
import { UpcomingEvent } from './../../interface/upcomingevent';
#Component({
selector: 'app-upcomingevent',
templateUrl: './upcomingevent.component.html',
styleUrls: ['./upcomingevent.component.css']
})
export class UpcomingeventComponent implements OnInit {
#Input()
upcomingEvent: UpcomingEvent;
#Input()
eventItemCss: string;
constructor() { }
ngOnInit() {
if (!this.upcomingEvent) {
this.upcomingEvent=<UpcomingEvent> {};
}
}
}
The way it suppose to work is that the HomeComponent request data from the eventService via an observable..the data is then passed to the eventList component where it is iterated on and pass to the upcomingEvents component to present via HTML. 25 events are requested at first and if the user scroll to the end another 25 is requested from the eventService...this time by the upcomingEvents component. I am not sure this is the most efficient way to do it but in either case it doesn't work. The virtual-scroll seems to have no effect on the output....I would really appreciate someone showing me what I am doing wrong....Thanks
I think I see a problem in your code. When using virtual-scroll you need to keep two arrays - one for all loaded data, and one for currently rendered items. Looks like you kinda mixed them up.
Let's call all-items-array as items and render-array - upcomingEvents
First chunk of items will be passed from the parent component, hence:
Home.component.html
...
<app-events-list [items]="upcomingEvents"></app-events-list>
event-list-component.html looks good
event-list-component.ts
import { Component, Input, OnDestroy, OnInit } from '#angular/core';
import { UpcomingEvent } from './../../interface/upcomingevent';
import { trigger,state,style,transition,animate,keyframes } from '#angular/animations';
import { ChangeEvent } from 'angular2-virtual-scroll';
import {EventService} from './../../services/event.service';
import {Subscription} from 'rxjs/Subscription';
#Component({
selector: 'app-events-list',
templateUrl: './events-list.component.html',
styleUrls: ['./events-list.component.css'],
animations: []
})
export class EventsListComponent implements OnInit, OnDestroy {
#Input()
items: Array<UpcomingEvent> = [];
upcomingEvents: Array<UpcomingEvent>;
protected loading: boolean;
eventServiceSub: Subscription;
constructor(private eventService: EventService) {
this.upcomingEvents=[];
}
ngOnInit() {
}
ngOnDestroy(){
if (this.eventServiceSub){
this.eventServiceSub.unsubscribe();
}
}
protected onlistChange(event: ChangeEvent){
if (event.end !==this.items.length) return;
this.loading=true;
this.eventServiceSub=this.eventService.getEvents().subscribe(upcomingEvents=>{
this.items=upcomingEvents.slice(0, this.items.length + 25);
this.loading=false;
}, ()=> this.loading=false);
}
}
warning! code not tested, I simply edited your code.