tobias-istvan closed pull request #67: [AMBARI-25028] [Log Search UI] Populate 
`Component Name` in validator
URL: https://github.com/apache/ambari-logsearch/pull/67
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git 
a/ambari-logsearch-web/src/app/modules/shared/components/dropdown-button/dropdown-button.component.html
 
b/ambari-logsearch-web/src/app/modules/shared/components/dropdown-button/dropdown-button.component.html
index 52f97be377..b24d15aabb 100644
--- 
a/ambari-logsearch-web/src/app/modules/shared/components/dropdown-button/dropdown-button.component.html
+++ 
b/ambari-logsearch-web/src/app/modules/shared/components/dropdown-button/dropdown-button.component.html
@@ -15,16 +15,18 @@
   limitations under the License.
 -->
 
-<div [ngClass]="{'dropup': isDropup, 'has-selection': hasSelection}">
-  <button [ngClass]="['btn', 'dropdown-toggle', buttonClass]" 
data-toggle="dropdown">
+<div [ngClass]="{ dropup: isDropup, 'has-selection': hasSelection }">
+  <button [ngClass]="['btn', 'dropdown-toggle', buttonClass]" 
[class.disabled]="disabled" data-toggle="dropdown">
     <span class="filter-label">
       <span *ngIf="iconClass || label" [class.plain]="!isMultipleChoice && 
!hideCaret && showSelectedValue">
         <span *ngIf="iconClass" [ngClass]="iconClass"></span>
-        <span *ngIf="label && (!hasSelection || isMultipleChoice || 
showCommonLabelWithSelection)"
-              [class.label-before-selection]="isSelectionDisplayable">
-          {{label}}
+        <span
+          *ngIf="label && (!hasSelection || isMultipleChoice || 
showCommonLabelWithSelection)"
+          [class.label-before-selection]="isSelectionDisplayable"
+        >
+          {{ label }}
         </span>
-        <span *ngIf="showTotalSelection && totalSelection" 
class="total-selection badge">{{totalSelection}}</span>
+        <span *ngIf="showTotalSelection && totalSelection" 
class="total-selection badge">{{ totalSelection }}</span>
       </span>
       <span *ngIf="isSelectionDisplayable">
         <span class="selected-item-label" *ngFor="let item of 
selectedItems">{{ item.label | translate }}</span>
@@ -32,8 +34,15 @@
       <span *ngIf="!hideCaret" class="caret"></span>
     </span>
   </button>
-  <ul data-component="dropdown-list" 
(selectedItemChange)="updateSelection($event)"
-    [ngClass]="{'dropdown-menu': true, 'dropdown-menu-right': isRightAlign}" 
[closeOnSelection]="closeOnSelection"
-    [items]="options" [actionArguments]="listItemArguments" 
[isMultipleChoice]="isMultipleChoice"
-    [useClearToDefaultSelection]="useClearToDefaultSelection" 
[useLocalFilter]="useDropDownLocalFilter"></ul>
+  <ul
+    data-component="dropdown-list"
+    (selectedItemChange)="updateSelection($event)"
+    [ngClass]="{ 'dropdown-menu': true, 'dropdown-menu-right': isRightAlign }"
+    [closeOnSelection]="closeOnSelection"
+    [items]="options"
+    [actionArguments]="listItemArguments"
+    [isMultipleChoice]="isMultipleChoice"
+    [useClearToDefaultSelection]="useClearToDefaultSelection"
+    [useLocalFilter]="useDropDownLocalFilter"
+  ></ul>
 </div>
diff --git 
a/ambari-logsearch-web/src/app/modules/shared/components/dropdown-button/dropdown-button.component.ts
 
b/ambari-logsearch-web/src/app/modules/shared/components/dropdown-button/dropdown-button.component.ts
index 77ea8e12b0..fb8a9819bc 100644
--- 
a/ambari-logsearch-web/src/app/modules/shared/components/dropdown-button/dropdown-button.component.ts
+++ 
b/ambari-logsearch-web/src/app/modules/shared/components/dropdown-button/dropdown-button.component.ts
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 
-import { Component, Input, Output, EventEmitter } from '@angular/core';
+import { Component, Input, Output, EventEmitter, OnChanges, SimpleChanges } 
from '@angular/core';
 import { ListItem } from '@app/classes/list-item';
 import { UtilsService } from '@app/services/utils.service';
 
@@ -25,8 +25,7 @@ import { UtilsService } from '@app/services/utils.service';
   templateUrl: './dropdown-button.component.html',
   styleUrls: ['./dropdown-button.component.less']
 })
