首页 文章

Angular 2表调用连接函数

提问于
浏览
2

Topic

md-table ui在Angular Material 2中实现cdk-table

Problem

用户调用的http调用返回响应后无法连接到emit

Approach

从服务中的行为主题创建一个热的可观察对象 . parent组件调用服务中的一个方法,该方法将一组对象提供给行为主题 . 子组件在其构造函数中订阅行为主体的热可观察性 . 子组件使用新接收的对象数组重新创建订阅方法中对数据源的引用

Expected Behavior

每次行为主题通过.next()提供新数据时,应该触发连接

Observed Behavior

connect方法仅在子组件的初始化时触发


父组件:

import { Component }                 from '@angular/core';

import { InboundMessagesService }       from '../messages/services/inbound/inbound.service';
import { Message }                      from '../messages/model/message';

@Component({
    selector: 'search-bar',
    templateUrl: './search-bar.component.html',
    styleUrls: [ './search-bar.component.css'],
    providers: [ InboundMessagesService ]
})

export class SearchBarComponent {
    hac: string = "";

    constructor( private inboundMessagesService: InboundMessagesService ) { }

    onSubmit( event: any ): void {
        this.hac = event.target.value;
        this.inboundMessagesService.submitHac( this.hac );
    }
}

服务:

import { Injectable }                 from '@angular/core';
import { Headers, 
         Http, 
         RequestMethod, 
         RequestOptions, 
         Response }                   from '@angular/http';
import { HttpErrorResponse }          from "@angular/common/http";
import { Observable }                 from 'rxjs/Rx';
import { Subject }                    from 'rxjs/Subject';
import { BehaviorSubject }            from 'rxjs/BehaviorSubject';
import { ReplaySubject }              from 'rxjs/ReplaySubject';
import { Subscription }               from 'rxjs/Subscription';
import "rxjs/add/operator/mergeMap";
import { Message }                    from '../../model/message'; 
import { LookupService }         from '../../../lookup/lookup.service';
@Injectable()
export class InboundMessagesService {
    dataChange: BehaviorSubject<Message[]> = new BehaviorSubject<Message[]>([]);
    dataChangeObservable = Observable.from( this.dataChange ).publish();
    messages: Message[];
    get data(): Message[] { 
        return this.dataChange.value; 
    }
    baseUrl: string = 'http://foobar/query?parameter=';
    headers = new Headers();
    options = new RequestOptions({ headers: this.headers });
    response: Observable<Response>;

    constructor( private http: Http, 
                 private lookupService: LookupService ) {
        console.log( "inboundService constructor - dataChange: ", this.dataChange );
        this.dataChangeObservable.connect()        
    }
    submitHac( hac: string ) {
        console.log( "submitHac received: ", hac );    

        this.getMessages( hac )
            .subscribe( ( messages: any ) => {
                this.dataChange.next( messages )
            }),
            ( err: HttpErrorResponse ) => {
                if ( err.error instanceof Error ) {
                    // A client-side or network error occurred. Handle it accordingly.
                    console.log( 'An error occurred:', err.error.message );
                } else {
                    // The backend returned an unsuccessful response code.
                    // The response body may contain clues as to what went wrong,
                    console.log( `Backend returned code ${ err.status }, body was: ${ err.error }` );
                    console.log( "full error: ", err );
                }
            };
    }
    getMessages( hac: string ) {
        console.log( "inboundService.getMessages( hac ) got: ", hac );
        return this.lookupService
            .getMailboxUuids( hac )
            .switchMap( 
                ( mailboxUuidsInResponse: Response ) => {
                    console.log( "lookup service returned: ", mailboxUuidsInResponse );
                    return this.http.get( this.baseUrl + mailboxUuidsInResponse.json(), this.options )
                })
            .map(
                ( messagesInResponse: any ) => {
                    console.log( "request returned these messages: ", messagesInResponse );
                    messagesInResponse.forEach( 
                        (message: any ) => {
                            this.messages.push( 
                                this.createMessage( message )
                    )});

                    return this.messages;
            })
    }
    createMessage( message: any ): Message {
        return new Message(
            message.name,
            message.type,
            message.contentType,
            message.state,
            message.source,
            message.target,
            message.additionalData
        )
    }
}

子组件:

