Angular 5 Uncaught(承诺):TypeError:无法分配给只读属性

时间:2018-01-05 11:43:50

标签: angular promise rxjs

我需要在切换到其他路线时取消订阅请求。在我看来,无论请求发送到哪里(解析器,组件)。在我的代码中,请求的基本发送由解析器执行

@Injectable()
  export class RoomsResolve implements Resolve<boolean> {
constructor(
    private roomsService: RoomsService,
    private store: Store<fromRooms.IRoomsState>,
    private resolveRequestUnsubscribeService: ResolveRequestUnsubscribeService
) {}

public resolve(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
): boolean {
    this.store.select(fromRooms.getRoomsLoaded).pipe(
        take(1)
    ).subscribe((loaded) => {
        if (!loaded) {
            this.store.dispatch(new fromRooms.GetRoomList({
                requestParams: {
                    lpuId: 204
                },
                requestOptions: {
                    unsubscribe: this.resolveRequestUnsubscribeService.createUnsubscriber(state.url, 'getRoomList')
                }
            }));
        }
    });

    return true;
}

}

这里我们发送一个动作来请求来自后端服务的数据,并为请求创建一个取消器

以下服务负责取消者的操作:

import { startWith } from 'rxjs/operator/startWith';
import { Injectable } from '@angular/core';
import { ActivatedRoute, NavigationStart, Router, RouterEvent, 
RoutesRecognized } from '@angular/router';
import { Subject } from 'rxjs/Subject';
import * as moment from 'moment';
import * as _ from 'lodash';

export interface IUnsubscribeCache {
    [name: string]: IUnsubscribeCacheItem;
}

export interface IUnsubscribeCacheItem {
    liveTime: number;
    unsubscribers: {[name: string]: Subject<boolean>};
}

@Injectable()
export class ResolveRequestUnsubscribeService {
   public unsubscribeCache: IUnsubscribeCache = {};
   private unsubscriberLiveTime: number = 60000;

constructor(
    private router: Router,
    private activatedRoute: ActivatedRoute
) {
    //
    console.log('INIT_ResolveRequestUnsubscribeService');

    this.watchChangeURL();
}

private watchChangeURL(): void {
    this.router.events
        .filter((e) => e instanceof RoutesRecognized)
        .pairwise()
        .subscribe((event: any[]) => {
            const prevPath: string = event[0].urlAfterRedirects;

            !!prevPath && this.unsubscribeRequests(this.convertPathToUnsubscribersGroupName(prevPath));

            console.log('PREV_URL', event, event[0].urlAfterRedirects);
        });
}

private removeUnfulfilledUnsubscribers(): void {
    const unfulfilledUnsubscribers: string[] = [];

    const now = moment().valueOf();

    _.forIn(this.unsubscribeCache, (value: IUnsubscribeCacheItem, key) => {
        if (value.liveTime + this.unsubscriberLiveTime <= now) {
            unfulfilledUnsubscribers.push(key);
        }
    });

    _.omit(this.unsubscribeCache, unfulfilledUnsubscribers);
}

public createUnsubscriber(path: string, methodName?: string): Subject<boolean> {
    this.removeUnfulfilledUnsubscribers();

    const groupName = this.convertPathToUnsubscribersGroupName(path);

    const unsubscribersGroup = this.unsubscribeCache[groupName];

    !unsubscribersGroup && this.addUnsubscribersGroupToCache(groupName);

    this.unsubscribeCache[groupName].unsubscribers[methodName] = new Subject<boolean>();

    return this.unsubscribeCache[groupName].unsubscribers[methodName];
}

private convertPathToUnsubscribersGroupName(path: string): string {
    return path.replace('/', '_');
}

private addUnsubscribersGroupToCache(groupName: string): void {
    this.unsubscribeCache[groupName] = {
        liveTime: moment().valueOf(),
        unsubscribers: {}
    };
}

private unsubscribeRequests(name: string): void {
    this.removeUnfulfilledUnsubscribers();

    const cache: IUnsubscribeCacheItem = this.unsubscribeCache[name];

    if (cache) {
        if (_.size(cache.unsubscribers) > 0) {
            _.forIn(this.unsubscribeCache[name].unsubscribers, (value: Subject<boolean>, key: string) => {
                value.next(false);
                value.complete();
            });
        }

        _.omit(this.unsubscribeCache, name);
    }
}

}