-export class DropdownButtonComponent {
-
+export class DropdownButtonComponent implements OnChanges {
   @Input()
   label?: string;
 
@@ -76,6 +75,9 @@ export class DropdownButtonComponent {
   @Input()
   closeOnSelection = true;
 
+  @Input()
+  disabled = false;
+
   protected selectedItems: ListItem[] = [];
 
   get selection(): ListItem[] {
@@ -83,15 +85,19 @@ export class DropdownButtonComponent {
   }
 
   set selection(items: ListItem[]) {
-    this.selectedItems = <ListItem[]>(Array.isArray(items) ? items : (items || 
[]));
+    this.selectedItems = <ListItem[]>(Array.isArray(items) ? items : items || 
[]);
     if (this.selectedItems.length > 1 && !this.isMultipleChoice) {
       this.selectedItems = this.selectedItems.slice(0, 1);
     }
     if (this.isMultipleChoice && this.options) {
-      this.options.forEach((option: ListItem): void => {
-        const selectionItem = this.selectedItems.find((item: ListItem): 
boolean => this.utils.isEqual(item.value, option.value));
-        option.isChecked = !!selectionItem;
-      });
+      this.options.forEach(
+        (option: ListItem): void => {
+          const selectionItem = this.selectedItems.find(
+            (item: ListItem): boolean => this.utils.isEqual(item.value, 
option.value)
+          );
+          option.isChecked = !!selectionItem;
+        }
+      );
     }
   }
 
@@ -113,9 +119,22 @@ export class DropdownButtonComponent {
     return this.showSelectedValue && !this.isMultipleChoice && 
this.hasSelection;
   }
 
-  constructor(
-    protected utils: UtilsService
-  ) {}
+  get value(): any {
+    const values = this.selectedItems && this.selectedItems.length && 
this.selectedItems.map(item => item.value);
+    return this.isMultipleChoice ? values : values[0];
+  }
+
+  constructor(protected utils: UtilsService) {}
+
+  ngOnChanges(changes: SimpleChanges) {
+    if (changes.options) {
+      this.hanldeOptionsChange();
+    }
+  }
+
+  hanldeOptionsChange() {
+    this.filterAndSetSelection();
+  }
 
   clearSelection(silent: boolean = false) {
     let hasChange = false;
@@ -128,14 +147,16 @@ export class DropdownButtonComponent {
     }
   }
 
-  updateSelection(updates: ListItem | ListItem[], callOnChange: boolean = 
true): boolean {
+  updateSelection(updates: ListItem | ListItem[]): boolean {
     let hasChange = false;
     if (updates && (!Array.isArray(updates) || updates.length)) {
       const items: ListItem[] = Array.isArray(updates) ? updates : [updates];
       if (this.isMultipleChoice) {
         items.forEach((item: ListItem) => {
           if (this.options && this.options.length) {
-            const itemToUpdate: ListItem = this.options.find((option: 
ListItem) => this.utils.isEqual(option.value, item.value));
+            const itemToUpdate: ListItem = this.options.find((option: 
ListItem) =>
+              this.utils.isEqual(option.value, item.value)
+            );
             if (itemToUpdate) {
               hasChange = hasChange || itemToUpdate.isChecked !== 
item.isChecked;
               itemToUpdate.isChecked = item.isChecked;
@@ -151,15 +172,18 @@ export class DropdownButtonComponent {
         });
       }
     } else {
-      this.options.forEach((item: ListItem) => item.isChecked = false);
+      this.options.forEach((item: ListItem) => (item.isChecked = false));
     }
-    const checkedItems = this.options.filter((option: ListItem): boolean => 
option.isChecked);
-    this.selection = checkedItems;
+    this.filterAndSetSelection();
     if (hasChange) {
-      const selectedValues = checkedItems.map((option: ListItem): any => 
option.value);
+      const selectedValues = this.selection.map((option: ListItem): any => 
option.value);
       this.selectItem.emit(this.isMultipleChoice ? selectedValues : 
selectedValues.shift());
     }
     return hasChange;
   }
 
+  protected filterAndSetSelection() {
+    const checkedItems = this.options.filter((option: ListItem): boolean => 
option.isChecked);
+    this.selection = checkedItems;
+  }
 }
diff --git 
a/ambari-logsearch-web/src/app/modules/shared/components/filter-dropdown/filter-dropdown.component.spec.ts
 
b/ambari-logsearch-web/src/app/modules/shared/components/filter-dropdown/filter-dropdown.component.spec.ts
index 4157333da7..a8aff3f811 100644
--- 
a/ambari-logsearch-web/src/app/modules/shared/components/filter-dropdown/filter-dropdown.component.spec.ts
+++ 
b/ambari-logsearch-web/src/app/modules/shared/components/filter-dropdown/filter-dropdown.component.spec.ts
@@ -15,37 +15,41 @@
  * limitations under the License.
  */
 
-import {NO_ERRORS_SCHEMA} from '@angular/core';
-import {async, ComponentFixture, TestBed} from '@angular/core/testing';
-import {MockHttpRequestModules, TranslationModules} from 
'@app/test-config.spec';
-import {StoreModule} from '@ngrx/store';
-import {AppSettingsService, appSettings} from 
'@app/services/storage/app-settings.service';
-import {AppStateService, appState} from 
'@app/services/storage/app-state.service';
-import {AuditLogsService, auditLogs} from 
'@app/services/storage/audit-logs.service';
-import {AuditLogsFieldsService, auditLogsFields} from 
'@app/services/storage/audit-logs-fields.service';
-import {AuditLogsGraphDataService, auditLogsGraphData} from 
'@app/services/storage/audit-logs-graph-data.service';
-import {ServiceLogsService, serviceLogs} from 
'@app/services/storage/service-logs.service';
-import {ServiceLogsFieldsService, serviceLogsFields} from 
'@app/services/storage/service-logs-fields.service';
+import { NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { MockHttpRequestModules, TranslationModules } from 
'@app/test-config.spec';
+import { StoreModule } from '@ngrx/store';
+import { AppSettingsService, appSettings } from 
'@app/services/storage/app-settings.service';
+import { AppStateService, appState } from 
'@app/services/storage/app-state.service';
+import { AuditLogsService, auditLogs } from 
'@app/services/storage/audit-logs.service';
+import { AuditLogsFieldsService, auditLogsFields } from 
'@app/services/storage/audit-logs-fields.service';
+import { AuditLogsGraphDataService, auditLogsGraphData } from 
'@app/services/storage/audit-logs-graph-data.service';
+import { ServiceLogsService, serviceLogs } from 
'@app/services/storage/service-logs.service';
+import { ServiceLogsFieldsService, serviceLogsFields } from 
'@app/services/storage/service-logs-fields.service';
 import {
-  ServiceLogsHistogramDataService, serviceLogsHistogramData
+  ServiceLogsHistogramDataService,
+  serviceLogsHistogramData
 } from '@app/services/storage/service-logs-histogram-data.service';
-import {ServiceLogsTruncatedService, serviceLogsTruncated} from 
'@app/services/storage/service-logs-truncated.service';
-import {TabsService, tabs} from '@app/services/storage/tabs.service';
-import {ClustersService, clusters} from 
'@app/services/storage/clusters.service';
-import {ComponentsService, components} from 
'@app/services/storage/components.service';
-import {HostsService, hosts} from '@app/services/storage/hosts.service';
-import {UtilsService} from '@app/services/utils.service';
-import {LogsContainerService} from '@app/services/logs-container.service';
-import {AuthService} from '@app/services/auth.service';
+import {
+  ServiceLogsTruncatedService,
+  serviceLogsTruncated
+} from '@app/services/storage/service-logs-truncated.service';
+import { TabsService, tabs } from '@app/services/storage/tabs.service';
+import { ClustersService, clusters } from 
'@app/services/storage/clusters.service';
+import { ComponentsService, components } from 
'@app/services/storage/components.service';
+import { HostsService, hosts } from '@app/services/storage/hosts.service';
+import { UtilsService } from '@app/services/utils.service';
+import { LogsContainerService } from '@app/services/logs-container.service';
+import { AuthService } from '@app/services/auth.service';
 
-import {FilterDropdownComponent} from './filter-dropdown.component';
-import {ClusterSelectionService} from 
'@app/services/storage/cluster-selection.service';
-import {LogsStateService} from '@app/services/storage/logs-state.service';
-import {RoutingUtilsService} from '@app/services/routing-utils.service';
-import {LogsFilteringUtilsService} from 
'@app/services/logs-filtering-utils.service';
-import {RouterTestingModule} from '@angular/router/testing';
-import {NotificationService} from 
'@modules/shared/services/notification.service';
-import {NotificationsService} from 
'angular2-notifications/src/notifications.service';
+import { FilterDropdownComponent } from './filter-dropdown.component';
+import { ClusterSelectionService } from 
'@app/services/storage/cluster-selection.service';
+import { LogsStateService } from '@app/services/storage/logs-state.service';
+import { RoutingUtilsService } from '@app/services/routing-utils.service';
+import { LogsFilteringUtilsService } from 
'@app/services/logs-filtering-utils.service';
+import { RouterTestingModule } from '@angular/router/testing';
+import { NotificationService } from 
'@modules/shared/services/notification.service';
+import { NotificationsService } from 
'angular2-notifications/src/notifications.service';
 
 import * as auth from '@app/store/reducers/auth.reducers';
 import { EffectsModule } from '@ngrx/effects';
@@ -77,8 +81,7 @@ describe('FilterDropdownComponent', () => {
     const httpClient = {
       get: () => {
         return {
-          subscribe: () => {
-          }
+          subscribe: () => {}
         };
       }
     };
@@ -136,9 +139,8 @@ describe('FilterDropdownComponent', () => {
         NotificationsService,
         NotificationService
       ],
-      schemas: [NO_ERRORS_SCHEMA]
-    })
-    .compileComponents();
+      schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA]
+    }).compileComponents();
   }));
 
   beforeEach(() => {
@@ -150,5 +152,4 @@ describe('FilterDropdownComponent', () => {
   it('should create component', () => {
     expect(component).toBeTruthy();
   });
-
 });
diff --git 
a/ambari-logsearch-web/src/app/modules/shared/components/filter-dropdown/filter-dropdown.component.ts
 
b/ambari-logsearch-web/src/app/modules/shared/components/filter-dropdown/filter-dropdown.component.ts
index b0c766e933..2bcf4a206d 100644
--- 
a/ambari-logsearch-web/src/app/modules/shared/components/filter-dropdown/filter-dropdown.component.ts
+++ 
b/ambari-logsearch-web/src/app/modules/shared/components/filter-dropdown/filter-dropdown.component.ts
@@ -15,10 +15,10 @@
  * limitations under the License.
  */
 
-import {Component, forwardRef} from '@angular/core';
-import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
-import {DropdownButtonComponent} from 
'@modules/shared/components/dropdown-button/dropdown-button.component';
-import {ListItem} from '@app/classes/list-item';
+import { Component, forwardRef, Input } from '@angular/core';
+import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
+import { DropdownButtonComponent } from 
'@modules/shared/components/dropdown-button/dropdown-button.component';
+import { ListItem } from '@app/classes/list-item';
 
 @Component({
   selector: 'filter-dropdown',
@@ -33,7 +33,6 @@ import {ListItem} from '@app/classes/list-item';
   ]
 })
 export class FilterDropdownComponent extends DropdownButtonComponent 
implements ControlValueAccessor {
-
   private onChange;
 
   private _onChange(value) {
@@ -42,6 +41,9 @@ export class FilterDropdownComponent extends 
DropdownButtonComponent implements
     }
   }
 
+  @Input()
+  options: ListItem[] = [];
+
   updateSelection(updates: ListItem | ListItem[], callOnChange: boolean = 
true): boolean {
     const hasChange = super.updateSelection(updates);
     if (hasChange && callOnChange) {
@@ -50,15 +52,18 @@ export class FilterDropdownComponent extends 
DropdownButtonComponent implements
     return hasChange;
   }
 
+  hanldeOptionsChange() {
+    super.hanldeOptionsChange();
+    this._onChange(this.selection);
+  }
+
   writeValue(items: ListItem[]) {
-    this.selection = items ? (Array.isArray(items) ? items : [items] ) : [];
+    this.selection = items ? (Array.isArray(items) ? items : [items]) : [];
   }
 
   registerOnChange(callback: any): void {
     this.onChange = callback;
   }
 
-  registerOnTouched() {
-  }
-
+  registerOnTouched() {}
 }
diff --git 
a/ambari-logsearch-web/src/app/modules/shipper/components/shipper-configuration/shipper-configuration.component.spec.ts
 
b/ambari-logsearch-web/src/app/modules/shipper/components/shipper-configuration/shipper-configuration.component.spec.ts
index 0ec4f56690..a8e2b1f50a 100644
--- 
a/ambari-logsearch-web/src/app/modules/shipper/components/shipper-configuration/shipper-configuration.component.spec.ts
+++ 
b/ambari-logsearch-web/src/app/modules/shipper/components/shipper-configuration/shipper-configuration.component.spec.ts
@@ -16,39 +16,45 @@
  * limitations under the License.
  */
 import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import {ShipperConfigurationComponent} from 
'./shipper-configuration.component';
-import {StoreModule} from '@ngrx/store';
-import {auditLogs, AuditLogsService} from 
'@app/services/storage/audit-logs.service';
-import {serviceLogsTruncated, ServiceLogsTruncatedService} from 
'@app/services/storage/service-logs-truncated.service';
-import {components, ComponentsService} from 
'@app/services/storage/components.service';
-import {UtilsService} from '@app/services/utils.service';
-import {tabs, TabsService} from '@app/services/storage/tabs.service';
-import {serviceLogs, ServiceLogsService} from 
'@app/services/storage/service-logs.service';
-import {hosts, HostsService} from '@app/services/storage/hosts.service';
-import {MockHttpRequestModules, TranslationModules} from 
'@app/test-config.spec';
-import {ComponentGeneratorService} from 
'@app/services/component-generator.service';
-import {auditLogsGraphData, AuditLogsGraphDataService} from 
'@app/services/storage/audit-logs-graph-data.service';
-import {serviceLogsHistogramData, ServiceLogsHistogramDataService} from 
'@app/services/storage/service-logs-histogram-data.service';
-import {clusters, ClustersService} from 
'@app/services/storage/clusters.service';
-import {AuditLogsFieldsService, auditLogsFields} from 
'@app/services/storage/audit-logs-fields.service';
-import {appSettings, AppSettingsService} from 
'@app/services/storage/app-settings.service';
-import {appState, AppStateService} from 
'@app/services/storage/app-state.service';
-import {ClusterSelectionService} from 
'@app/services/storage/cluster-selection.service';
-import {serviceLogsFields, ServiceLogsFieldsService} from 
'@app/services/storage/service-logs-fields.service';
-import {LogsContainerService} from '@app/services/logs-container.service';
-import {ShipperRoutingModule} from '@modules/shipper/shipper-routing.module';
-import {ShipperClusterServiceListComponent} from 
'@modules/shipper/components/shipper-cluster-service-list/shipper-cluster-service-list.component';
-import {ShipperServiceConfigurationFormComponent} from 
'@modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component';
-import {FormsModule, ReactiveFormsModule} from '@angular/forms';
-import {TypeaheadModule} from 'ngx-bootstrap';
-import {DisableControlDirective} from 
'@modules/shared/directives/disable-control.directive';
-import {ModalComponent} from 
'@modules/shared/components/modal/modal.component';
-import {RouterTestingModule} from '@angular/router/testing';
-import {ShipperClusterServiceListService} from 
'@modules/shipper/services/shipper-cluster-service-list.service';
-import {ShipperConfigurationService} from 
'@modules/shipper/services/shipper-configuration.service';
-import {NotificationService} from 
'@modules/shared/services/notification.service';
-import {NotificationsService} from 
'angular2-notifications/src/notifications.service';
+import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
+import { ShipperConfigurationComponent } from 
'./shipper-configuration.component';
+import { StoreModule } from '@ngrx/store';
+import { auditLogs, AuditLogsService } from 
'@app/services/storage/audit-logs.service';
+import {
+  serviceLogsTruncated,
+  ServiceLogsTruncatedService
+} from '@app/services/storage/service-logs-truncated.service';
+import { components, ComponentsService } from 
'@app/services/storage/components.service';
+import { UtilsService } from '@app/services/utils.service';
+import { tabs, TabsService } from '@app/services/storage/tabs.service';
+import { serviceLogs, ServiceLogsService } from 
'@app/services/storage/service-logs.service';
+import { hosts, HostsService } from '@app/services/storage/hosts.service';
+import { MockHttpRequestModules, TranslationModules } from 
'@app/test-config.spec';
+import { ComponentGeneratorService } from 
'@app/services/component-generator.service';
+import { auditLogsGraphData, AuditLogsGraphDataService } from 
'@app/services/storage/audit-logs-graph-data.service';
+import {
+  serviceLogsHistogramData,
+  ServiceLogsHistogramDataService
+} from '@app/services/storage/service-logs-histogram-data.service';
+import { clusters, ClustersService } from 
'@app/services/storage/clusters.service';
+import { AuditLogsFieldsService, auditLogsFields } from 
'@app/services/storage/audit-logs-fields.service';
+import { appSettings, AppSettingsService } from 
'@app/services/storage/app-settings.service';
+import { appState, AppStateService } from 
'@app/services/storage/app-state.service';
+import { ClusterSelectionService } from 
'@app/services/storage/cluster-selection.service';
+import { serviceLogsFields, ServiceLogsFieldsService } from 
'@app/services/storage/service-logs-fields.service';
+import { LogsContainerService } from '@app/services/logs-container.service';
+import { ShipperRoutingModule } from '@modules/shipper/shipper-routing.module';
+import { ShipperClusterServiceListComponent } from 
'@modules/shipper/components/shipper-cluster-service-list/shipper-cluster-service-list.component';
+import { ShipperServiceConfigurationFormComponent } from 
'@modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { TypeaheadModule } from 'ngx-bootstrap';
+import { DisableControlDirective } from 
'@modules/shared/directives/disable-control.directive';
+import { ModalComponent } from 
'@modules/shared/components/modal/modal.component';
+import { RouterTestingModule } from '@angular/router/testing';
+import { ShipperClusterServiceListService } from 
'@modules/shipper/services/shipper-cluster-service-list.service';
+import { ShipperConfigurationService } from 
'@modules/shipper/services/shipper-configuration.service';
+import { NotificationService } from 
'@modules/shared/services/notification.service';
+import { NotificationsService } from 
'angular2-notifications/src/notifications.service';
 
 describe('ShipperConfigurationComponent', () => {
   let component: ShipperConfigurationComponent;
@@ -110,9 +116,9 @@ describe('ShipperConfigurationComponent', () => {
         ShipperServiceConfigurationFormComponent,
         DisableControlDirective,
         ModalComponent
-      ]
-    })
-    .compileComponents();
+      ],
+      schemas: [CUSTOM_ELEMENTS_SCHEMA]
+    }).compileComponents();
   }));
 
   beforeEach(() => {
diff --git 
a/ambari-logsearch-web/src/app/modules/shipper/components/shipper-configuration/shipper-configuration.component.ts
 
b/ambari-logsearch-web/src/app/modules/shipper/components/shipper-configuration/shipper-configuration.component.ts
index d8fccbdd61..82ae869ee4 100644
--- 
a/ambari-logsearch-web/src/app/modules/shipper/components/shipper-configuration/shipper-configuration.component.ts
+++ 
b/ambari-logsearch-web/src/app/modules/shipper/components/shipper-configuration/shipper-configuration.component.ts
@@ -15,25 +15,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {Component, Input, OnDestroy, OnInit, ViewChild} from '@angular/core';
-import {ActivatedRoute, Router} from '@angular/router';
-import {Response} from '@angular/http';
-import {Observable} from 'rxjs/Observable';
+import { Component, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { Response } from '@angular/http';
+import { Observable } from 'rxjs/Observable';
 import 'rxjs/add/operator/skipWhile';
 
-import {NotificationService, NotificationType} from 
'@modules/shared/services/notification.service';
-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 {ShipperConfigurationService} from 
'../../services/shipper-configuration.service';
-import {ShipperClusterServiceListService} from 
'../../services/shipper-cluster-service-list.service';
-import {
-  ShipperServiceConfigurationFormComponent
-} from 
'@modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component';
-import {TranslateService} from '@ngx-translate/core';
-import {ClusterSelectionService} from 
'@app/services/storage/cluster-selection.service';
-import {Subscription} from 'rxjs/Subscription';
+import { NotificationService, NotificationType } from 
'@modules/shared/services/notification.service';
+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 { ShipperConfigurationService } from 
'../../services/shipper-configuration.service';
+import { ShipperClusterServiceListService } from 
'../../services/shipper-cluster-service-list.service';
+import { ShipperServiceConfigurationFormComponent } from 
'@modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component';
+import { TranslateService } from '@ngx-translate/core';
+import { ClusterSelectionService } from 
'@app/services/storage/cluster-selection.service';
+import { Subscription } from 'rxjs/Subscription';
 import { BehaviorSubject } from 'rxjs/BehaviorSubject';
 import { FormGroup } from '@angular/forms';
 
@@ -43,7 +41,6 @@ import { FormGroup } from '@angular/forms';
   styleUrls: ['./shipper-configuration.component.less']
 })
 export class ShipperConfigurationComponent implements CanComponentDeactivate, 
OnInit, OnDestroy {
-
   static clusterSelectionStoreKey = 'shipper';
 
   @Input()
@@ -57,21 +54,25 @@ export class ShipperConfigurationComponent implements 
CanComponentDeactivate, On
   private clusterName$: Observable<ShipperClusterService> = 
this.activatedRoute.params.map(params => params.cluster);
   private serviceName$: Observable<ShipperClusterService> = 
this.activatedRoute.params.map(params => params.service);
 
-  private serviceNamesList$: Observable<ShipperClusterService[]> = 
this.clusterName$.switchMap((cluster: ShipperCluster) => {
-    return cluster ? 
this.shipperClusterServiceListService.getServicesForCluster(cluster) : 
Observable.of(undefined);
-  });
-
-  private configuration$: Observable<{[key: string]: any}> = 
Observable.combineLatest(
-      this.clusterName$,
-      this.serviceName$
-    ).switchMap(([clusterName, serviceName]: [ShipperCluster, 
ShipperClusterService]) => {
-      return clusterName && serviceName ?
-        this.shipperConfigurationService.loadConfiguration(clusterName, 
serviceName) : Observable.of(undefined);
-    });
+  private serviceNamesList$: Observable<ShipperClusterService[]> = 
this.clusterName$.switchMap(
+    (cluster: ShipperCluster) => {
+      return cluster ? 
this.shipperClusterServiceListService.getServicesForCluster(cluster) : 
Observable.of(undefined);
+    }
+  );
+
+  private configuration$: Observable<{
+    [key: string]: any;
+  }> = Observable.combineLatest(this.clusterName$, 
this.serviceName$).switchMap(
+    ([clusterName, serviceName]: [ShipperCluster, ShipperClusterService]) => {
+      return clusterName && serviceName
+        ? this.shipperConfigurationService.loadConfiguration(clusterName, 
serviceName)
+        : Observable.of(undefined);
+    }
+  );
 
   private subscriptions: Subscription[] = [];
 
-  validationResponse: {[key: string]: any};
+  validationResponse: { [key: string]: any };
 
   constructor(
     private router: Router,
@@ -81,7 +82,7 @@ export class ShipperConfigurationComponent implements 
CanComponentDeactivate, On
     private notificationService: NotificationService,
     private translate: TranslateService,
     private clusterSelectionStoreService: ClusterSelectionService
-  ) { }
+  ) {}
 
   ngOnInit() {
     this.subscriptions.push(
@@ -98,7 +99,8 @@ export class ShipperConfigurationComponent implements 
CanComponentDeactivate, On
   }
 
   private getPathMapForClusterFirstService(cluster: ShipperCluster): 
Observable<string[]> {
-    return this.shipperClusterServiceListService.getServicesForCluster(cluster)
+    return this.shipperClusterServiceListService
+      .getServicesForCluster(cluster)
       .switchMap((serviceNamesList: ShipperClusterService[]) => {
         return Observable.of(this.getRouterLink([cluster, 
serviceNamesList[0]]));
       });
@@ -112,26 +114,42 @@ export class ShipperConfigurationComponent implements 
CanComponentDeactivate, On
     if (clusterName) {
       this.clusterName$.first().subscribe((currentClusterName: ShipperCluster) 
=> {
         if (currentClusterName !== clusterName) {
-          
this.getPathMapForClusterFirstService(clusterName).first().subscribe((path: 
string[]) => this.router.navigate(path));
+          this.getPathMapForClusterFirstService(clusterName)
+            .first()
+            .subscribe((path: string[]) => this.router.navigate(path));
         }
       });
     }
-  }
+  };
 
   private getRouterLink(path: string | string[]): string[] {
     return [...this.routerPath, ...(Array.isArray(path) ? path : [path])];
   }
 
-  getResponseHandler(cmd: string, type: string, form?: FormGroup) {
-    const msgVariables = form.getRawValue();
+  getResponseHandler(cmd: string, type: string, msgVariables: any = {}, form?: 
FormGroup) {
     return (response: Response) => {
       const result = response.json();
       // @ToDo change the backend response status to some error code if the 
configuration is not valid and don't use the .message prop
-      const resultType = response ? (response.ok && !result.errorMessage ? 
NotificationType.SUCCESS : NotificationType.ERROR) : type;
-      const translateParams = {errorMessage: (result && result.message) || '', 
...msgVariables, ...result};
+      const resultType = response
+        ? response.ok && !result.errorMessage
+          ? NotificationType.SUCCESS
+          : NotificationType.ERROR
+        : type;
+      const translateParams = {
+        errorMessage: (result && result.message) || '',
+        ...msgVariables,
+        ...result
+      };
       const title = 
this.translate.instant(`shipperConfiguration.action.${cmd}.title`, 
translateParams);
-      const message = 
this.translate.instant(`shipperConfiguration.action.${cmd}.${resultType}.message`,
 translateParams);
-      this.notificationService.addNotification({type: resultType, title, 
message});
+      const message = this.translate.instant(
+        `shipperConfiguration.action.${cmd}.${resultType}.message`,
+        translateParams
+      );
+      this.notificationService.addNotification({
+        type: resultType,
+        title,
+        message
+      });
       if (resultType !== NotificationType.ERROR) {
         form.markAsPristine();
       }
@@ -149,23 +167,24 @@ export class ShipperConfigurationComponent implements 
CanComponentDeactivate, On
         service: rawValue.serviceName,
         configuration: JSON.parse(rawValue.configuration)
       }).subscribe(
-        this.getResponseHandler(cmd, NotificationType.SUCCESS, 
configurationForm),
-        this.getResponseHandler(cmd, NotificationType.ERROR, configurationForm)
+        this.getResponseHandler(cmd, NotificationType.SUCCESS, rawValue, 
configurationForm),
+        this.getResponseHandler(cmd, NotificationType.ERROR, rawValue, 
configurationForm)
       );
     });
   }
 
