This is an automated email from the ASF dual-hosted git repository.

tobiasistvan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ambari-logsearch.git


The following commit(s) were added to refs/heads/master by this push:
     new b89f41f  [AMBARI-25028] [Log Search UI] Populate `Component Name` in 
validator (#67)
b89f41f is described below

commit b89f41f357d77d6e8e95c7a0aebc43f0f17563b2
Author: Istvan Tobias <tobias.ist...@gmail.com>
AuthorDate: Sat Jan 5 23:20:12 2019 +0100

    [AMBARI-25028] [Log Search UI] Populate `Component Name` in validator (#67)
    
    * MBARI-23456. Add cloud mode documentation (#66)
    
    * [AMBARI-25028] [Log Search UI] Populate `Component Name` in validator
---
 .../dropdown-button/dropdown-button.component.html |  29 ++--
 .../dropdown-button/dropdown-button.component.ts   |  58 +++++--
 .../filter-dropdown.component.spec.ts              |  69 ++++----
 .../filter-dropdown/filter-dropdown.component.ts   |  23 +--
 .../shipper-configuration.component.spec.ts        |  78 ++++-----
 .../shipper-configuration.component.ts             | 112 +++++++------
 ...ipper-service-configuration-form.component.html | 174 +++++++++++++--------
 ...ipper-service-configuration-form.component.less |  72 +++++----
 ...shipper-service-configuration-form.component.ts | 164 +++++++++++--------
 .../shipper/directives/validator.directive.ts      |  37 +++--
 10 files changed, 488 insertions(+), 328 deletions(-)

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 52f97be..b24d15a 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 77ea8e1..fb8a981 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 4157333..a8aff3f 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 b0c766e..2bcf4a2 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 0ec4f56..a8e2b1f 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 d8fccbd..82ae869 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 7e91137..742eb1d 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 @@
       <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 @@
     </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 f6f3713..0fa195c 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 b41b941..d3d319c 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 50c1237..a590125 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;
   };
 }

Reply via email to