带引导事件的Angular2不会触发可观察到的更改

我发现了一个涉及bootstrapjs模态和angular2的奇怪问题。 我有一个绑定到bootstrap模式的html的组件:

// ... lots of imports here @Component({ selector: 'scrum-sessions-modal', bindings: [ScrumSessionService, ScrumSessionRepository, ScrumModuleDataContext, ElementRef] }) @View({ templateUrl: 'templates/modals/scrumSessions.modal.html', styleUrls: [''] }) export class ScrumSessionsModalComponent { private _elementRef: ElementRef; public sessions: ScrumSession[]; constructor(scrumSessionsProxy: ScrumSessionsProxy, scrumSessionService: ScrumSessionService, elementRef: ElementRef) { this._proxy = scrumSessionsProxy; this._scrumSessionService = scrumSessionService; this._elementRef = elementRef; // This is the bootstrap on shown event where i call the initialize method jQuery(this._elementRef.nativeElement).on("shown.bs.modal", () => { this.initialize(); }); jQuery("#scrumSessionsModal").on("hidden.bs.modal", () => { this.cleanup(); }); } public initialize() { // Start our hub proxy to the scrumsessionshub this._proxy.start().then(() => { // Fetch all active sessions; this._scrumSessionService.fetchActiveSessions().then((sessions: ScrumSession[]) => { // This console.log is being shown, however the sessions list is never populated in the view even though there are active sessions! console.log("loaded"); this.sessions = sessions; }); }); } // Free up memory and stop any event listeners/hubs private cleanup() { this.sessions = []; this._proxy.stop(); } } 

在这个模态中,我在构造函数中有一个事件监听器,用于检查何时显示模态。 当它这样做时,它调用初始化函数,该函数将加载将在模态中显示的初始数据。

模态的html如下所示:

 

我遇到的问题是由fetchactivesessions函数填充的sessions变量不会在视图中更新。 我在调用完成方法上放了一个调试器,它被调用了一个scrumsession对象的结果。

但是,当我删除on(“shown.bs.modal”)事件监听器并简单地将调用初始化()置于5秒的setTimeout函数内,然后打开模式时会正确填充会话列表。

它只发生在我将initialize调用放在bootstrap显示事件的回调中。 当调用在jquery回调中时,它就像停止检查更改一样。 有谁知道这笔交易是什么? 我可以只在构造函数中调用初始化,但我宁愿在bootstrap中调用显示的事件。

我已经发布了最新版本的应用程序,用于调试: http : //gbscrumboard.azurewebsites.net/

通过易于使用的服务包含在上面的解决方案:

更新:如果您使用的是rxjs-6,则需要进行重大更改

 import { fromEvent } from 'rxjs'; .. 

并使用

 .. fromEvent(document, BsEventsService.BS_COLLAPSIBLE_SHOWN); 

代替

 Observable.fromEvent(document, BsEventsService.BS_COLLAPSIBLE_SHOWN); 

BS-events.service.ts

 import { Subject, Observable, Subscription } from 'rxjs'; import { Injectable } from '@angular/core'; @Injectable() export class BsEventsService { private static BS_COLLAPSIBLE_SHOWN: string = 'shown.bs.collapse'; private static BS_COLLAPSIBLE_HIDDEN: string = 'hidden.bs.collapse'; constructor() { this.registerEvent(BsEventsService.BS_COLLAPSIBLE_SHOWN); this.registerEvent(BsEventsService.BS_COLLAPSIBLE_HIDDEN); } onCollapsibleShown(): Observable { return Observable.fromEvent(document, BsEventsService.BS_COLLAPSIBLE_SHOWN); } onCollapsibleHidden(): Observable { return Observable.fromEvent(document, BsEventsService.BS_COLLAPSIBLE_HIDDEN); } private registerEvent(event) { var script = document.createElement('script'); script.innerHTML = "eventRelay('" + event + "');" document.body.appendChild(script); } } 

的index.html

  ..    ..  

零件

 import { BsEventsService } from './service/bs-events.service'; import { Subject, Observable, Subscription } from 'rxjs'; .. private onCllapsibleShownSubject: Subscription; private onCllapsibleHiddenSubject: Subscription; .. constructor(.., private bsEventsService: BsEventsService) { .. } ngOnInit() { this.onCllapsibleShownSubject = this.bsEventsService.onCollapsibleShown().subscribe((event: any) => { console.log('shown: ' + event.target.id); }); this.onCllapsibleHiddenSubject = this.bsEventsService.onCollapsibleHidden().subscribe((event: any) => { console.log('hidden: ' + event.target.id); }); } ngOnDestroy() { this.onCllapsibleShownSubject.unsubscribe(); this.onCllapsibleHiddenSubject.unsubscribe(); } 

解决方案:使用Angular4 / Bootstrap4观察Bootstrap事件。

摘要:拦截引导事件,并在其位置触发自定义事件。 自定义事件将在角度组件内观察到。

index.html的:

  ...   

在您的组件/服务中:

 //dynamically execute the event relays private registerEvent(event){ var script = document.createElement('script'); script.innerHTML = "eventRelay('"+event+"');" document.body.appendChild(script); } 

在Component的构造函数或ngOnInit()中,您现在可以注册并观察引导事件。 例如,Bootstrap Modal’隐藏’事件。

 constructor(){ registerEvent('hidden.bs.modal'); Observable.fromEvent(document, 'hidden.bs.modal') .subscribe(($event) => { console.log('my component observed hidden.bs.modal'); //...insert your code here... }); } 

注意:’eventRelay’函数必须在index.html内,以便DOM加载它。 否则,当您从’registerEvent’内发出对’eventRelay’的调用时,将无法识别它。

结论:这是一个适用于vanilla Angular4 / Bootstrap4的中间件解决方案。 我不知道为什么bootstrap事件在angular中是不可见的,我还没有找到任何其他解决方案。

注1:每次事件只调用一次registerEvent。 这意味着在整个应用程序中“一次”,因此请考虑将所有registerEvent调用放在app.component.ts中。 多次调用registerEvent将导致重复事件被发送到角度。

注意2:有一个替代的bootstrap框架可以使用角度调用ngx-bootstrap( https://valor-software.com/ngx-bootstrap/#/ ),它可能使引导事件可见,但是我还没有测试过。

高级解决方案:创建一个Angular Service来管理通过’registerEvent’注册的事件,因此它只为每个事件调用一次’registerEvent’。 这样,在组件中,您可以调用’registerEvent’,然后立即为该事件创建Observable,而不必担心在其他组件中对’registerEvent’的重复调用。

用Mark提供的链接解决了这个问题。 显然,角度不知道引导事件,我必须告诉zone.js手动触发变化检测。

 this._elementRef = elementRef; jQuery(this._elementRef.nativeElement).on("shown.bs.modal", () => { this._zone.run(() => { this.initialize(); }); });