-  private setValidationResult = (result: {[key: string]: any}) => {
+  private setValidationResult = (result: { [key: string]: any }) => {
     this.validationResponse = result;
-  }
+  };
 
   onValidationFormSubmit(validationForm: FormGroup): void {
     this.validationResponse = null;
     const rawValue = validationForm.getRawValue();
+    rawValue.componentName = rawValue.componentName[0].value;
     const request$: Observable<Response> = 
this.shipperConfigurationService.testConfiguration(rawValue);
     request$.subscribe(
-      this.getResponseHandler('validate', NotificationType.SUCCESS, 
validationForm),
-      this.getResponseHandler('validate', NotificationType.ERROR, 
validationForm)
+      this.getResponseHandler('validate', NotificationType.SUCCESS, rawValue, 
validationForm),
+      this.getResponseHandler('validate', NotificationType.ERROR, rawValue, 
validationForm)
     );
     request$
       .filter((response: Response): boolean => response.ok)
@@ -178,5 +197,4 @@ export class ShipperConfigurationComponent implements 
CanComponentDeactivate, On
   canDeactivate() {
     return this.configurationFormRef.canDeactivate();
   }
-
 }
diff --git 
a/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.html
 
b/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.html
index 7e9113700b..742eb1dd91 100644
--- 
a/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.html
+++ 
b/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.html
@@ -14,52 +14,78 @@
   See the License for the specific language governing permissions and
   limitations under the License.
 -->
