簡體   English   中英

帶引導事件的Angular2不會觸發可觀察到的更改

[英]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">&times;</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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM