Accessing an element from sibling component in Angular 2+ - javascript

I have a set of components that work together. Basically I am displaying a list of labels, and a list of data for each label. Since the label pane is resizable, it becomes a sibling of the data component. I.e.- my list component looks like:
<app-list-labels></app-list-labels>
<app-list-data></app-list-data>
And list-labels and list-data look like this, respectively:
// app-list-labels:
<div class="label-header">Labels</div>
<div class="label-labels" id="labels-labels">
<!-- all of my labels looped over and displayed -->
</div>
// app-list-data:
<div class="data-header">Labels</div>
<div class="data-data" id="data-data">
<!-- all of my data rows looped over and displayed -->
</div>
Both labels-labels and data-data have overflow-y set to auto, so that they can scroll if the number of rows exceeds the container size. The number and size of rows between the two are always identical. My goal is to have both containers scroll if one container is being scrolled. So I'd need to attach a (scroll) event listener to both of those divs (#data-data and #labels-labels), and update the scroll value of the non-scrolled element. My problem is- how can I access an element from one component in a sibling component? If app-labels was embedded in app-data it would be straight forward, but being siblings, I cant see how to do it.

You could try exposing the Div's using #Output decoratos, like this:
<app-component-one (divComponent)="divOne = $event"></app-component-one>
<app-component-two (divComponent)="divTwo = $event"></app-component-two>
Sibling 1:
import { AfterViewInit, Component, ElementRef, EventEmitter, OnInit, Output, ViewChild } from '#angular/core';
#Component({
selector: 'app-component-one',
templateUrl: './component-one.component.html',
styleUrls: ['./component-one.component.css']
})
export class ComponentOneComponent implements OnInit, AfterViewInit {
#ViewChild('divComponent1') divComponent1: ElementRef;
#Output() divComponent = new EventEmitter();
constructor() {
}
ngOnInit() {
}
ngAfterViewInit(): void {
this.divComponent.emit(this.divComponent1);
}
}
Sibling 2:
import { AfterViewInit, Component, ElementRef, EventEmitter, OnInit, Output, ViewChild } from '#angular/core';
#Component({
selector: 'app-component-two',
templateUrl: './component-two.component.html',
styleUrls: ['./component-two.component.css']
})
export class ComponentTwoComponent implements OnInit, AfterViewInit {
#ViewChild('divComponent2') divComponent1: ElementRef;
#Output() divComponent = new EventEmitter();
constructor() {
}
ngOnInit() {
}
ngAfterViewInit(): void {
this.divComponent.emit(this.divComponent1);
}
}
Parent Component, the one that has the siblings in :
import { AfterViewInit, Component, ElementRef, OnInit } from '#angular/core';
#Component({
selector: 'app-component-base',
templateUrl: './component-base.component.html',
styleUrls: ['./component-base.component.css']
})
export class ComponentBaseComponent implements OnInit, AfterViewInit {
divOne: ElementRef;
divTwo: ElementRef;
constructor() {
}
ngOnInit() {
}
ngAfterViewInit(): void {
console.log('div one' , this.divOne);
console.log('div two' , this.divTwo);
}
}

Related

Angular - trying to use child component function in parent view but I'm gettting an error

