Angular 4 - 从应用程序组件调用 child 组件方法
Angular 4 - invoking child component method from app component
简而言之,我有一个 Angular 4 应用程序,它有一个 header 包含一个 android 启发的浮动操作按钮,单击此按钮时应该执行一个方法当前活动的组件(基本上是页面内容)。我正在使用 angular 4 路由器,如果这有什么不同的话,它引起了对 View Child 方法的一些担忧,因为我不会总是确定哪个组件当前处于活动状态,如果可能的话我会喜欢避免向每个模板添加 #binding
属性。
我已经通过 ViewChild 装饰器、ContentChild 装饰器、事件、服务尝试过,到目前为止绝对 没有任何 起作用,我'我完全卡住了。在这一点上,我不确定这是否真的很复杂,或者只是 PEBCAK。
有问题的按钮包含在 app.component
-
中
app.component.ts
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
currentUrl: string = null;
constructor(private router: Router, private currentRoute: ActivatedRoute) {}
ngOnInit() {
this.router.events.subscribe((val) => {
this.currentUrl = this.router.url;
});
}
}
app.component.html
(为简洁起见,只是按钮)
<div class="fab-container">
<button class="btn btn-warning btn-fab" (click)="fabAction()">
+
</button>
这里的大意是当这个按钮被点击时,它会在child组件上执行fabAction()
方法;
@Component({
selector: 'app-course-list',
templateUrl: './course-list.component.html',
styleUrls: ['./course-list.component.scss']
})
export class CourseListComponent implements OnInit {
courses: Course[];
showFilter: boolean = false;
constructor(
private courseService: CourseService
) { }
ngOnInit(): void {
this.getCourses();
}
getCourses(): void {
this.courseService.getCourses().then(courses => this.courses = courses);
}
// Should execute this method when button is clicked on app component
fabAction(): void {
console.log('responding to click on parent component');
this.showFilter = !this.showFilter;
}
}
</div>
一件毫无意义的事情是,当在应用程序组件上单击 fab 按钮时,每个组件都会执行自己的操作,有些会打开模式,其他会 show/hide 东西,有些会重定向,所以它能够在组件本身上指定此行为真的很棒。
阅读关于此的官方 angular 文档给我留下的问题多于答案,因此我将其全部恢复原样,希望有人可以提出更理想的解决方法。
编辑 - 根据要求 app.component.html
的完整标记
<header *ngIf="currentUrl != '/landing'">
<div class="brand">
<img src="/assets/circle-logo.png" />
</div>
<nav>
<div class="page-title">
<h2>{{ page?.title }}</h2>
<h4>{{ page?.strapline }}</h4>
</div>
<ul class="header-nav">
<li [routerLink]="['/courses']">Courses</li>
<li>Dashboard</li>
<li>Community</li>
<li>Blog</li>
<li>About</li>
</ul>
<div class="fab-container">
<button class="btn btn-warning btn-fab" (click)="fabAction()">
+
</button>
</div>
</nav>
</header>
<router-outlet></router-outlet>
<app-footer></app-footer>
这是我的 app-routing 模块,我在其中定义了我的路线以及为哪条路线显示哪个组件(就像在英雄之旅 angular 4 演示中所做的那样)
const routes: Routes = [
{ path: '', redirectTo: 'landing', pathMatch: 'full' },
{ path: 'landing', component: LandingComponent },
{ path: 'courses', component: CourseListComponent },
{ path: 'course/:id', component: CourseComponent }
];
@NgModule({
imports: [ RouterModule.forRoot(routes) ],
exports: [ RouterModule ]
})
export class AppRoutingModule {}
要访问与当前路由关联的组件实例,您可以利用 OnActivate 挂钩接口在路由激活时将此实例设置为共享服务:
@Component({
selector: 'some-cmp',
template: `
<div>routerOnActivate: {{log}}</div>
`})
export class AppComponent implements OnActivate {
constructor(private service:RouteStateService) {
}
routerOnActivate(next: ComponentInstruction, prev: ComponentInstruction) {
this.service.setCurrentRouteComponent(this);
}
}
然后您就可以访问组件实例并以这种方式访问函数:
var component = this.service.getCurrentRouteComponent();
component.somefunction()
尚未测试代码,请查看 了解更多信息。希望这有帮助。
创建一个具有 RxJS 主题的服务,您可以在按下 FAB 时调用 next
。然后在任何关心的组件中,您可以subscribe
到主题。
import { Subject } from 'rxjs/Subject'
@Injectable()
export class FabService {
clickSubject = new Subject();
}
app.component
@Component(...)
export class AppComponent {
constructor(private fabService: FabService) {}
fabAction() {
this.fabService.clickSubject.next();
}
}
child.component
@Component(...)
export class ChildComponent implements OnInit, OnDestroy {
private clickSub: Subscription;
constructor(private fabService: FabService) {}
ngOnInit() {
this.clickSub = this.fabService.clickSubject.subscribe(() => {
// FAB was clicked
});
}
ngOnDestroy() {
this.clickSub.unsubscribe();
}
}
我能想到的唯一可能不起作用的情况是如果您延迟加载功能模块,因为这些模块将获得 FabService 的单独实例(或您最终调用它的任何实例)。我认为有办法解决这个问题,但我一时想不起来。
简而言之,我有一个 Angular 4 应用程序,它有一个 header 包含一个 android 启发的浮动操作按钮,单击此按钮时应该执行一个方法当前活动的组件(基本上是页面内容)。我正在使用 angular 4 路由器,如果这有什么不同的话,它引起了对 View Child 方法的一些担忧,因为我不会总是确定哪个组件当前处于活动状态,如果可能的话我会喜欢避免向每个模板添加 #binding
属性。
我已经通过 ViewChild 装饰器、ContentChild 装饰器、事件、服务尝试过,到目前为止绝对 没有任何 起作用,我'我完全卡住了。在这一点上,我不确定这是否真的很复杂,或者只是 PEBCAK。
有问题的按钮包含在 app.component
-
app.component.ts
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
currentUrl: string = null;
constructor(private router: Router, private currentRoute: ActivatedRoute) {}
ngOnInit() {
this.router.events.subscribe((val) => {
this.currentUrl = this.router.url;
});
}
}
app.component.html
(为简洁起见,只是按钮)
<div class="fab-container">
<button class="btn btn-warning btn-fab" (click)="fabAction()">
+
</button>
这里的大意是当这个按钮被点击时,它会在child组件上执行fabAction()
方法;
@Component({
selector: 'app-course-list',
templateUrl: './course-list.component.html',
styleUrls: ['./course-list.component.scss']
})
export class CourseListComponent implements OnInit {
courses: Course[];
showFilter: boolean = false;
constructor(
private courseService: CourseService
) { }
ngOnInit(): void {
this.getCourses();
}
getCourses(): void {
this.courseService.getCourses().then(courses => this.courses = courses);
}
// Should execute this method when button is clicked on app component
fabAction(): void {
console.log('responding to click on parent component');
this.showFilter = !this.showFilter;
}
}
</div>
一件毫无意义的事情是,当在应用程序组件上单击 fab 按钮时,每个组件都会执行自己的操作,有些会打开模式,其他会 show/hide 东西,有些会重定向,所以它能够在组件本身上指定此行为真的很棒。
阅读关于此的官方 angular 文档给我留下的问题多于答案,因此我将其全部恢复原样,希望有人可以提出更理想的解决方法。
编辑 - 根据要求 app.component.html
的完整标记
<header *ngIf="currentUrl != '/landing'">
<div class="brand">
<img src="/assets/circle-logo.png" />
</div>
<nav>
<div class="page-title">
<h2>{{ page?.title }}</h2>
<h4>{{ page?.strapline }}</h4>
</div>
<ul class="header-nav">
<li [routerLink]="['/courses']">Courses</li>
<li>Dashboard</li>
<li>Community</li>
<li>Blog</li>
<li>About</li>
</ul>
<div class="fab-container">
<button class="btn btn-warning btn-fab" (click)="fabAction()">
+
</button>
</div>
</nav>
</header>
<router-outlet></router-outlet>
<app-footer></app-footer>
这是我的 app-routing 模块,我在其中定义了我的路线以及为哪条路线显示哪个组件(就像在英雄之旅 angular 4 演示中所做的那样)
const routes: Routes = [
{ path: '', redirectTo: 'landing', pathMatch: 'full' },
{ path: 'landing', component: LandingComponent },
{ path: 'courses', component: CourseListComponent },
{ path: 'course/:id', component: CourseComponent }
];
@NgModule({
imports: [ RouterModule.forRoot(routes) ],
exports: [ RouterModule ]
})
export class AppRoutingModule {}
要访问与当前路由关联的组件实例,您可以利用 OnActivate 挂钩接口在路由激活时将此实例设置为共享服务:
@Component({
selector: 'some-cmp',
template: `
<div>routerOnActivate: {{log}}</div>
`})
export class AppComponent implements OnActivate {
constructor(private service:RouteStateService) {
}
routerOnActivate(next: ComponentInstruction, prev: ComponentInstruction) {
this.service.setCurrentRouteComponent(this);
}
}
然后您就可以访问组件实例并以这种方式访问函数:
var component = this.service.getCurrentRouteComponent();
component.somefunction()
尚未测试代码,请查看
创建一个具有 RxJS 主题的服务,您可以在按下 FAB 时调用 next
。然后在任何关心的组件中,您可以subscribe
到主题。
import { Subject } from 'rxjs/Subject'
@Injectable()
export class FabService {
clickSubject = new Subject();
}
app.component
@Component(...)
export class AppComponent {
constructor(private fabService: FabService) {}
fabAction() {
this.fabService.clickSubject.next();
}
}
child.component
@Component(...)
export class ChildComponent implements OnInit, OnDestroy {
private clickSub: Subscription;
constructor(private fabService: FabService) {}
ngOnInit() {
this.clickSub = this.fabService.clickSubject.subscribe(() => {
// FAB was clicked
});
}
ngOnDestroy() {
this.clickSub.unsubscribe();
}
}
我能想到的唯一可能不起作用的情况是如果您延迟加载功能模块,因为这些模块将获得 FabService 的单独实例(或您最终调用它的任何实例)。我认为有办法解决这个问题,但我一时想不起来。