-<ng-template #typeAheadTpl let-item="item">
-  {{item | translate}}
-</ng-template>
+<ng-template #typeAheadTpl let-item="item"> {{ item | translate }} 
</ng-template>
 <div class="container-fluid">
   <div class="row">
     <div class="shipper-form-configuration col-md-6">
       <form [formGroup]="configurationForm" 
(ngSubmit)="onConfigurationSubmit($event)">
         <fieldset [disabled]="disabled">
-          <h2>{{(serviceName ? 'shipperConfiguration.form.titleEdit' : 
'shipperConfiguration.form.titleAdd') | translate}}</h2>
-          <div [ngClass]="{'has-error': serviceNameField.invalid, 
'form-group': true}">
+          <h2>
+            {{
+              (serviceName ? 'shipperConfiguration.form.titleEdit' : 
'shipperConfiguration.form.titleAdd') | translate
+            }}
+          </h2>
+          <div
+            [ngClass]="{
+              'has-error': serviceNameField.invalid && 
configurationForm.touched,
+              'form-group': true
+            }"
+          >
             <label>
-              {{'shipperConfiguration.form.serviceLabel' | translate}}
-              <span *ngIf="serviceNameField.errors && 
serviceNameField.errors.serviceNameExists"
-                    class="help-block validation-block pull-right">
-                {{'shipperConfiguration.form.errors.serviceName.exists' | 
translate}}
+              {{ 'shipperConfiguration.form.serviceLabel' | translate }}
+              <span
+                *ngIf="serviceNameField.errors && 
serviceNameField.errors.serviceNameExists"
+                class="help-block validation-block pull-right"
+              >
+                {{ 'shipperConfiguration.form.errors.serviceName.exists' | 
translate }}
               </span>