When I use #ViewChild I get the error that the component is not defined.
When I use #ViewChildren I get the error that the function from that component is not a function.
I am new to using child components in Angular so I'm not sure why it's doing this when I do have the child component defined in the parent component and when it's clearly a function in the child component.
I don't want to have to define every function from the child in the parent or else what's even the point of using a separate component.
Child Component
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-mood',
templateUrl: './mood.component.html',
styleUrls: ['./mood.component.css']
})
export class MoodComponent implements OnInit {
moodColors = ['red', 'orange', 'grey', 'yellow', 'green'];
constructor() { }
ngOnInit(): void {
}
chooseMood() {
alert(this.moodColors);
}
}
Parent Component (Relavant Part of Version with "ERROR TypeError: ctx_r3.mood is undefined")
import { Component, OnInit, ViewChild, ViewChildren } from '#angular/core';
import { ViewEncapsulation } from '#angular/core';
import { MoodComponent } from '../mood/mood.component';
#Component({
selector: 'app-calendar',
templateUrl: './calendar.component.html',
styleUrls: ['./calendar.component.css'],
encapsulation: ViewEncapsulation.None
})
export class CalendarComponent implements OnInit {
#ViewChild('mood') mood: MoodComponent = new MoodComponent;
Parent Component (Relavant Part of Version with "ERROR TypeError: ctx_r3.mood.chooseMood is not a function")
import { Component, OnInit, ViewChild, ViewChildren } from '#angular/core';
import { ViewEncapsulation } from '#angular/core';
import { MoodComponent } from '../mood/mood.component';
#Component({
selector: 'app-calendar',
templateUrl: './calendar.component.html',
styleUrls: ['./calendar.component.css'],
encapsulation: ViewEncapsulation.None
})
export class CalendarComponent implements OnInit {
#ViewChildren('mood') mood: MoodComponent = new MoodComponent;
Parent View
<h2 (click)="mood.chooseMood()"></h2>
You don't explicitly initialize view children via new.
Just use:
#ViewChild('mood') mood : MoodComponent;
If that doesn't work post a Stackblitz example which I can edit to resolve the issue.
Also, using ViewChild is more of an exception in Angular, and your use of it points to a probable design issue. More likely you child component should emit via an Output to the parent.
Regarding outputs, you can do something like this - though it is hard to give a precise answer without deeper knowledge of what you are trying to achieve:
export class MoodComponent implements OnInit {
#Input() moodId: string;
#Output() chooseMood = new EventEmitter<string>();
moodClicked(){
this.chooseMood.emit(moodId);
}
}
export class CalendarComponent implements OnInit {
moodChosen(string: moodId){
console.log(moodId);
}
}
// Calendar template:
<app-mood
moodId="happy"
(chooseMood)="moodChosen($event)"
></app-mood>
1 - you have to use this code
#ViewChild('mood') mood : MoodComponent;
when you are using #ViewChildren it will return list of items with the 'mood' name then you have to use this code
mood.first.chooseMood() ;
its better use ViewChildren when there is ngIf in your element
2- no need new keyword for initialize mood variable
it would be fill after ngOnInit life cycle fires

Angular 10: Console shows my object as "unidentified" (pass data from parent to child)

I am trying to pass data from the parent to the child. I get the correct output when I pass the string (data1) from parent to child but when I try to display object (data) the console says it's unidentified and nothing is displayed on the screen.
Parent Component:
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
data={
name:'Charles',
age:24,
email:'charles#gmail.com'
};
data1 = "Charles";
}
<app-contact [sendName] ="data"></app-contact>
Child Component
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'app-contact',
templateUrl: './contact.component.html',
styleUrls: ['./contact.component.css']
})
export class ContactComponent implements OnInit {
#Input() sendName;
constructor() { }
ngOnInit(): void {
console.log(this.sendName);
}
}
<h1 class="text-center">Contact Form</h1>
<h3 class="pb-5">from the parent {{sendName.name}}</h3>
<h3 class="pb-5">from the parent {{sendName.age}}</h3>
please someone help me.
Thank you
If your data (in parent component) variable has static value then your child component will be
#Input() sendName;
ngOnInit(): void {
console.log(this.sendName);
}
If your data (in parent component) variable will assign from dynamic value then your child component will be
(Assume you have an API to call and update sendName variable on API response, but our child component is initialised without waiting the API response then we need to trigger #Input() to send updated data in child component )
#Input() set sendName(value: any) {
if(value){
console.log(value);
this.name = value
// call any function from here
}
}
name:any; // use this variable at anywhere
constructor(){
}
you might be rendering the child, before setting the value in data in parent
in Child component import Onchanges and get from ngOnchanges()
import { Component, OnInit, Input ,Onchanges} from '#angular/core';
export class ContactComponent implements OnInit,Onchanges {
#Input() sendName; constructor() { }
ngOnChanges() {
console.log(this.sendName); }

How to insert HTML from server side in a component in angular4?

suppose i want to insert some HTML element from the server side to the component, how can i do it
import { Component, ElementRef, ViewChild, AfterViewChecked, TemplateRef, ViewContainerRef } from '#angular/core';
#Component({
selector: 'app-root',
template: `
<div>
<h3>this is the container</h3>
<div #holder></div>
</div>
`
})
export class AppComponent implements AfterViewChecked {
#ViewChild('holder', { read: TemplateRef }) _template: TemplateRef<any>;
constructor() { }
ngAfterViewChecked() {
debugger;
this._template.createEmbeddedView('<div>this is a new "div" element</div>');
}
}
in this code i am trying to insert a div to another div with the id='holder' but i am not able to achieve it.
I got the simple way to achieve.
html
<div [innerHTML]="yourHtml"></div>
ts
public yourHtml = '<div>this is a new "div" element</div>';

