Angular 5 ngOnInit вызывается более одного раза - PullRequest
0 голосов
/ 14 октября 2019

У меня есть компонент для выбора периодов для некоторых графиков. Это могут быть абсолютные даты или короткие интервалы (последние 30 дней и т. Д.). Затем, если прошло менее 14 дней, вы получите данные с интервалом в 1 час по умолчанию. Если его больше 14 дней, вы получаете 1-дневный интервал (и не можете выбрать 1 час). Проблема в том, что когда я выбираю абсолютную дату с менее чем 14 днями - сначала я получаю данные за 1-часовой интервал - это нормально - но потом я хочу изменить данные на 1-дневный интервал - сначала я получаю их, но во-вторых, вызывается ngOnInitснова - и тогда, конечно, интервал по умолчанию установлен. Может кто-нибудь сказать мне, почему ngOnInit вызывается снова?

Я использую этот компонент в трех местах - и я хочу, чтобы они совместно использовали одни и те же данные - поэтому у меня есть и сервис для него, из которого я получаю данные вмои interval-chooser.component родители и затем я получаю их на @Input() - в ngOnChanges, только если данные отличаются. Я также попытался не использовать @Input, но получить данные общего интервала из Observable для самого компонента (но это не решает мою проблему).

Моя проблема в том, что этот метод вызывается из ngOnInit с false, поэтому мой интервал изменяется. Это нормально в первый раз, но не более ...

private setDefaultInterval(changeIntervalOnly: boolean) {
    if (!changeIntervalOnly) {
    if (this.disableIntervalSwitch || this.hideIntervalSwitch) {
        this.interval = '1d';
    } else {
        this.interval = '1h';
    }
    }
}

Я думаю, что мне нужно поделиться целым кодом компонента. Прямо сейчас я устанавливаю переменные для своего шаблона с помощью @Input intervalData в ngOnChanges, но я также пытаюсь подписать его, как вы видите в комментарии:

import {Component, HostListener, Input, OnChanges, OnInit, SimpleChanges} from '@angular/core';
import {IntervalChooserData, IntervalEnum, IntervalService, PeriodsInterval} from '../interval.service';
import {ElementService} from '../element.service';
import {Subscription} from 'rxjs/Subscription';


@Component({
    selector: 'app-interval-chooser',
    templateUrl: './interval-chooser.component.html',
    styleUrls: ['./interval-chooser.component.less']
})

export class IntervalChooserComponent implements OnInit, OnChanges {
    @Input() hideIntervalSwitch: boolean;
    @Input() intervalData: IntervalChooserData;
    mode = 'date';
    intervalType: string;
    selectedPeriod: PeriodsInterval;
    periods: PeriodsInterval[] = [];
    pickerMaxDate: Date;
    dateFrom: Date;
    dateTo: Date;
    lastDateFrom: Date;
    lastDateTo: Date;
    setMessage: boolean;
    timeZone: string;
    intervals = ['1d', '1h'];
    interval: string;
    disableIntervalSwitch = false;
    radioButtonClass = 'grouped fields';
    subscription: Subscription;
    dataSet = false;
    @HostListener('window:resize') onResize() {
        this.checkResize();
    }

    constructor(private intervalService: IntervalService, private elementService: ElementService) {
        this.periods = this.intervalService.periods;
    }

    ngOnInit() {
        console.log('NG OnInit CALLED !!!!!!!!!!!!!!!!!')
        this.pickerMaxDate = new Date();
        if (typeof this.timeZone === 'undefined') {
            console.log('callled once with true', this.timeZone);
            this.setVariablesFromSharedIntervalData(true);
        }
        // this.subscription = this.intervalService.getSharedIntervalDataObservable().subscribe(intervalData => {
        //  if (this.intervalService.isDifferentIntervalData(intervalData, this.intervalData)) {
        //      this.intervalData = intervalData;
        //      console.log('set variables', this.intervalData);
        //      this.setVariablesFromSharedIntervalData(false);
        //      // this.cdRef.detectChanges();
        //  }
        // });
        this.checkResize();
    }

    ngOnChanges(changes: SimpleChanges): void {
        if (!changes.intervalData.previousValue || changes.intervalData &&
            this.intervalService.isDifferentIntervalData(changes.intervalData.currentValue, changes.intervalData.previousValue)) {
            console.log('set variables', this.intervalData);
            this.setVariablesFromSharedIntervalData(false);
        }
    }