import { Component }                  from '@angular/core';
import { HttpErrorResponse }          from "@angular/common/http";
import { DataSource, CdkTable }       from '@angular/cdk';
import { Observable }                 from 'rxjs/Observable';

import { Message }                    from '../../../messages/model/message';
import { InboundMessagesService }     from '../../../messages/services/inbound/inbound.service';
import { SearchBarComponent }         from '../../../search_bar/search-bar.component';

@Component({
    selector: 'inbound-messages',
    templateUrl: './../inbound-messages.component.html',
    styleUrls: [ 
        'app/mailboxes/mailboxes-layout.css',
        './../inbound-messages.component.css'      
    ],
    providers: [ InboundMessagesService ]
})

export class InboundMessagesComponent {
    dataSource: InboundDataSource | null;
    displayedColumns = [ 'name', 'type', 'contentType', 'state', 'source', 'target', 'additionalData' ];

    constructor( private inboundMessagesService: InboundMessagesService ) { 
        console.log( "inbound component constructor (this): ", this );
        this.inboundMessagesService.dataChangeObservable.connect();
    } 

    ngOnInit() {
        console.log( "inbound component ngOnInit()" );
        this.dataSource = new InboundDataSource( this.inboundMessagesService );        
    }
}

export class InboundDataSource extends DataSource<Message> {
        constructor( private inboundMessagesService: InboundMessagesService ) {
            super();
            console.log( "InboundDataSource constructor" );
        }

        connect(): Observable<Message[]> {
            console.log( "CONNECT called" );
            return this.inboundMessagesService.dataChangeObservable
        }

        disconnect() {}
    }

1 回答

  • 0

    我已经简化了一些特定于您的应用程序的细节,但是这显示了如何使用空数据集立即呈现表,然后在 SearchBarComponent 中调用 onSubmit 时从服务中获取新数据 .

    搜索组件

    @Component({ ... })
    export class SearchBarComponent {
    
      constructor(private inboundMessagingService: InboundMessagingService) { }
    
      onSubmit(event): void {
        this.inboundMessagingService.submitHac(event.target.value);
      }
    }
    

    服务

    @Injectable()
    export class InboundMessagingService {
    
      messageDataSubject$ = new BehaviorSubject<Message[]>([]);
    
      get data$(): Observable<Message[]> {
        return this.messageDataSubject$.asObservable();
      }
    
      constructor(
        private http: Http,
        private addressBookService: AddressBookService
      ) { }
    
      submitHac(hac: string): void {
        this.getMessages(hac)
          .subscribe((messages: Message[]) => this.messageDataSubject$.next(messages));
      }
    
      getMessages(hac: string): Observable<Message[]> {
        return this.addressBookService
          .getMailboxUuids(hac)
          .switchMap(x => this.http.get(x))
          .map(messagesInResponse => messagesInResponse.map(m => this.createMessage(m)))
      }
    
    
    }
    

    表组件

    @Component({ ... })
    export class InboundMessagesComponent {
    
      dataSource: InboundDataSource | null;
    
      displayedColumns = [ ... ];
    
      constructor(private inboundMessagesService: InboundMessagesService) { }
    
      ngOnInit() {
        this.dataSource = new InboundDataSource(this.inboundMessagesService);
      }
    }
    
    export class InboundDataSource extends DataSource<Message> {
    
      constructor(private inboundMessagesService: InboundMessagesService) { }
    
      /**
      * This is only called once, when `dataSource` is provided to the md/cdk-table. To
      * update the table rows, you must make sure the source observable emits again.
      * The way it is setup, this will emit whenever `messageDataSubject$.next()` is called
      * in the service.
      */
      connect(): Observable<Message[]> {
        // Since messageDataSubject$ is a behavior subject, it will immediately emit an empty array
        // when subscribed to. This will show as an empty table.
        return this.inboundMessagesService.data$;
      }
    
      diconnect() { }
    }
    

    其他说明

    • 很多人喜欢将 $ 添加到可观察变量名的末尾以区分它们 . 我在这里使用过那个惯例 .

    • 由于您要将 InboundMessagesService 添加到每个组件提供程序,因此最终将获得该服务的多个实例 . 您应该在模块级别提供此服务,如果要确保此服务在应用程序的生命周期中仅存在一次,请将其添加到根模块提供程序 .

相关问题