html wont render from inside angular 2 component

I'm using a service to dynamically change the content in my header depending on the page I'm on, however when I put HTML in my component it doesn't render in the browser (see example below)
home.component.ts
import { Component, OnInit } from '#angular/core';
import { HeaderTitleService } from '../../services/headerTitle.service';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
constructor(
private headerTitleService: HeaderTitleService
) { }
ngOnInit() {
this.headerTitleService.setTitle(`
We strive to create things
<br> that are engaging, progressive
<br> & above all
<span class="highlight">
<em>innovative.</em>
</span>
`);
}
}
header.component.ts
import { Component, OnInit } from '#angular/core';
import { HeaderTitleService } from '../../../services/headerTitle.service'
#Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss']
})
export class HeaderComponent implements OnInit {
title: any;
constructor(
private headerTitleService: HeaderTitleService
) { }
ngOnInit() {
this.headerTitleService.title.subscribe(updatedTitle => {
this.title = updatedTitle;
});
}
}
header.component.html
<h1>{{title}}</h1>
so Im trying to set the title to be a string that has html tags in it that I want to be rendered but what happens is the whole thing comes out as a string instead of how it would look like it I had put it in my home.component.html.
Is there a way I can do this??
You can set the [innerHtml] property
<h1 [innerHtml]="title"></h1>
Example

Accessing DOM element in Angular 2 and change the element's class attribute

I'm new in angular2. my code is like this:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'main',
template: `
<div class="current">
</div>
`
})
export class MainComponent implements OnInit {
ngOnInit(): void {
//change the div class from current to next...
}
}
i'd like to change the div class from 'current' to 'next'.
i appropriate if you let me know what is the best way do that?
One option is to use a template reference variable.
In the example below, the reference variable #target is added to the desired element and then the decorator #ViewChild (#ViewChild('target') target) allows you to access the variable in your component.
From there, you can get a reference to the DOM element by accessing the nativeElement property on the variable.
Here is an example where the class name is updated:
import { Component, AfterViewInit, ViewChild } from '#angular/core';
#Component({
selector: 'main',
template: `
<div #target class="current">
</div>
`
})
export class MainComponent implements AfterViewInit {
#ViewChild('target') target;
constructor() { }
ngAfterViewInit(): void {
let element = this.target.nativeElement;
element.className = 'next';
}
}
However, it's worth pointing out that you can handle most DOM manipulation with the build-in DOM directives. In this case you could just use the ngClass directive to bind a variable with the class attribute:
import { Component, AfterViewInit } from '#angular/core';
#Component({
selector: 'main',
template: `
<div [ngClass]="targetClass">
</div>
`
})
export class MainComponent implements AfterViewInit {
private targetClass: string = 'current';
constructor() { }
ngAfterViewInit(): void {
this.targetClass = 'next';
}
}

Categories

Resources