| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| import { |
| ChangeDetectorRef, |
| Component, |
| EventEmitter, |
| Input, |
| OnChanges, |
| OnDestroy, |
| OnInit, |
| Output, |
| SimpleChanges |
| } from '@angular/core'; |
| import { AbstractControl, FormBuilder, FormGroup, ValidatorFn, Validators } from '@angular/forms'; |
| import { Observable } from 'rxjs/Observable'; |
| import { Subject } from 'rxjs/Subject'; |
| import { Observer } from 'rxjs/Observer'; |
| import 'rxjs/add/operator/startWith'; |
| |
| import { CanComponentDeactivate } from '@modules/shared/services/can-deactivate-guard.service'; |
| |
| import { ShipperCluster } from '../../models/shipper-cluster.type'; |
| import { ShipperClusterService } from '../../models/shipper-cluster-service.type'; |
| import { ShipperClusterServiceConfigurationInterface } from '../../models/shipper-cluster-service-configuration.interface'; |
| import { ShipperConfigurationModel } from '../../models/shipper-configuration.model'; |
| import * as formValidators from '../../directives/validator.directive'; |
| import { BehaviorSubject } from 'rxjs/BehaviorSubject'; |
| import { Subscription } from 'rxjs/Subscription'; |
| import { ActivatedRoute } from '@angular/router'; |
| import { ListItem } from '@app/classes/list-item'; |
| |
| @Component({ |
| selector: 'shipper-configuration-form', |
| templateUrl: './shipper-service-configuration-form.component.html', |
| styleUrls: ['./shipper-service-configuration-form.component.less'] |
| }) |
| export class ShipperServiceConfigurationFormComponent implements OnInit, OnDestroy, OnChanges, CanComponentDeactivate { |
| private configurationForm: FormGroup; |
| private validatorForm: FormGroup; |
| |
| @Input() |
| clusterName: ShipperCluster; |
| |
| @Input() |
| serviceName: ShipperClusterService; |
| |
| @Input() |
| configuration: ShipperClusterServiceConfigurationInterface; |
| |
| @Input() |
| existingServiceNames: Observable<ShipperClusterService[]> | ShipperClusterService[]; |
| |
| @Input() |
| validationResponse: { [key: string]: any }; |
| |
| @Input() |
| disabled = false; |
| |
| @Output() |
| configurationSubmit: EventEmitter<FormGroup> = new EventEmitter<FormGroup>(); |
| |
| @Output() |
| validationSubmit: EventEmitter<FormGroup> = new EventEmitter<FormGroup>(); |
| |
| private configurationComponents$: Observable<string[]>; |
| private configurationComponentsList$: Observable<ListItem[]>; |
| |
| private isLeavingDirtyForm = false; |
| |
| private get clusterNameField(): AbstractControl { |
| return this.configurationForm.get('clusterName'); |
| } |
| |
| private get serviceNameField(): AbstractControl { |
| return this.configurationForm.get('serviceName'); |
| } |
| |
| private get configurationField(): AbstractControl { |
| return this.configurationForm.get('configuration'); |
| } |
| |
| private get componentNameField(): AbstractControl { |
| return this.validatorForm.get('componentName'); |
| } |
| |
| private get sampleDataField(): AbstractControl { |
| return this.validatorForm.get('sampleData'); |
| } |
| |
| private canDeactivateModalResult: Subject<boolean> = new Subject<boolean>(); |
| |
| private canDeactivateObservable$: Observable<boolean> = Observable.create((observer: Observer<boolean>) => { |
| this.canDeactivateModalResult.takeUntil(this.destroyed$).subscribe((result: boolean) => { |
| observer.next(result); |
| }); |
| }); |
| |
| private serviceNamesListSubject: BehaviorSubject<ShipperClusterService[]> = new BehaviorSubject< |
| ShipperClusterService[] |
| >([]); |
| |
| private subscriptions: Subscription[] = []; |
| |
| private destroyed$ = new Subject(); |
| |
| constructor( |
| private formBuilder: FormBuilder, |
| private activatedRoute: ActivatedRoute, |
| private changeDetectionRef: ChangeDetectorRef |
| ) { |
| // This is a fix to avoid the ExpressionChangedAfterItHasBeenCheckedError exception |
| // We create forms checking if there is serviceName set, so that is why we put this in the constructor. |
| this.createForms(); |
| } |
| |
| ngOnInit() { |
| this.subscriptions.push( |
| this.activatedRoute.params |
| .map(params => params.service) |
| .subscribe(service => { |
| this.serviceName = service; |
| }) |
| ); |
| if (!this.serviceName) { |
| this.configurationForm.controls.serviceName.setValidators([ |
| Validators.required, |
| formValidators.uniqueServiceNameValidator(this.serviceNamesListSubject) |
| ]); |
| } |
| this.configurationComponents$ = this.configurationForm.controls.configuration.valueChanges |
| .map( |
| (newValue: string): string[] => { |
| let components: string[]; |
| try { |
| const inputs: { [key: string]: any }[] = (newValue ? JSON.parse(newValue) : {}).input; |
| components = inputs && inputs.length ? inputs.map(input => input.type) : []; |
| } catch (error) { |
| components = []; |
| } |
| return components || []; |
| } |
| ) |
| .startWith([]); |
| this.configurationComponentsList$ = this.configurationComponents$ |
| .map( |
| (components: string[]): ListItem[] => |
| components |
| .filter((component: string) => component) |
| .map( |
| (component: string, index: number): ListItem => { |
| return { |
| value: component, |
| label: component, |
| isChecked: index === 0 |
| }; |
| } |
| ) |
| ) |
| .startWith([]); |
| if (this.existingServiceNames instanceof Observable) { |
| this.existingServiceNames.takeUntil(this.destroyed$).subscribe((serviceNames: ShipperClusterService[]) => { |
| this.serviceNamesListSubject.next(serviceNames); |
| }); |
| } else { |
| this.serviceNamesListSubject.next(this.existingServiceNames); |
| } |
| } |
| |
| ngOnChanges(changes: SimpleChanges): void { |
| if (this.configurationForm) { |
| Object.keys(changes).forEach((controlName: string) => { |
| if (this.configurationForm.controls[controlName]) { |
| let value: any = changes[controlName].currentValue; |
| if (controlName === 'configuration') { |
| value = value || new ShipperConfigurationModel(); |
| if (!(value instanceof String)) { |
| value = this.getConfigurationAsString(value); |
| } |
| } |
| if (this.configurationForm.controls[controlName].value !== value) { |
| this.configurationForm.controls[controlName].setValue(value); |
| this.configurationForm.markAsPristine(); |
| } |
| } |
| }); |
| } |
| if ( |
| this.validatorForm && |
| changes.clusterName && |
| this.validatorForm.controls.clusterName.value !== changes.clusterName.currentValue |
| ) { |
| this.validatorForm.controls.clusterName.setValue(changes.clusterName.currentValue); |
| this.validatorForm.markAsPristine(); |
| } |
| } |
| |
| ngOnDestroy() { |
| if (this.subscriptions) { |
| this.subscriptions.forEach(subscription => subscription.unsubscribe()); |
| } |
| this.destroyed$.next(true); |
| } |
| |
| leaveDirtyFormConfirmed = () => { |
| this.canDeactivateModalResult.next(true); |
| this.isLeavingDirtyForm = false; |
| }; |
| |
| leaveDirtyFormCancelled = () => { |
| this.canDeactivateModalResult.next(false); |
| this.isLeavingDirtyForm = false; |
| }; |
| |
| canDeactivate(): Observable<boolean> { |
| if (this.configurationForm.pristine) { |
| return Observable.of(true); |
| } |
| this.isLeavingDirtyForm = true; |
| return this.canDeactivateObservable$; |
| } |
| |
| getConfigurationAsString(configuration: ShipperClusterServiceConfigurationInterface): string { |
| return configuration ? JSON.stringify(configuration, null, 4) : ''; |
| } |
| |
| createForms(): void { |
| const configuration: ShipperClusterServiceConfigurationInterface = |
| this.configuration || (this.serviceName ? this.configuration : new ShipperConfigurationModel()); |
| this.configurationForm = this.formBuilder.group({ |
| clusterName: this.formBuilder.control(this.clusterName, Validators.required), |
| serviceName: this.formBuilder.control(this.serviceName, [Validators.required]), |
| configuration: this.formBuilder.control(this.getConfigurationAsString(configuration), [ |
| Validators.required, |
| formValidators.configurationValidator() |
| ]) |
| }); |
| |
| this.validatorForm = this.formBuilder.group({ |
| clusterName: this.formBuilder.control(this.clusterName, [Validators.required]), |
| componentName: this.formBuilder.control('', [ |
| Validators.required, |
| formValidators.getConfigurationServiceValidator( |
| this.configurationForm.controls.configuration, |
| listItem => listItem && listItem.length && listItem[0].value |
| ) |
| ]), |
| sampleData: this.formBuilder.control('', Validators.required), |
| configuration: this.formBuilder.control('', Validators.required) |
| }); |
| this.configurationForm.valueChanges.takeUntil(this.destroyed$).subscribe(() => { |
| this.validatorForm.controls.componentName.updateValueAndValidity(); |
| this.validatorForm.controls.configuration.setValue(this.configurationForm.controls.configuration.value); |
| }); |
| } |
| |
| onConfigurationSubmit(): void { |
| if (this.configurationForm.valid) { |
| this.configurationSubmit.emit(this.configurationForm); |
| } |
| } |
| |
| onValidationSubmit(): void { |
| if (this.validatorForm.valid) { |
| this.validationSubmit.emit(this.validatorForm); |
| } |
| } |
| } |