    setVariablesFromSharedIntervalData(changeInterval: boolean) {
        console.log('setVariablesFromSharedIntervalData with', changeInterval, this.intervalData.interval);
        this.timeZone = this.intervalData.timeZone;
        this.selectedPeriod = this.intervalService.getPeriodInterfaceByPeriodTypeId(this.intervalData.period);
        if (this.intervalData.intervalEnum === IntervalEnum.Date
            && this.intervalData.period['dateFrom'] && this.intervalData.period['dateTo']) {
            this.intervalType = '0';
            this.dateFrom = this.intervalData.period['dateFrom'];
            this.dateTo = this.intervalData.period['dateTo'];
            if (changeInterval) {
                const isDayInterval =  this.intervalService.isDatesMoreThanXDays({dateFrom: this.dateFrom, dateTo: this.dateTo}, 14);
                this.interval = isDayInterval ? '1d' : '1h';
                console.log('CHANGE INTERVAL', this.interval);
            } else {
                this.interval = this.intervalData.interval;
            }
        } else if (this.intervalData.intervalEnum === IntervalEnum.Period) {
            this.intervalType = '1';
            this.interval = changeInterval ? this.selectedPeriod.default_interval : this.intervalData.interval;
        }
        this.dataSet = true;
    }

    onIntervalTypeChange() {
        console.log('change interval type');
        this.doChange(false);
    }

    onChangePeriod() {
        console.log('change period');
        this.doChange(false);
    }

    changeTimeZone(timeZone: string) {
        console.log('change timezone');
        this.timeZone = timeZone;
        this.doChange(false);
    }

    onChangeDate() {
        console.log('change date');
        if (this.isDatesDifferent()) {
            console.log('date is different');
            this.doChange(false);
        }
    }

    onChangeInterval() {
        this.doChange(true);
    }

    isDatesDifferent(): boolean {
        console.log('date from', this.dateFrom, this.lastDateFrom, 'date to', this.dateTo, this.lastDateTo);
        if ((!this.lastDateFrom || !this.lastDateTo || !this.dateFrom || !this.dateTo)
            || (this.dateFrom.getTime() !== this.lastDateFrom.getTime() || this.dateTo.getTime() !== this.lastDateTo.getTime())) {
            if (this.dateFrom && this.dateTo) {
                this.lastDateFrom = new Date(this.dateFrom.getTime());
                this.lastDateTo = new Date(this.dateTo.getTime());
                console.log('SET date from', this.dateFrom, this.lastDateFrom, 'date to', this.dateTo, this.lastDateTo);
            }
            return true;
        } else {
            return false;
        }
    }

    isDatesValid(): boolean {
        if (this.dateFrom > this.dateTo) {
            this.setMessage = true;
            return false;
        } else {
            this.setMessage = false;
            return !!(this.dateFrom && this.dateTo);
        }
    }

    doChange(changeIntervalOnly: boolean) {
        console.log('DO CHANGE', changeIntervalOnly);
        if (this.intervalType === '0') {
            if (this.isDatesValid()) {
                this.disableIntervalSwitch = this.intervalService.isDatesMoreThanXDays({dateFrom: this.dateFrom, dateTo: this.dateTo}, 14);
                this.setDefaultInterval(changeIntervalOnly);
                const date = {'dateFrom': this.dateFrom, 'dateTo': this.dateTo};
                this.intervalService.setSharedIntervalData({intervalEnum: IntervalEnum.Date, period: date,
                    timeZone: this.timeZone, interval: this.interval});
            }
        } else if (this.intervalType === '1') {
            this.disableIntervalSwitch = this.intervalService.isPeriodMoreThan14Days(this.selectedPeriod.type);
            this.setDefaultInterval(changeIntervalOnly);
            this.intervalService.setSharedIntervalData({intervalEnum: IntervalEnum.Period, period: this.selectedPeriod.type_id,
                timeZone: this.timeZone, interval: this.interval});
        }
    }

    private setDefaultInterval(changeIntervalOnly: boolean) {
        if (!changeIntervalOnly) {
            console.log('SET DEFAULT interval!!!')
            if (this.disableIntervalSwitch || this.hideIntervalSwitch) {
                this.interval = '1d';
            } else {
                this.interval = '1h';
            }
        }
    }

    // format from:  Wed Jun 27 2018 00:00:00 GMT+0200 (CEST)
    formatDate(date: Date): string {
        return date.toLocaleDateString('en-GB');
    }

    checkResize() {
        this.radioButtonClass = this.elementService.isMobileSize() ? 'inline fields' : 'grouped fields';
    }

    // ngOnDestroy(): void {
    //  if (this.subscription) {
    //      this.subscription.unsubscribe();
    //  }
    // }
}

