[英]Angular2 with bootstrap events does not trigger observable changes
我發現了一個涉及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如下所示:
<div class="modal fade" id="scrumSessionsModal" tabindex="-1" role="dialog" aria-labelledby="scrumSessionsModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel">Actieve sessies </h4>
</div>
<div class="modal-body">
<table class="table">
<tr>
<th>Id</th>
<th>Maker</th>
<th>Start datum</th>
<th>Aantal deelnemers</th>
</tr>
<tr *ngFor="#session of sessions">
<td>
{{ session.id }}
</td>
<td>
test
</td>
<td>
test
</td>
<td>
test
</td>
</tr>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
我遇到的問題是由fetchactivesessions函數填充的sessions變量不會在視圖中更新。 我在調用完成方法上放了一個調試器,並且調用了一個scrumsession對象的結果。
但是,當我刪除on(“shown.bs.modal”)事件監聽器並簡單地將調用初始化()放在5秒的setTimeout函數內,然后打開模態時會正確填充會話列表。
它只發生在我將initialize調用放在bootstrap顯示事件的回調中。 當調用在jquery回調中時,它就像停止檢查更改一樣。 有誰知道這筆交易是什么? 我可以只在構造函數中調用初始化,但我寧願調用bootstrap中顯示的事件。
我已經發布了最新版本的應用程序,用於調試: http : //gbscrumboard.azurewebsites.net/
解決方案:使用Angular4 / Bootstrap4觀察Bootstrap事件。
摘要:攔截引導事件,並在其位置觸發自定義事件。 自定義事件將在角度組件內觀察到。
index.html的:
<body>
...
<script>
function eventRelay(bs_event){
$('body').on(bs_event, function($event){
const customEvent = document.createEvent('Event');
customEvent.initEvent(bs_event, true, true);
let target_id = '#'+$event.target.id;
$(target_id)[0].dispatchEvent(customEvent);
});
</script>
</body>
在您的組件/服務中:
//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提供的鏈接解決了這個問題。 顯然,angular不知道bootstrap事件,我必須告訴zone.js手動觸發更改檢測。
this._elementRef = elementRef;
jQuery(this._elementRef.nativeElement).on("shown.bs.modal", () => {
this._zone.run(() => {
this.initialize();
});
});
通過易於使用的服務包含在解決方案之上:
更新:如果您使用的是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<Event> {
return Observable.fromEvent(document, BsEventsService.BS_COLLAPSIBLE_SHOWN);
}
onCollapsibleHidden(): Observable<Event> {
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
<app-root></app-root>
..
<!-- relay bootstrap events to angular - start -->
<script>
function eventRelay(bs_event) {
$('body').on(bs_event, function ($event) {
const customEvent = document.createEvent('Event');
customEvent.initEvent(bs_event, true, true);
let target_id = '#' + $event.target.id;
$(target_id)[0].dispatchEvent(customEvent);
});
}
</script>
<!-- relay bootstrap events to angular - end -->
..
</body>
零件
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();
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.