工作的逻辑很简单:创建缓存,当我们转到路由并执行请求,将缓存写入缓存并将其发送到请求,在转换到另一个路由期间,我们取消所有请求从以前的路线。

对于http请求实现了包装器:

import * as _ from 'lodash';
import { Config } from '../../services/config/config';
import { ConfigService } from '../../services/config/config.service';
import { RepositoryError } from './repository-error.model';
import { IRepositoryCachePromise } from './repository-cache-
promise.interface';
import { IRepositoryRequestParams } from './repository-request-
params.interface';
import { HttpClient, HttpClientModule, HttpErrorResponse, HttpHeaders, 
HttpResponse } from '@angular/common/http';
import { Subject } from 'rxjs/Subject';
import { AsyncSubject } from 'rxjs/AsyncSubject';

export class Repository {

    protected config: Config = null;
    protected connectorUrl: string = '';
    protected cachePromisses: IRepositoryCachePromise[] = [];

public constructor(
    protected http: HttpClient,
    protected configService: ConfigService
) {
    //
}

protected request(params: IRepositoryRequestParams): Promise<any> {
    const url: string = this.getConnectorUrl(params.action);

    return this.http.request(
        params.method,
        url,
        {
            body: JSON.stringify(params.data),
            headers: {
                'Content-Type': 'application/json',
                Authorization2: params.options.user.getAuthInfo().getAuthToken(),
                lpu_id: params.options.user.getUserInfo().getCurrentHospital().getPlaceID(),
                user_role_id: params.options.user.getUserInfo().getCurrentRole().getID(),
                job_id: params.options.user.getUserInfo().getCurrentJob().getContractID(),
                speciality_code: params.options.user.getUserInfo().getCurrentJob().getID(),
                available_resource_id: params.options.user.getUserInfo().getCurrentJob().getCurrentResource()
                    ? params.options.user.getUserInfo().getCurrentJob().getCurrentResource().getID() : ''
            }
        })
        .takeUntil(params.options.unsubscribe || new Subject<boolean>())
        .toPromise()
        .then((response) => {
            const isError: boolean = this.hasError(response);

            return isError ? Promise.reject(response) : response;
        })
        .catch((error: HttpErrorResponse) => Promise.reject(this.getError(error)));
}

private hasError(response): boolean {
    return !response.success && (response.error
        || (response.warnings && response.warnings.length) || response.errorMessage);
}

private getError(response): RepositoryError {
    return new RepositoryError().parse(response);
}

protected getConnectorUrl(action: string = ''): string {
    if (!this.config) {
        this.getConfig();
    }
    return `${this.config.getApiUrl(this.connectorUrl)}/${action}.api`;
}

private getConfig(): void {
    this.config = this.configService.getConfig();
}

public getCacheKey(url: string, params: any): string {
    params = _.cloneDeep(params);

    if (_.has(params, 'options.user.userInfo')) {
        delete params.options.user.userInfo.contracts;
        delete params.options.user.userInfo.nativeUserInfo;
        delete params.options.user.userInfo.workplaces;
        delete params.options.user.userInfo.roles;
    }

    if (_.has(params, 'options.user.userInfo.currentJob')) {
        delete params.options.user.userInfo.currentJob.resources;
    }

    if (_.has(params, 'options.user.userInfo.currentRole')) {
        delete params.options.user.userInfo.currentRole.allowRights;
        delete params.options.user.userInfo.currentRole.disallowRights;
    }

    if (_.has(params, 'options')) {
        delete params.options.forced;
        delete params.options.cacheLifeTime;
    }

    return url + '?' + JSON.stringify(params);
}

}

要取消请求,我尝试使用方法.takeUntil(params.options.unsubscribe || new Subject<boolean>()),从ResolveRequestUnsubscribeService传递取消器

当我尝试在控制台中取消请求时,出现错误:

  

core.js:1427 ERROR错误:未捕获(在承诺中):TypeError:无法分配给对象'[object Object]'的只读属性'isStopped'   TypeError:无法分配给对象'[object Object]'

的只读属性'isStopped'

错误导致此行:

  

value.complete();

ResolveRequestUnsubscribeService - &gt; unsubscribeRequests

请帮助我理解我做错了什么

抱歉我的英文。

我无法创建可行的示例

0 个答案:

没有答案