-              <span *ngIf="serviceNameField.errors && 
serviceNameField.errors.required"
-                    class="help-block validation-block pull-right">
-                {{'common.form.errors.required' | translate}}
+              <span
+                *ngIf="serviceNameField.errors && 
serviceNameField.errors.required"
+                class="help-block validation-block pull-right"
+              >
+                {{ 'common.form.errors.required' | translate }}
               </span>
             </label>
-            <input *ngIf="!serviceName" formControlName="serviceName" 
class="form-control">
+            <input *ngIf="!serviceName" formControlName="serviceName" 
class="form-control" />
             <ng-container *ngIf="serviceName">
-              <div 
class="shipper-configuration-service-name">{{serviceName}}</div>
-              <input type="hidden" name="serviceName" 
formControlName="serviceName">
+              <div class="shipper-configuration-service-name">{{ serviceName 
}}</div>
+              <input type="hidden" name="serviceName" 
formControlName="serviceName" />
             </ng-container>
           </div>
-          <input type="hidden" name="clusterName" 
formControlName="clusterName">
-          <div [ngClass]="{'has-error': configurationField.invalid, 
'form-group': true}">
+          <input type="hidden" name="clusterName" 
formControlName="clusterName" />
+          <div
+            [ngClass]="{
+              'has-error': configurationField.invalid,
+              'form-group': true
+            }"
+          >
             <label>
