Angular 6 #Viewchild is not working with lazy loading - javascript

Here is my code that gives error cannot read property title undefined.
Parent Component
import { Child } from './child.component';
#Component({
selector: 'parent',
})
export class ParentComponet implements OnInit, AfterViewInit {
constructor(){}
#ViewChild(Child) child: Child;
ngAfterViewInit(){
console.log("check data", this.child.title)
}
}
And Child Component is.
#Component({
selector: 'child',
})
export class ChildComponet {
public title = "hi"
constructor(){}
}
routing.module.ts is like
{
path: "",
component: ParentComponent,
children: [
{
path: '/child',
component: ChildComponent
}
]
}
And Gives error is
ERROR TypeError: Cannot read property 'title' of undefined(…)

I think you are missing 'template' or 'templateUrl' in relevance to creating a Component
ParentComponent
import { ChildComponent } from './child.component'; // {ChildComponent} not {Child} as we are referencing it to the exported class of ChildComponent
#Component({
selector: 'parent',
template: `<child></child>`
})
export class ParentComponet implements OnInit, AfterViewInit {...}
ChildComponent
#Component({
selector: 'child',
template: `<h1>{{ title }}</h1>`
})
export class ChildComponent {...} // Be sure to spell it right as yours were ChildComponet - missing 'n'
UPDATE as per the user's clarification on this thread
Had added a Stackblitz Demo for your reference (Check the console)
If you want to access the ChildComponent that is rendered under the Parent Component's <router-outlet> you can do so by utilizing (activate) supported property of router-outlet:
A router outlet will emit an activate event any time a new component is being instantiated
Angular Docs
ParentComponent's Template
#Component({
selector: 'parent',
template: `<router-outlet (activate)="onActivate($event)"></router-outlet>`
})
export class ParentComponent {
onActivate(event): void {
console.log(event); // Sample Output when you visit ChildComponent url
// ChildComponent {title: "hi"}
console.log(event.title); // 'hi'
}
}
The result will differ based on the visited page under your parent's children
If you visit Child1Component you will get its instance Child1Component {title: "hi"}
If you visit Child2Component you will get its instance Child2Component {name: "Angular"}
These results will then be reflected on your ParentComponent's onActivate(event) console for you to access

That's not how it's supposed to work. You'll be only able to get the ChildComponent in your ParentComponent ONLY if you have the <app-child></app-child> tag in your ParentComponent Template.
Something like this:
...
<app-child></app-child>
...
But since you're using child routing, and the ChildComponent will load on the router-outlet of your ParentComponent you won't have access to that using ViewChild
PS: You'll only have access to it inside ngAfterViewInit as ViewChild can only be considered safe to have instantiated after the View has loaded:
import { Component, OnInit, ViewChild } from '#angular/core';
import { ChildComponent } from '../child/child.component';
...
#Component({...})
export class ParentComponent implements OnInit {
#ViewChild(ChildComponent) childComponent: ChildComponent;
...
ngAfterViewInit() {
console.log(this.childComponent);
}
}
Here's a Working Sample StackBlitz for your ref that illustrates your scenario in both the cases.
PS: To get the ChildComponent properties in your ParentComponent, with Routing, you'll have to either use a SharedService or you'll have to pass the ChildProperty in the route as a QueryParam and read it in your ParentComponent using the ActivatedRoute
UPDATE:
Sharing Data using Route Query Params:
Although this won't make much sense, but in your ChildComponent, you can have a Link that would route the user to the ChildComponent with the title property passed as a queryParam. Something like this:
<a
[routerLink]="['/child']"
[queryParams]="{title: title}">
Go To Child Route With Query Params
</a>
And in your ParentComponent have access to it using ActivatedRoute like this:
...
import { ActivatedRoute } from '#angular/router';
...
#Component({...})
export class ParentComponent implements OnInit {
...
constructor(
private route: ActivatedRoute,
...
) { }
ngOnInit() {
this.route.queryParams.subscribe(queryParams => {
console.log('queryParams[`title`]', queryParams['title']);
});
...
}
...
}
Using a SharedService
Just create a SharedService with a private BehaviorSubject that would be exposed as an Observable by calling the asObservable method on it. It's value can be set by exposing a method(setChildProperty) that will essentially call the next method with the updated childProperty value :
import { Injectable } from '#angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
#Injectable()
export class SharedService {
private childProperty: BehaviorSubject<string> = new BehaviorSubject<string>(null);
childProperty$: Observable<string> = this.childProperty.asObservable();
constructor() { }
setChildProperty(childProperty) {
this.childProperty.next(childProperty);
}
}
You can then inject it both in your ParentComponent and in your ChildComponent:
In ChildComponent set the value:
import { Component, OnInit } from '#angular/core';
import { SharedService } from '../shared.service';
#Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit {
public title = "hi"
constructor(private sharedService: SharedService) { }
ngOnInit() {
this.sharedService.setChildProperty(this.title);
}
}
And in your ParentComponent get the value:
...
import { SharedService } from '../shared.service';
#Component({...})
export class ParentComponent implements OnInit {
...
constructor(
...,
private sharedService: SharedService
) { }
ngOnInit() {
...
this.sharedService.childProperty$.subscribe(
childProperty => console.log('Got the Child Property from the Shared Service as: ', childProperty)
);
}
...
}

