Angular 2+ 嵌套兄弟组件事件捕获及视图同步求助
我来帮你搞定这个视图不同步的问题,核心原因是日历组件内部切换视图时没有通知父组件,另外模板里的枚举引用也有小问题,一步步来修复:
第一步:修复日历模板的枚举引用错误
先看你的tm-calendar.html,*ngSwitchCase用了字符串'CalendarView.Month',这是不对的——模板里直接用枚举值就行,不需要加引号,否则Angular会把它当成普通字符串,匹配不上你的CalendarView枚举。修改后的模板:
<div [ngSwitch]="view"> <mwl-calendar-month-view *ngSwitchCase="CalendarView.Month" [viewDate]="viewDate" [events]="events" (dayClicked)="onDayClicked($event)"> </mwl-calendar-month-view> <mwl-calendar-week-view *ngSwitchCase="CalendarView.Week" [viewDate]="viewDate" [events]="events" (dayHeaderClicked)="onDayHeaderClicked($event)"> </mwl-calendar-week-view> <mwl-calendar-day-view *ngSwitchCase="CalendarView.Day" [viewDate]="viewDate" [events]="events"> </mwl-calendar-day-view> </div>
这里我把事件处理抽成了组件方法,方便统一处理emit逻辑,避免模板里写太多代码。
第二步:在日历组件中触发视图变化事件
接下来修改tm-calendar.ts,添加事件处理方法,当内部切换视图时,主动emitviewChange事件,让父组件同步更新selectedView,进而传递给导航栏:
@Component({ selector: "tm-calendar", templateUrl: "./calendar.component.html", changeDetection: ChangeDetectionStrategy.OnPush, styleUrls: ["./calendar.component.scss"], providers: [ { provide: CalendarEventTitleFormatter, useClass: CustomEventTitleFormatter } ] }) export class CalendarComponent implements OnInit, OnChanges { @Input() view: CalendarView = CalendarView.Week; @Input() viewDate: Date = new Date(); @Input() locale="de"; @Output() openEvent: EventEmitter<CalendarEvent> = new EventEmitter(); @Output() newEvent: EventEmitter<Date> = new EventEmitter(); @Output() viewChange: EventEmitter<CalendarView> = new EventEmitter(); // 要把枚举暴露给模板使用 CalendarView = CalendarView; constructor() { } ngOnInit() { } ngOnChanges(changes: SimpleChanges) { // 可选:如果需要响应外部视图变化,可以在这里处理 } onDayClicked(event: any) { this.viewDate = event.day.date; this.view = CalendarView.Day; // 关键:emit视图变化事件 this.viewChange.emit(this.view); } onDayHeaderClicked(event: any) { this.viewDate = event.day.date; this.view = CalendarView.Day; // 关键:emit视图变化事件 this.viewChange.emit(this.view); } }
这里注意要把CalendarView枚举赋值给组件的同名属性,这样模板里才能正确引用它。另外,不管是月视图点击日期还是周视图点击头部切换到日视图,都要调用viewChange.emit()把新的视图状态通知给父组件。
第三步:确认父组件的双向绑定逻辑
你的app.component.html里已经用了[(view)]="selectedView",这个语法是Angular的双向绑定糖,它会自动关联@Input() view和@Output() viewChange,所以只要日历组件正确emitviewChange,父组件的selectedView就会自动更新,进而同步到导航栏的[(view)]="selectedView"绑定上,导航栏的按钮状态也就跟着变了。
为什么之前不同步?
之前的问题在于:日历组件内部修改了view属性,但没有触发viewChange事件,父组件的selectedView根本不知道日历已经切换了视图,所以导航栏的view输入还是原来的值,按钮的激活状态自然不会更新。另外模板里的枚举字符串引用也可能导致视图切换不生效,双重问题叠加就出现了同步异常。
现在修改完之后,不管是从导航栏切换视图,还是从日历内部切换视图,两者的状态都会保持一致啦!
内容的提问来源于stack exchange,提问作者Juliet Rybets