-              {{'shipperConfiguration.form.configurationJSONLabel' | 
translate}}
-              <span *ngIf="configurationField.errors && 
configurationField.errors.invalidJSON"
-                    class="help-block validation-block pull-right">
-                {{'shipperConfiguration.form.errors.configuration.invalidJSON' 
| translate}}
+              {{ 'shipperConfiguration.form.configurationJSONLabel' | 
translate }}
+              <span
+                *ngIf="configurationField.errors && 
configurationField.errors.invalidJSON"
+                class="help-block validation-block pull-right"
+              >
+                {{ 
'shipperConfiguration.form.errors.configuration.invalidJSON' | translate }}
               </span>
-              <span *ngIf="configurationField.errors && 
configurationField.errors.required"
-                    class="help-block validation-block pull-right">
-                {{'common.form.errors.required' | translate}}
+              <span
+                *ngIf="configurationField.errors && 
configurationField.errors.required"
+                class="help-block validation-block pull-right"
+              >
+                {{ 'common.form.errors.required' | translate }}
               </span>
             </label>
-            <textarea class="form-control configuration" name="configuration"
-              formControlName="configuration"></textarea>
+            <textarea
+              class="form-control configuration"
+              name="configuration"
+              formControlName="configuration"
+            ></textarea>
           </div>
-          <button class="btn btn-primary pull-right" type="submit"
-                  [disabled]="(!configurationForm.valid || 
configurationForm.pristine)">
-            {{'shipperConfiguration.form.saveBtn.label' | translate}}
+          <button
+            class="btn btn-primary pull-right"
+            type="submit"
+            [disabled]="!configurationForm.valid || configurationForm.pristine"
+          >
+            {{ 'shipperConfiguration.form.saveBtn.label' | translate }}
           </button>
         </fieldset>
       </form>
