Angular 6 @Viewchild 不支持延迟加载
Angular 6 @Viewchild is not working with lazy loading
这是我的代码,错误无法读取 属性 标题未定义。
Parent 组件
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)
}
}
而Child组件是。
@Component({
selector: 'child',
})
export class ChildComponet {
public title = "hi"
constructor(){}
}
routing.module.ts 就像
{
path: "",
component: ParentComponent,
children: [
{
path: '/child',
component: ChildComponent
}
]
}
并且给出的错误是
ERROR TypeError: Cannot read property 'title' of undefined(…)
确保在您的 parent.component.html
模板中添加了 <child></child>
标签。
这不是它应该的工作方式。如果您的 ParentComponent
模板中有 <app-child></app-child>
标签,您将只能在 ParentComponent
中获得 ChildComponent
.
像这样:
...
<app-child></app-child>
...
但是由于您使用的是子路由,并且 ChildComponent
将加载到 ParentComponent
的 router-outlet
上,您将无法使用 ViewChild
PS: 您只能在 ngAfterViewInit
内部访问它,因为 ViewChild
只能被认为是安全的,可以在之后实例化视图已加载:
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);
}
}
这里有一个 Working Sample StackBlitz 你的参考,说明了你在这两种情况下的情况。
PS: 要在 ParentComponent
中获取 ChildComponent
属性,使用路由,您必须使用 SharedService
或者你必须在路由中将 ChildProperty 作为 QueryParam 传递并使用 ActivatedRoute
在你的 ParentComponent 中读取它
更新:
使用路由查询参数共享数据:
虽然这没有多大意义,但在您的 ChildComponent
中,您可以有一个 Link 将用户路由到具有 title
属性 作为 queryParam
传递。像这样:
<a
[routerLink]="['/child']"
[queryParams]="{title: title}">
Go To Child Route With Query Params
</a>
并且在您的 ParentComponent
中可以像这样使用 ActivatedRoute
访问它:
...
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']);
});
...
}
...
}
使用 SharedService
只需创建一个带有 private
BehaviorSubject
的 SharedService
,通过调用 asObservable
方法将其公开为 Observable
。它的值可以通过公开一个方法(setChildProperty
)来设置,该方法本质上将使用更新的 childProperty
值调用 next
方法:
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);
}
}
然后您可以在 ParentComponent
和 ChildComponent
中注入它:
在ChildComponent
中设置值:
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);
}
}
然后在您的 ParentComponent
中获取值:
...
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)
);
}
...
}
我认为您缺少与创建组件相关的 'template' 或 'templateUrl'
父组件
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 {...}
子组件
@Component({
selector: 'child',
template: `<h1>{{ title }}</h1>`
})
export class ChildComponent {...} // Be sure to spell it right as yours were ChildComponet - missing 'n'
更新 根据用户对此线程的说明
已添加 Stackblitz Demo 供您参考 (检查控制台)
如果您想访问在父组件的 <router-outlet>
下呈现的子组件,您可以使用 (activate) supported 属性 of router-outlet:
A router outlet will emit an activate event any time a new component is being instantiated
ParentComponent 的模板
@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
这是我的代码,错误无法读取 属性 标题未定义。
Parent 组件
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)
}
}
而Child组件是。
@Component({
selector: 'child',
})
export class ChildComponet {
public title = "hi"
constructor(){}
}
routing.module.ts 就像
{
path: "",
component: ParentComponent,
children: [
{
path: '/child',
component: ChildComponent
}
]
}
并且给出的错误是
ERROR TypeError: Cannot read property 'title' of undefined(…)
确保在您的 parent.component.html
模板中添加了 <child></child>
标签。
这不是它应该的工作方式。如果您的 ParentComponent
模板中有 <app-child></app-child>
标签,您将只能在 ParentComponent
中获得 ChildComponent
.
像这样:
...
<app-child></app-child>
...
但是由于您使用的是子路由,并且 ChildComponent
将加载到 ParentComponent
的 router-outlet
上,您将无法使用 ViewChild
PS: 您只能在 ngAfterViewInit
内部访问它,因为 ViewChild
只能被认为是安全的,可以在之后实例化视图已加载:
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);
}
}
这里有一个 Working Sample StackBlitz 你的参考,说明了你在这两种情况下的情况。
PS: 要在 ParentComponent
中获取 ChildComponent
属性,使用路由,您必须使用 SharedService
或者你必须在路由中将 ChildProperty 作为 QueryParam 传递并使用 ActivatedRoute
更新:
使用路由查询参数共享数据:
虽然这没有多大意义,但在您的 ChildComponent
中,您可以有一个 Link 将用户路由到具有 title
属性 作为 queryParam
传递。像这样:
<a
[routerLink]="['/child']"
[queryParams]="{title: title}">
Go To Child Route With Query Params
</a>
并且在您的 ParentComponent
中可以像这样使用 ActivatedRoute
访问它:
...
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']);
});
...
}
...
}
使用 SharedService
只需创建一个带有 private
BehaviorSubject
的 SharedService
,通过调用 asObservable
方法将其公开为 Observable
。它的值可以通过公开一个方法(setChildProperty
)来设置,该方法本质上将使用更新的 childProperty
值调用 next
方法:
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);
}
}
然后您可以在 ParentComponent
和 ChildComponent
中注入它:
在ChildComponent
中设置值:
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);
}
}
然后在您的 ParentComponent
中获取值:
...
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)
);
}
...
}
我认为您缺少与创建组件相关的 'template' 或 'templateUrl'
父组件
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 {...}
子组件
@Component({
selector: 'child',
template: `<h1>{{ title }}</h1>`
})
export class ChildComponent {...} // Be sure to spell it right as yours were ChildComponet - missing 'n'
更新 根据用户对此线程的说明
已添加 Stackblitz Demo 供您参考 (检查控制台)
如果您想访问在父组件的 <router-outlet>
下呈现的子组件,您可以使用 (activate) supported 属性 of router-outlet:
A router outlet will emit an activate event any time a new component is being instantiated
ParentComponent 的模板
@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