Make sure inside your parent.component.html template you've added the <child></child> tag.

Related

Sending calculated data from one component to another without Services

I want to send the value from one component to another, they are not related so all solutions are saying that I must use shared service to do that. But these services are using templates (if I'm right). Is there a way to do this sharing without services?
I want to send the BMI value from homepage.component.ts to result.component.ts.
homepage.component.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-homepage',
templateUrl: './homepage.component.html',
styleUrls: ['./homepage.component.css']
})
export class HomepageComponent implements OnInit {
constructor() { }
myHeight!:number;
myWeight!:number;
bmi!:number;
ngOnInit(): void {
}
onGenerate( height:string,width:string){
this.myHeight = +height;
this.myHeight=Math.pow(this.myHeight/100,2);
this.myWeight = +width;
this.bmi=this.myWeight/this.myHeight
console.log(this.bmi); //this is the calculated value to send
}
}
result.component.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-result',
templateUrl: './result.component.html',
styleUrls: ['./result.component.css']
})
export class ResultComponent implements OnInit {
constructor() { }
//I want to get the bmi here
ngOnInit(): void {
}
}
There are Two ways to communicate between unrelated components in angular:
1 - Through services, you have to understand where to inject it, in your case I think it should be injected in root, so try this with your service ( follow this tutorial to implement your service, just add my code instead of theirs )
#Injectable({
providedIn: 'root',
})
2 - Through a store ( a lot of boilerplate coding, to use if you have complexe states to keep synchronized through the whole app, by the way the store is basically a service )
If your components are not related then you can create a shared service between them. Then, you need to use dependency injection to communicate between these components. So, there is a great Angular tutorial which describes how to do it.
The service code would look like this:
#Injectable()
export class FooService {
constructor( ) { }
private yourData;
setData(data){
this.yourData = data;
}
getData(){
let temp = this.yourData;
this.clearData();
return temp;
}
}
and sender component:
import { Router } from '#angular/router';
import { FooService} from './services/foo.service';
export class SenderComponent implements OnInit {
constructor(
private fooService: FooService,
private router:Router) {}
somefunction(data){
this.fooService.setData(data);
this.router.navigateByUrl('/reciever');//as per router
}
}
and subscriber:
import { Router } from '#angular/router';
import { TransfereService } from './services/transfer.service';
export class RecieverComponent implements OnInit {
data;
constructor(
private fooService: FooService){
}
ngOnInit() {
data = this.transfereService.getData();
console.log(`data: `, data)
}
}
Solution: To pass the data from one component to another we can store it in a session storage or a local storage and then access it in other components from that storage. Here I have provided a sample code using local storage for your reference.
homepage.component.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-homepage',
templateUrl: './homepage.component.html',
styleUrls: ['./homepage.component.css']
})
export class HomepageComponent implements OnInit {
constructor() { }
myHeight!:number;
myWeight!:number;
data:string='';
bmi!:number;
ngOnInit(): void {
}
onGenerate( height:string,width:string){
this.myHeight = +height;
this.myHeight=Math.pow(this.myHeight/100,2);
this.myWeight = +width;
this.bmi=this.myWeight/this.myHeight;
this.data=localStorage.setItem('bmi',this.bmi);
console.log(this.bmi); //this is the calculated value to send
}
}
resultcomponent.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-result',
templateUrl: './result.component.html',
styleUrls: ['./result.component.css']
})
export class ResultComponent implements OnInit {
data:any;
constructor() { this.data=localstorage.getItem('bmi')}
//Access the bmi using the data variable here
ngOnInit(): void {
}
}

