WedX - журнал о программировании и компьютерных науках

Angular 2 RxJS Observable Пропуск подписки при первом звонке

Я использую общую службу для связи между рядом компонентов, которые находятся в модальном окне, и я пытаюсь получить значение, которое установлено в модальном окне в компоненте, в котором оно находится.

Однако, когда я подписываюсь на функцию getSelectedSourceField в своей службе, она пропускает подписку в первый раз, когда пользователь выбирает источник в модальном окне, но в последующие раз она работает так, как ожидалось (т. Е. Возвращает выбранное поле в обратном вызове успеха).

Есть предположения?

Моя служба:

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { Observable } from 'rxjs/Observable';
import { Source, SourceField } from '../source/source';
import { DataService } from './dataService';

@Injectable()
export class SFieldSelectService {
private selectedSource: Source = null;
private selectedSourceField: SourceField = null;
private filteredSourceFields: SourceField[] = [];

private selectedSourceSubj = new Subject<Source>();
private selectedSourceFieldSubj = new Subject<SourceField>();
private filteredSourceFieldsSubj = new Subject<SourceField[]>();
private hasSourceFieldSubj = new Subject<boolean>();

//Source methods
setSelectedSource(source: Source) {
    this.selectedSource = source;
    this.selectedSourceSubj.next(source);

    //Set the availabel source fields
    this._dataService.GetSingle('SourceSelect', this.selectedSource["sourceId"])
        .subscribe(sourceFields => {
            this.filteredSourceFields = sourceFields
            this.filteredSourceFieldsSubj.next(sourceFields);
        });   

    this.hasSourceFieldSubj.next(false);
}
getSelectedSource(): Observable<Source> {
    return this.selectedSourceSubj.asObservable();
}

//Sourcefield methods
setSelectedSourceField(sourceField: SourceField) {
    this.selectedSourceField = sourceField;
    this.selectedSourceFieldSubj.next(sourceField);

    this.hasSourceFieldSubj.next(true);
}
getSelectedSourceField(): Observable<SourceField> {
    return this.selectedSourceFieldSubj.asObservable();
}

//Filtered sourcefields
getFilteredSourceFields(): Observable<SourceField[]> {
    return this.filteredSourceFieldsSubj.asObservable();
}

//Whether or not the modal has a selected sourcefield
hasSelectedSourceField(): Observable<boolean> {
    return this.hasSourceFieldSubj.asObservable();
}

constructor(private _dataService: DataService) {}
}

Компонент, на который я подписываюсь:

import { Component, ViewChild, OnInit } from '@angular/core';
import { DataService } from '../../services/dataService';
import { Response, Headers } from '@angular/http';
import { Condition } from '../transformation';
import { NgbModal, ModalDismissReasons } from '@ng-bootstrap/ng-bootstrap';
import { SourceFieldListComponent } from '../../source/selection/sourcefield-list.component';
import { SourceListComponent } from '../../source/selection/source-list.component';
import { SFieldSelectService } from '../../services/source-select.service';

@Component({
    selector: 'condition-addedit',
    templateUrl: 'app/transformation/condition/condition-addedit.component.html',
    providers: [DataService, SFieldSelectService]
})
export class ConditionAddEditComponent implements OnInit {
    active: boolean = true;
    condSeqCount = 1;
    selectingCondition: Condition;
    hasSelectedSourceField: boolean = false;

    //List of Conditions currently in the add/edit list
    public conditions: Condition[] = [];

    //Modal Functions
    closeResult: string;
    openSourceSelect(content, condition) {
        this.selectingCondition = condition;
        this.modalService.open(content, { size: 'lg' }).result.then((result) => {
            //User selected source field in modal
            if (result == 'Select SField') {
                this.selectService.getSelectedSourceField()
                    .subscribe(sourceField => { <--- SKIPS THIS THE FIRST TIME BUT NOT AFTER
                        alert(sourceField.name);
                        this.selectingCondition.sourceField = sourceField
                    }
                , (error) => alert(error));
            }
        }, (reason) => {
            this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
        });
    }

    private getDismissReason(reason: any): string {
        if (reason === ModalDismissReasons.ESC) {
            return 'by pressing ESC';
        } else if (reason === ModalDismissReasons.BACKDROP_CLICK) {
            return 'by clicking on a backdrop';
        } else {
            return `with: ${reason}`;
        }
    }

    //Add a new condition to the list of conditions
    addCondition() {
        this.conditions.push(new Condition(this.condSeqCount++, (this.condSeqCount == 1) ? '' : 'or', '', '', '', '', null));
    }

    constructor(private _dataService: DataService, private modalService: NgbModal, private selectService: SFieldSelectService) {}
    ngOnInit(): void {
        this.selectService.hasSelectedSourceField().subscribe(hasSelectedSourceField => this.hasSelectedSourceField = hasSelectedSourceField);
    }
}

  • Я должен отметить, что мне удалось обойти это, просто подписавшись в ngOnInit и назначив переменной, а затем установив для нее selectionCondition. Однако мне все еще любопытно, что происходит в моем примере вопроса. 10.10.2016

Ответы:


1

Может быть, ngOnInit - лучший подход, не уверен. Но причина проблемы в использовании Subject. Если вы что-то испускаете, если во время выпуска никто не подписался, это излучение теряется навсегда.

Это ситуация, в которой ReplaySubject может быть полезным. Это позволяет вам установить размер буфера, и если в буфере есть что-то, когда кто-то подписывается, все они излучаются из этого буфера. Во многих случаях вам просто нужно, чтобы размер буфера был равен единице, так как вы хотите, чтобы буферизовался только последний (самый последний).

import { ReplaySubject } from 'rxjs/ReplaySubject';

class Service {
  something = new ReplaySubject<Source>(1); // buffer size 1
}

При этом вам не нужно пытаться сохранить поле для обслуживания самого последнего излучения. Он уже хранится в самом сабже.

10.10.2016
  • ReplaySubject вроде работает. Я думаю, что я продолжу и буду придерживаться ngOnInit, поскольку это вроде как лучшая практика, но я переключусь на ReplaySubject для этого свойства. Спасибо! 10.10.2016

  • 2

    Как предполагает @peeskillet, здесь может быть проблема с использованием Subject. Для описанного вами варианта использования я обычно использую BehaviorSubject. Вы можете инициализировать его с помощью null, если нет лучшего варианта.

    На самом деле, я думаю, что в большинстве случаев вы хотите использовать BehaviorSubject поверх Subject (или любого другого * Subject), если вы разделяете значения между компонентами / службами. Он просто дает вам «текущее / последнее» значение. Вот хороший сообщение на angular-university по этому поводу.

    10.10.2016
    Новые материалы

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..


    Для любых предложений по сайту: [email protected]