И шаблон:

<div *ngIf="setMessage" class="ui attached warning message">
  <i class="close icon" (click)="setMessage=false"></i>
  <div class="header">Date Error</div>
  <p>Date from cannot be greater than date to</p>
</div>

<div id="chooser" class="ui form attached" *ngIf="selectedPeriod">

  <div id="interval_type_chooser" [ngClass]="radioButtonClass">
    <div class="field">
        <sui-radio-button value="0" [(ngModel)]="intervalType" (ngModelChange)=onIntervalTypeChange()>absolute</sui-radio-button>
    </div>
    <div class="field">
        <sui-radio-button value="1" [(ngModel)]="intervalType" (ngModelChange)=onIntervalTypeChange()>quick</sui-radio-button>
    </div>
  </div>

  <span id='interval_type' [ngSwitch]="intervalType">

    <span *ngSwitchCase="0">
      <button class="ui button"
              suiDatepicker
              [pickerMode]="mode"
              [(ngModel)]="dateFrom"
              [pickerMaxDate]="pickerMaxDate"
              (ngModelChange)=onChangeDate()>From</button>

      <button class="ui button"
              suiDatepicker
              [pickerMode]="mode"
              [(ngModel)]="dateTo"
              [pickerMaxDate]="pickerMaxDate"
              (ngModelChange)=onChangeDate()>To</button>
      <span *ngIf="dateFrom" class="date_desc">From: {{formatDate(dateFrom)}} </span>
      <span *ngIf="dateTo" class="date_desc">To: {{formatDate(dateTo)}} </span>
    </span>

    <span *ngSwitchCase="1" class="field">
        <sui-select class="selection"
                    id="periodSelector"
                    [(ngModel)]="selectedPeriod"
                    (ngModelChange)=onChangePeriod()
                    [options]="periods"
                    labelField="name"
                    placeholder="select period"
                    #selectPeriod>
            <sui-select-option *ngFor="let option of selectPeriod.filteredOptions"
                               [value]="option">
            </sui-select-option>
        </sui-select>
    </span>
  </span>
  <span id="time_zone">
      <app-timezone (changeTimezone)="changeTimeZone($event)"></app-timezone>
  </span>
    <span id="interval" *ngIf="intervals && !hideIntervalSwitch">
      <sui-select   [(ngModel)]="interval"
                    [options]="intervals"
                    (ngModelChange)=onChangeInterval()
                    [isDisabled]="disableIntervalSwitch"
                    #selectInterval>
            <sui-select-option *ngFor="let option of selectInterval.filteredOptions"
                               [value]="option">
            </sui-select-option>
        </sui-select>
  </span>

</div>

Здесьэто шаблон, в котором я использую свой компонент:

<app-interval-chooser *ngIf="canSeeChart" [intervalData]="intervalData" [hideIntervalSwitch]="hideIntervalSwitch"></app-interval-chooser>
<div *ngIf="websites" [class.noCaption]="noCaption" id="chart_segment">
    <div *ngIf="websites.length<=10||noCaption; else TooManySelected">
        <p *ngIf="!noCaption">Showing data for: <app-more-websites [websites]="websites"></app-more-websites></p>
        <div [ngClass]="showDimmer ? 'ui active dimmer' : 'inactive dimmer'">
            <div class="ui medium text loader">Loading</div>
        </div>
        <app-plotly-chart *ngIf="showPlotly && plotlyData" [data]="plotlyData" [chartType]="chartType"></app-plotly-chart>
    </div>
    <ng-template #TooManySelected>
        <app-info-segment [text]="'Only data for 10 or less selected websites can be shown for performance reasons. Try filtering the websites in the select boxes above.'"></app-info-segment>
    </ng-template>
    <div *ngIf="setNoDataMessage">
        <app-info-segment [text]="'No data to show.'"></app-info-segment>
    </div>
</div>

со следующим кодом компонента:

beforePost() {
    this.postData();
}

, который расширяет этот мета-компонент следующим ngOninit:

        this.canSeeChart = this.websites.length <= 10 || this.noCaption;
        this.intervalDataSubscription = this.intervalService.getSharedIntervalDataObservable().subscribe((intervalData => {
            this.intervalData = intervalData;
            this.beforePost();
        }));
        this.chart = this.chartService.getChartTypeInterfaceByEnum(this.chartType);
        this.fragmentSubscription = this.route.fragment.subscribe(fragment => {
            this.showPlotly = this.chart.hash === fragment;
        });
...