Angular: Getter/Setter - Getter is returning undefined

I'm trying to pass a variable that is set on a component, to the parent component via a getter/setter in a service. The setter is applied correctly, but the getter returns undefined.
The below code was pulled out of another project I work on that does just fine with this code so I'm not sure why it isn't working here.
I simply just need to pass the pageTitle that is set on the child component, pass it to the parent component to display in its HTML.
Parent Component
TS: styleguide.component.ts
import { Component } from '#angular/core';
import { Router, ActivatedRoute } from '#angular/router';
import { StyleguideService } from './styleguide.service';
#Component({
selector: 'styleguide',
templateUrl: './styleguide.component.html',
host: {'class': 'route'},
})
export class StyleguideComponent {
constructor( private ss: StyleguideService ) {}
}
Relevant HTML: styleguide.component.html
<a [routerLink]="[]" aria-current="page" class="crumbs__link crumbs__link--active" [title]="ss.pageTitle">{{ss.pageTitle}}</a>
Parent Module: styleguide.module.ts
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { FormsModule } from '#angular/forms';
import { StyleguideService } from './styleguide.service';
import { StyleguideComponent } from './styleguide.component';
import { TemplatesComponent } from './templates/templates.component';
...
#NgModule({
imports: [
CommonModule,
FormsModule,
...
],
declarations: [
StyleguideComponent,
TemplatesComponent,
...
],
exports: [
...
],
providers: [
StyleguideService
]
})
export class StyleguideModule {}
Service: styleguide.service.ts
import { Injectable } from '#angular/core';
#Injectable()
export class StyleguideService {
pageTitleS: string;
get pageTitle(): string {
console.log('get title: ', this.pageTitleS); // <-- Returns undefined
return this.pageTitleS;
}
set pageTitle(s: string) {
console.log('set title: ', s);
this.pageTitleS= s;
}
}
Child Component: templates.component.ts
import { Component } from '#angular/core';
import { StyleguideService } from '../styleguide.service';
#Component({
selector: 'templates',
templateUrl: './templates.component.html',
host: {'class': 'route__content'}
})
export class TemplatesComponent {
constructor( private ss: StyleguideService ) {
this.ss.pageTitle = "Templates";
}
}
You should implement the Service with Observables. A quick example would be something like this:
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {Injectable} from '#angular/core'
#Injectable()
export class Service {
private value: BehaviorSubject<string>;
constructor() {
this.value = <BehaviorSubject<string>>new BehaviorSubject();
}
setValue(value=""){
this.value.next(value);
}
getValue() {
return this.value.asObservable();
}
}
The Parent Component would subscribe to it like so:
import {Component, OnInit} from '#angular/core'
import { Service } from './service';
#Component({
selector: 'parent-component',
template: `
<div>
<h2>Value {{value}}</h2>
<child-component></child-component>
</div>
`,
})
export class ParentComponent implements OnInit {
value:string;
constructor(private service: Service) {
}
ngOnInit(){
this.service.getValue().subscribe((newValue)=>{
this.value = newValue;
})
}
}
And the Child Component would set the value and also subscribe to it like so:
import {Component, OnInit} from '#angular/core'
import { Service } from './service';
#Component({
selector: 'child-component',
template: `
<div>
<h2>Child Value {{value}}</h2>
</div>
`,
})
export class ChildComponent implements OnInit {
value:string;
constructor(private service: Service) {
this.service.setValue('New Value');
}
ngOnInit(){
this.service.getValue().subscribe((newValue)=>{
this.value = newValue;
})
}
}
Your setter is never called. You instantiate the service using StyleguideComponent, not the TemplatesComponent which does call the setter, and the constructor of StyleguideComponent does not call the setter on the service which is why the value remains undefined.
The TemplatesComponent has an element selector templates which I do not see in the styleguide.component.html you have in the question which is why I believe TemplatesComponent is never being created.
You are not calling the setter function in your child.component.ts instead you are setting the value of variable but I think you are accessing it wrongly as you are missing the last S in the variable name. You should be doing
export class TemplatesComponent {
constructor( private ss: StyleguideService ) {
this.ss.pageTitle("Templates");
// Now to get it you should call
this.ss.pageTitle(); // Should console.log the value
}
}
Okay so it was related to my routing setup, I didn't have my child routes setup correctly so this really had nothing to do with the getter/setter after all.

