带引导事件的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(); }); });
Interesting Posts
asp.net核心spa模板,语义UI
如何将DOM附加到Angular 2组件并仍然获取封装样式
Angular2在焦点事件上添加类
Angular – 是否可以为一个组件加载jquery插件?
如何关闭Angular 4中的Accordion中的所有菜单
使用Angular2的语义UI – 如何在组件中设置jQuery的侧栏设置?
Bootstrap无法在Angular 6应用程序中运行
访问jquery中的angular2变量
如何在Angular2 Typescript项目中全局导入Javascript库?
@HostListener OnClick for outside click在firefox中不起作用