@@ -68,47 +94,67 @@ <h2>{{(serviceName ? 'shipperConfiguration.form.titleEdit' 
: 'shipperConfigurati
       <div class="container-fluid">
         <div class="row">
           <form [formGroup]="validatorForm" 
(ngSubmit)="onValidationSubmit($event)">
-            <h2>{{'shipperConfiguration.validator.title' | translate}}</h2>
-            <input type="hidden" name="clusterName" 
formControlName="clusterName">
+            <h2>{{ 'shipperConfiguration.validator.title' | translate }}</h2>
+            <input type="hidden" name="clusterName" 
formControlName="clusterName" />
             <div class="form-group" 
[class.has-warning]="componentNameField.invalid">
               <label>
-                {{'shipperConfiguration.validator.componentNameLabel' | 
translate}}
-                <span [class.hide]="!componentNameField.errors || 
!componentNameField.errors.required"
-                      class="help-block validation-block pull-right">
-                  {{'common.form.errors.required' | translate}}
+                <span
+                  [class.hide]="
+                    !componentNameField.errors || 
!componentNameField.errors.required || configurationForm.invalid
+                  "
+                  class="help-block validation-block pull-right"
+                >
+                  {{ 'common.form.errors.required' | translate }}
                 </span>
-                <span [class.hide]="!componentNameField.value || 
!componentNameField.errors || 
!componentNameField.errors.serviceNameDoesNotExistInConfiguration"
-                      class="help-block validation-block pull-right">
-                  
{{'shipperConfiguration.form.errors.componentNameField.serviceNameDoesNotExistInConfiguration'
 | translate}}
+                <span
+                  [class.hide]="
+                    !componentNameField.value ||
+                    !componentNameField.errors ||
+                    
!componentNameField.errors.serviceNameDoesNotExistInConfiguration
+                  "
+                  class="help-block validation-block pull-right"
+                >
+                  {{
+                    
'shipperConfiguration.form.errors.componentNameField.serviceNameDoesNotExistInConfiguration'
+                      | translate
+                  }}
                 </span>
               </label>
-              <input class="form-control component-name" name="componentName" 
formControlName="componentName"
-                     [typeahead]="configurationComponents$ | async"
-                     [typeaheadItemTemplate]="typeAheadTpl"
-                     [typeaheadMinLength]="0"
-                     [disableControl]="configurationForm.invalid"
-                     autocomplete="off">
+              <filter-dropdown
+                [options]="configurationComponentsList$ | async"
+                formControlName="componentName"
+                [label]="'shipperConfiguration.validator.componentNameLabel' | 
translate"
+                [showCommonLabelWithSelection]="true"
+                [disabled]="configurationForm.invalid || 
!(configurationComponentsList$ | async).length"
+              ></filter-dropdown>
             </div>
             <div class="form-group" 
[class.has-warning]="sampleDataField.invalid">
               <label>
-                {{'shipperConfiguration.validator.sampleDataLabel' | 
translate}}
-                <span [class.hide]="!sampleDataField.errors || 
!sampleDataField.errors.required"
-                      class="help-block validation-block pull-right">
-                  {{'common.form.errors.required' | translate}}
+                {{ 'shipperConfiguration.validator.sampleDataLabel' | 
translate }}
+                <span
+                  [class.hide]="!sampleDataField.errors || 
!sampleDataField.errors.required"
+                  class="help-block validation-block pull-right"
+                >
+                  {{ 'common.form.errors.required' | translate }}
                 </span>
               </label>
-              <textarea class="form-control sample-data" name="sampleData" 
formControlName="sampleData"
-                        
[disableControl]="configurationForm.invalid"></textarea>
+              <textarea
+                class="form-control sample-data"
+                name="sampleData"
+                formControlName="sampleData"
+                [disableControl]="configurationForm.invalid"
+              ></textarea>
             </div>
             <div *ngIf="validationResponse" class="form-group">
-              <label>
-                {{'shipperConfiguration.validator.result' | translate}}
-              </label>
-              <pre>{{validationResponse | json}}</pre>
+              <label> {{ 'shipperConfiguration.validator.result' | translate 
}} </label>
+              <pre>{{ validationResponse | json }}</pre>
             </div>
-            <button class="btn btn-default pull-right" type="submit"
-                    [disabled]="(!validatorForm.valid)">
-              <i class="fa fa-table" aria-hidden="true"></i> 
{{'shipperConfiguration.form.testBtn.label' | translate}}
+            <button
+              class="btn btn-default pull-right"
+              type="submit"
+              [disabled]="validatorForm.invalid || configurationForm.invalid"
+            >
+              <i class="fa fa-table" aria-hidden="true"></i> {{ 
'shipperConfiguration.form.testBtn.label' | translate }}
             </button>
           </form>
         </div>
@@ -116,8 +162,14 @@ <h2>{{'shipperConfiguration.validator.title' | 
translate}}</h2>
     </div>
   </div>
 </div>
-<modal *ngIf="isLeavingDirtyForm" (submit)="leaveDirtyFormConfirmed()" 
(cancel)="leaveDirtyFormCancelled()"
-       (close)="leaveDirtyFormCancelled()" showCloseButton="false" 
isSmallModal="true"
-       title="{{'shipperConfiguration.form.leavingDirty.title' | translate}}"
-       bodyText="{{'shipperConfiguration.form.leavingDirty.message' | 
translate}}">
+<modal
+  *ngIf="isLeavingDirtyForm"
+  (submit)="leaveDirtyFormConfirmed()"
+  (cancel)="leaveDirtyFormCancelled()"
+  (close)="leaveDirtyFormCancelled()"
+  showCloseButton="false"
+  isSmallModal="true"
+  title="{{ 'shipperConfiguration.form.leavingDirty.title' | translate }}"
+  bodyText="{{ 'shipperConfiguration.form.leavingDirty.message' | translate }}"
+>
 </modal>
diff --git 
a/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.less
 
b/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.less
index f6f371388d..0fa195c9d8 100644
--- 
a/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.less
+++ 
b/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.less
@@ -16,39 +16,51 @@
  * limitations under the License.
  */
 
-@import "../../../shared/forms";
-
-textarea {
-  @extend input.form-control
-  min-height: 5em;
-  resize: vertical;
-  width: 100%;
-  &.validation-result,
-  &.configuration {
-    min-height: 30em;
+@import '../../../shared/forms';
+:host {
+  textarea {
+    &:extend(input.form-control);
+    min-height: 5em;
+    resize: vertical;
+    width: 100%;
+    &.validation-result,
+    &.configuration {
+      min-height: 30em;
+    }
+    &.sample-data {
+      min-height: 8em;
+    }
   }
-  &.sample-data {
-    min-height: 8em;
+  label {
+    width: 100%;
   }
-}
-label {
-  width: 100%;
-}
-.help-block.validation-block {
-  display: none;
-  font-size: .9em;
-  margin: 0;
-  opacity: 0;
-  transition: all 1s ease-in;
-}
-.has-error, .has-warning {
   .help-block.validation-block {
-    display: inline-block;
-    opacity: 1;
+    display: none;
+    font-size: 0.9em;
+    margin: 0;
+    opacity: 0;
+    transition: all 1s ease-in;
+  }
+  .has-error,
+  .has-warning {
+    .help-block.validation-block {
+      display: inline-block;
+      opacity: 1;
+    }
+  }
+
+  .shipper-form-configuration {
+    background-color: @main-background-color;
+    padding-bottom: 4em;
   }
-}
 
-.shipper-form-configuration {
-  background-color: @main-background-color;
-  padding-bottom: 4em;
+  /deep/ filter-dropdown {
+    button,
+    button:focus {
+      padding: 0;
+      .label-before-selection {
+        font-weight: bold;
+      }
+    }
+  }
 }
diff --git 
a/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.ts
 
b/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.ts
index b41b9413e2..d3d319cc2d 100644
--- 
a/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.ts
+++ 
b/ambari-logsearch-web/src/app/modules/shipper/components/shipper-service-configuration-form/shipper-service-configuration-form.component.ts
@@ -16,23 +16,34 @@
  * 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 {
+  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 { 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 { 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 { 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',
@@ -40,7 +51,6 @@ import {ActivatedRoute} from '@angular/router';
   styleUrls: ['./shipper-service-configuration-form.component.less']
 })
 export class ShipperServiceConfigurationFormComponent implements OnInit, 
OnDestroy, OnChanges, CanComponentDeactivate {
-
   private configurationForm: FormGroup;
   private validatorForm: FormGroup;
 
@@ -57,7 +67,7 @@ export class ShipperServiceConfigurationFormComponent 
implements OnInit, OnDestr
   existingServiceNames: Observable<ShipperClusterService[]> | 
ShipperClusterService[];
 
   @Input()
-  validationResponse: {[key: string]: any};
+  validationResponse: { [key: string]: any };
 
   @Input()
   disabled = false;
@@ -69,6 +79,7 @@ export class ShipperServiceConfigurationFormComponent 
implements OnInit, OnDestr
   validationSubmit: EventEmitter<FormGroup> = new EventEmitter<FormGroup>();
 
   private configurationComponents$: Observable<string[]>;
+  private configurationComponentsList$: Observable<ListItem[]>;
 
   private isLeavingDirtyForm = false;
 
@@ -94,18 +105,20 @@ export class ShipperServiceConfigurationFormComponent 
implements OnInit, OnDestr
 
   private canDeactivateModalResult: Subject<boolean> = new Subject<boolean>();
 
-  private canDeactivateObservable$: Observable<boolean> = 
Observable.create((observer: Observer<boolean>)  => {
-    this.subscriptions.push(
-      this.canDeactivateModalResult.subscribe((result: boolean) => {
-        observer.next(result);
-      })
-    );
+  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 serviceNamesListSubject: BehaviorSubject<ShipperClusterService[]> = 
new BehaviorSubject<
+    ShipperClusterService[]
+  >([]);
 
   private subscriptions: Subscription[] = [];
 
+  private destroyed$ = new Subject();
+
   constructor(
     private formBuilder: FormBuilder,
     private activatedRoute: ActivatedRoute,
@@ -118,33 +131,52 @@ export class ShipperServiceConfigurationFormComponent 
implements OnInit, OnDestr
 
   ngOnInit() {
     this.subscriptions.push(
-      this.activatedRoute.params.map(params => 
params.service).subscribe((service) => {
-        this.serviceName = service;
-      })
+      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.changeDetectionRef.detectChanges();
     }
-    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.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.subscriptions.push(
-        this.existingServiceNames.subscribe((serviceNames: 
ShipperClusterService[]) => {
-          this.serviceNamesListSubject.next(serviceNames);
-        })
-      );
+      
this.existingServiceNames.takeUntil(this.destroyed$).subscribe((serviceNames: 
ShipperClusterService[]) => {
+        this.serviceNamesListSubject.next(serviceNames);
+      });
     } else {
       this.serviceNamesListSubject.next(this.existingServiceNames);
     }
@@ -168,7 +200,11 @@ export class ShipperServiceConfigurationFormComponent 
implements OnInit, OnDestr
         }
       });
     }
-    if (this.validatorForm && changes.clusterName && 
this.validatorForm.controls.clusterName.value !== 
changes.clusterName.currentValue) {
+    if (
+      this.validatorForm &&
+      changes.clusterName &&
+      this.validatorForm.controls.clusterName.value !== 
changes.clusterName.currentValue
+    ) {
       
this.validatorForm.controls.clusterName.setValue(changes.clusterName.currentValue);
       this.validatorForm.markAsPristine();
     }
@@ -178,17 +214,18 @@ export class ShipperServiceConfigurationFormComponent 
implements OnInit, OnDestr
     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) {
@@ -203,39 +240,33 @@ export class ShipperServiceConfigurationFormComponent 
implements OnInit, OnDestr
   }
 
   createForms(): void {
-    const configuration: ShipperClusterServiceConfigurationInterface = 
this.configuration || (
-      this.serviceName ? this.configuration : new ShipperConfigurationModel()
-    );
+    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()]
-      )
+      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]
-      ),
+      clusterName: this.formBuilder.control(this.clusterName, 
[Validators.required]),
       componentName: this.formBuilder.control('', [
         Validators.required,
-        
formValidators.getConfigurationServiceValidator(this.configurationForm.controls.configuration)
+        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.subscriptions.push(
-      this.configurationForm.valueChanges.subscribe(() => {
-        this.validatorForm.controls.componentName.updateValueAndValidity();
-        
this.validatorForm.controls.configuration.setValue(this.configurationForm.controls.configuration.value);
-      })
-    );
+    
this.configurationForm.valueChanges.takeUntil(this.destroyed$).subscribe(() => {
+      this.validatorForm.controls.componentName.updateValueAndValidity();
+      
this.validatorForm.controls.configuration.setValue(this.configurationForm.controls.configuration.value);
+    });
   }
 
   onConfigurationSubmit(): void {
@@ -249,5 +280,4 @@ export class ShipperServiceConfigurationFormComponent 
implements OnInit, OnDestr
       this.validationSubmit.emit(this.validatorForm);
     }
   }
-
 }
diff --git 
a/ambari-logsearch-web/src/app/modules/shipper/directives/validator.directive.ts
 
b/ambari-logsearch-web/src/app/modules/shipper/directives/validator.directive.ts
index 50c12375a5..a5901250b6 100644
--- 
a/ambari-logsearch-web/src/app/modules/shipper/directives/validator.directive.ts
+++ 
b/ambari-logsearch-web/src/app/modules/shipper/directives/validator.directive.ts
@@ -16,46 +16,49 @@
  * limitations under the License.
  */
 
-import {AbstractControl, ValidatorFn} from '@angular/forms';
-import {ShipperClusterService} from 
'@modules/shipper/models/shipper-cluster-service.type';
-import {ValidationErrors} from '@angular/forms/src/directives/validators';
-import {BehaviorSubject} from 'rxjs/BehaviorSubject';
+import { AbstractControl, ValidatorFn } from '@angular/forms';
+import { ShipperClusterService } from 
'@modules/shipper/models/shipper-cluster-service.type';
+import { ValidationErrors } from '@angular/forms/src/directives/validators';
+import { BehaviorSubject } from 'rxjs/BehaviorSubject';
 
 export function configurationValidator(): ValidatorFn {
   return (control: AbstractControl): ValidationErrors | null => {
     try {
-      const json: {[key: string]: any} = JSON.parse(control.value);
+      const json: { [key: string]: any } = JSON.parse(control.value);
       return null;
     } catch (error) {
       return {
-        invalidJSON: {value: control.value}
+        invalidJSON: { value: control.value }
       };
     }
   };
 }
 
-export function uniqueServiceNameValidator(
-  serviceNames: BehaviorSubject<ShipperClusterService[]>
-): ValidatorFn {
+export function uniqueServiceNameValidator(serviceNames: 
BehaviorSubject<ShipperClusterService[]>): ValidatorFn {
   return (control: AbstractControl): ValidationErrors | null => {
     const services: ShipperClusterService[] = serviceNames.getValue();
-    return services && services.indexOf(control.value) > -1 ? {
-      serviceNameExists: {value: control.value}
-    } : null;
+    return services && services.indexOf(control.value) > -1
+      ? {
+          serviceNameExists: { value: control.value }
+        }
+      : null;
   };
 }
 
-export function getConfigurationServiceValidator(configControl: 
AbstractControl): ValidatorFn {
+export function getConfigurationServiceValidator(configControl: 
AbstractControl, valueMapper?: Function): ValidatorFn {
   return (control: AbstractControl): ValidationErrors | null => {
     let components: string[];
     try {
-      const inputs: {[key: string]: any}[] = (configControl.value ? 
JSON.parse(configControl.value) : {}).input;
+      const inputs: { [key: string]: any }[] = (configControl.value ? 
JSON.parse(configControl.value) : {}).input;
       components = inputs && inputs.length ? inputs.map(input => input.type) : 
[];
     } catch (error) {
       components = [];
     }
-    return components.length && components.indexOf(control.value) === -1 ? {
-      serviceNameDoesNotExistInConfiguration: {value: control.value}
-    } : null;
+    const value = valueMapper ? valueMapper(control.value) : control.value;
+    return components.length && components.indexOf(value) === -1
+      ? {
+          serviceNameDoesNotExistInConfiguration: { value: value }
+        }
+      : null;
   };
 }


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to