Call function from one component to another component Angularjs 2 [duplicate]

This question already has answers here:
How to communicate between component in Angular?
(8 answers)
Closed 5 years ago.
I want to pass value from one component to another component.
i.e., I need to pass value from Dashboard Component to Header Component
Here is my Dashboard Component
import{Component}from '#angular/core';
import { Header } from '../../layout/header.component';
export class Dashboard{
showAlert(id : any)
{
setItem(id);
}
}
Here is my Header component
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'header',
templateUrl: './header.component.html',
})
export class Header{
public setItem(id : any)
{
console.log('Exported value'+id)
}
}
But it is always giving Cannot find setItem
What is the mistake i am doing and how can i fix this ?
Note : I am doing this in Angularjs 2
If the element raises events, you can listen to them with an event binding. Refer to the angular doc https://angular.io/guide/template-syntax#event-binding for in depth knowledge.
Dashboard Component
import{Component}from '#angular/core';
import { Header } from '../../layout/header.component';
#Component({
selector: 'dashboard',
templateUrl: './dashboard.component.html',
})
export class Dashboard{
#Output() setItemEvent = new EventEmitter();
showAlert(id : any)
{
this.setItemEvent.emit(id);
}
}
Header component
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'header',
template: '<dashboard (setItemEvent)="setItem(param)"></dashboard>',
})
export class Header{
public setItem(id : any)
{
console.log('Exported value'+id)
}
}
You can use:
localStorage.setItem('name', 'value');
where name is the variable name you will use to access the value. You can access the value using:
var x = localStorage.getItem('name');
You can use event-binding like this :
////First Component
#component({
selector:'componentA',
})
export class ComponentA{
yourMethod(yourPassedParameter:any){...}
}
////// Second Component
#component({
selector:'componentB',
template: `<button (click)="yourMethod(yourParameter)">click</button>`
)
export class ComponentB{
#Output() eventLis = new EventEmitter();
yourMethod(yourParameter:any){...
this.eventLis.emit(yourData);
}
}
////Your crosscomponent
#component({
selector:'crosscomponent',
template: `<componentA #componentA></componentA>
<componentB (eventLis)="componentA.yourMethod(yourParameter)"></componentB>`
)
export class crosscomponent{
}

Passing value between two angular2 component typescript files

I have two components that are not parent and child components but i need to pass value from component A to component B.
example:
src/abc/cde/uij/componentA.ts has variable CustomerId = "ssss"
need to pas that variable customerID to src/abc/xyz/componentB.ts
Simple example:
Component A:
#Component({})
export class ComponentA {
constructor(private sharedService : SharedService) {}
sendMessage(msg : string) {
this.sharedService.send(msg);
}
}
Component B:
#Component({})
export class ComponentB {
constructor(private sharedService : SharedService) {
this.sharedService.stream$.subscribe(this.receiveMessage.bind(this));
}
receiveMessage(msg : string) {
console.log(msg); // your message from component A
}
}
Shared service:
#Injectable()
export class SharedService {
private _stream$ = new Rx.BehaviorSubject("");
public stream$ = this._stream$.asObservable();
send(msg : string) {
this._stream$.next(msg);
}
}
Shared service have to be placed in the same NgModule.
On your service define a setMyProperty() and a getMyProperty(). Then setMyProperty with a value from Component A. ComponentB then getMyProperty where you return the value...
You have to inject the service into both components.
You could give this a shot. Its pretty simple and straight forward.
I just followed THIS example and made some changes so that it could be siblings talking instead of parent/child.
my-service.service.ts
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class MyService {
// Observable string sources
private myAnnouncedSource = new Subject<string>();
// Observable string streams
myAnnounced$ = this.myAnnouncedSource.asObservable();
// Service message commands
announceItem(item: string) {
this.myAnnouncedSource.next(item);
}
}
my-comp1.component.ts
import { Component } from '#angular/core';
import { MyService } from './my-service.service';
#Component({
selector: 'my-compA',
template: `...`,
providers: [MyService]
})
export class MyComponentA {
constructor(private myService: MyService) {
}
announceToOtherComps() {
let sharedItem = "shibby";
this.myService.announceItem(sharedItem);
}
}
my-comp2.component.ts
import { Component, Input, OnDestroy } from '#angular/core';
import { MyService } from './my-service.service';
import { Subscription } from 'rxjs/Subscription';
#Component({
selector: 'my-compB',
template: `...`,
providers: [MyService]
})
export class MyComponentB implements OnDestroy {
sharedItem = '<no data>';
subscription: Subscription;
constructor(private myService: MyService) {
this.subscription = myService.myAnnounced$.subscribe(
item => {
this.sharedItem = item;
});
}
ngOnDestroy() {
// prevent memory leak when component destroyed
this.subscription.unsubscribe();
}
}
<component-a [id]="product.id"></component-a>
In the component-a ts file .Use it like below
export class ComponentA implements OnInit {
#Input() // <------------
id: number;
(...)
}

Using input/output events to trigger methods in a parent component in Angular 2

How can a child service notify a parent component of a change? I used to do this in angular 1 by $watching a variable in the child service. Unfortunately, this is no longer possible.
I tried injecting the service back into the component, but this fails, probably due to circular dependencies. Based on what I could find in current documentation, I came up with the code below:
AppComponent
|
SomeComponent
|
SomeService
AppComponent
#Component({
selector: '[app-component]',
templateUrl: 'partials/app.html',
directives: [
SomeComponent
],
providers: [
SomeService
]
})
export class AppComponent {
constructor() { }
}
bootstrap(AppComponent);
SomeComponent
import {Component, Input} from 'angular2/core'
import {SomeService} from '../services/some.service'
#Component({
selector: 'foo',
templateUrl: 'partials/foo.html'
})
export class SomeComponent {
constructor() {}
#Input set someEvent(value) {
console.log(value);
}
}
SomeService
import {EventEmitter, Output} from 'angular2/core'
export class CoreService {
constructor() {
this.someEvent = new EventEmitter();
}
#Output() someEvent: EventEmitter<any>;
public foo() {
this.someEvent.emit(true); // Or next(true)?
}
}
#Output must be used for components only not in services. At this level you can register on this event using the (...) syntax.
From the angular.io documentation (https://angular.io/docs/ts/latest/api/core/Output-var.html):
Declares an event-bound output property.
When an output property emits an event, an event handler attached to that event the template is invoked.
For a service you need to explicitly subscribe on this event, as described below:
import {Component, Input} from 'angular2/core'
import {SomeService} from '../services/some.service'
#Component({
selector: 'foo',
templateUrl: 'partials/foo.html'
})
export class SomeComponent {
constructor(service:CoreService) {
service.someEvent.subscribe((val) => {
console.log(value);
});
}
}

Categories