Sfoglia il codice sorgente

AMBARI-1303. Remake clearFilters function in app_view. (Arun Kandregula via yusaku)

git-svn-id: https://svn.apache.org/repos/asf/incubator/ambari/trunk@1440747 13f79535-47bb-0310-9956-ffa450edef68
Yusaku Sako 12 anni fa
parent
commit
fcb07f3a98

+ 3 - 0
CHANGES.txt

@@ -36,6 +36,9 @@ Trunk (unreleased changes):
 
  IMPROVEMENTS
 
+ AMBARI-1303. Remake clearFilters function in app_view. (Arun Kandregula via
+ yusaku)
+
  AMBARI-1302. Minor label cleanup on Jobs Charts popup. (Arun Kandregula via
  yusaku)
 

+ 13 - 0
ambari-web/app/controllers/main/apps_controller.js

@@ -28,6 +28,19 @@ App.MainAppsController = Em.ArrayController.extend({
   loaded : false,
   loading : false,
 
+  /**
+   * List of users.
+   * Will be used for filtering in user column.
+   * Go to App.MainAppsView.userFilterView for more information
+   */
+  users: function () {
+    return this.get('content').mapProperty("userName").uniq().map(function(userName){
+      return {
+        name: userName,
+        checked: false
+      };
+    });
+  }.property('content.length'),
 
   loadRuns:function () {
 

+ 9 - 15
ambari-web/app/styles/apps.less

@@ -56,29 +56,27 @@
       word-wrap: break-word;
     }
 
-    .view-wrapper {
-      width: 73%;
-    }
     input, select{
-      width: 90%;
+      width: 77%;
+    }
+
+    input.input-super-mini{
+      width: 47px;
+      max-width: 57%;
     }
+
     label.checkbox input {
       width: auto;
     }
     .col8,
     td:first-child + td,
     th:first-child + th{
-      width: 15%;
-    }
-    .col0,
-    td:first-child + td,
-    th:first-child + th{
-      width: 15%;
+      width: 11%;
     }
     .col1,
     td:first-child + td + td,
     th:first-child + th + th{
-      width: 12%;
+      width: 15%;
     }
     .col2,
     td:first-child + td + td + td,
@@ -253,10 +251,6 @@
     }
   }
 
-  div.view-wrapper {
-    float: left;
-  }
-
   a.ui-icon-circle-close {
     float: right;
     opacity: 0.2;

+ 10 - 10
ambari-web/app/templates/main/apps.hbs

@@ -66,7 +66,7 @@
     </div>
   </div>
 
-  <table class="table table-striped runsList" id="dataTable">
+  <table class="table table-striped runsList">
     <thead>
     {{#view view.wrapSorting}}
       {{#each controller.columnsName}}
@@ -76,15 +76,15 @@
       {{/each}}
     {{/view}}
     <tr>
-      <th class="notActive"><div class="view-wrapper">{{view view.appidFilterView valueBinding="controller.filterObject.sSearch_0" viewName="appidFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_appidFilterViewInstance" class="ui-icon ui-icon-circle-close ui-appid"></a></th>
-      <th class="notActive"><div class="view-wrapper">{{view view.nameFilterView valueBinding="controller.filterObject.sSearch_1" viewName="nameFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_nameFilterViewInstance" class="ui-icon ui-icon-circle-close ui-name"></a></th>
-      <th class="notActive"><div class="view-wrapper">{{view view.typeSelectView selectionBinding="controller.filterObject.runType" viewName="typeSelectViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_typeSelectViewInstance" class="ui-icon ui-icon-circle-close ui-type"></a></th>
-      <th class="notActive"><div class="view-wrapper">{{view view.userFilterView viewName="userFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_userFilterViewInstance" class="ui-icon ui-icon-circle-close ui-user"></a><input id="user_filter" type="hidden"></th>
-      <th class="notActive"><div class="view-wrapper">{{view view.jobsFilterView valueBinding="controller.filterObject.jobs" viewName="jobsFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_jobsFilterViewInstance" class="ui-icon ui-icon-circle-close ui-jobs"></a></th>
-      <th class="notActive"><div class="view-wrapper">{{view view.inputFilterView valueBinding="controller.filterObject.input" viewName="inputFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_inputFilterViewInstance" class="ui-icon ui-icon-circle-close ui-input"></a></th>
-      <th class="notActive"><div class="view-wrapper">{{view view.outputFilterView valueBinding="controller.filterObject.output" viewName="outputFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_outputFilterViewInstance" class="ui-icon ui-icon-circle-close ui-output"></a></th>
-      <th class="notActive"><div class="view-wrapper">{{view view.durationFilterView valueBinding="controller.filterObject.duration" viewName="durationFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_durationFilterViewInstance" class="ui-icon ui-icon-circle-close ui-duration"></a></th>
-      <th class="notActive"><div class="view-wrapper">{{view view.rundateSelectView selectionBinding="controller.filterObject.runDate" viewName="rundateSelectViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_rundateSelectViewInstance" class="ui-icon ui-icon-circle-close ui-rundate"></a><input id="custom_rundate_filter" type="hidden"></th>
+      <th>{{view view.appIdFilterView}}</th>
+      <th>{{view view.nameFilterView}}</th>
+      <th>{{view view.typeFilterView}}</th>
+      <th>{{view view.userFilterView}}</th>
+      <th>{{view view.jobsFilterView}}</th>
+      <th>{{view view.inputFilterView}}</th>
+      <th>{{view view.outputFilterView}}</th>
+      <th>{{view view.durationFilterView}}</th>
+      <th>{{view view.runDateFilterView}}</th>
     </tr>
     </thead>
     <tbody>

+ 2 - 4
ambari-web/app/templates/main/host/component_filter.hbs

@@ -16,7 +16,6 @@
 * limitations under the License.
 }}
 
-<div {{bindAttr class="view.btnGroupClass"}}>
     <button class="btn btn-info single-btn-group" {{action "clickFilterButton" target="view"}}>
         Components <span class="caret"></span>
     </button>
@@ -68,8 +67,7 @@
             </ul>
         </li>
         <li>
-            <button class="btn" {{action "closeFilters" target="view"}}>Cancel</button>
+            <button class="btn" {{action "closeFilter" target="view"}}>Cancel</button>
             <button class="btn btn-primary" {{action "applyFilter" target="view"}}>Apply</button>
         </li>
-    </ul>
-</div>
+    </ul>

+ 99 - 66
ambari-web/app/views/common/filter_view.js

@@ -16,20 +16,42 @@
  * limitations under the License.
  */
 
+/**
+ * Wrapper View for all filter components. Layout template and common actions are located inside of it.
+ * Logic specific for data component(input, select, or custom multi select, which fire any changes on interface) are
+ * located in inner view - <code>filterView</code>.
+ *
+ * If we want to have input filter, put <code>textFieldView</code> to it.
+ * All inner views implemented below this view.
+ * @type {*}
+ */
 var wrapperView = Ember.View.extend({
   classNames: ['view-wrapper'],
-  layout: Ember.Handlebars.compile('<a href="#" {{action "clearFilter" target="view"}} class="ui-icon ui-icon-circle-close ui-name"></a> {{yield}}'),
+  layout: Ember.Handlebars.compile('<a href="#" {{action "clearFilter" target="view"}} class="ui-icon ui-icon-circle-close"></a> {{yield}}'),
   template: Ember.Handlebars.compile('{{#if view.fieldId}}<input type="hidden" id="{{unbound view.fieldId}}" value="" />{{/if}} {{view view.filterView}}'),
 
   value: null,
 
+  /**
+   * If this field is exists we dynamically create hidden input element and set value there.
+   * Used for some cases, where this values will be used outside of component
+   */
+  fieldId: null,
+
   clearFilter: function(){
     this.set('value', this.get('emptyValue'));
     return false;
   },
 
+  /**
+   * Use to determine whether filter is clear or not. Also when we want to set empty value
+   */
   emptyValue: '',
 
+  /**
+   * Whether our <code>value</code> is empty or not
+   * @return {Boolean}
+   */
   isEmpty: function(){
     if(this.get('value') === null){
       return true;
@@ -37,6 +59,11 @@ var wrapperView = Ember.View.extend({
     return this.get('value').toString() === this.get('emptyValue').toString();
   },
 
+  /**
+   * Show/Hide <code>Clear filter</code> button.
+   * Also this method updates computed field related to <code>fieldId</code> if it exists.
+   * Call <code>onChangeValue</code> callback when everything is done.
+   */
   showClearFilter: function(){
     if(!this.get('parentNode')){
       return;
@@ -62,13 +89,14 @@ var wrapperView = Ember.View.extend({
 
   },
 
+  /**
+   * Filter components is located here. Should be redefined
+   */
   filterView: Em.View,
 
-  init: function(){
-    this.set('value', this.get('emptyValue'));
-    this._super();
-  },
-
+  /**
+   * Update class of parentNode(hide clear filter button) on page load
+   */
   didInsertElement: function(){
     var parent = this.$().parent();
     this.set('parentNode', parent);
@@ -76,62 +104,45 @@ var wrapperView = Ember.View.extend({
   }
 });
 
+/**
+ * Simple input control for wrapperView
+ */
 var textFieldView = Ember.TextField.extend({
   type:'text',
   placeholder: 'Any',
   valueBinding: "parentView.value"
 });
 
+/**
+ * Simple multiselect control for wrapperView.
+ * Used to render blue button and popup, which opens on button click.
+ * All content related logic should be implemented manually outside of it
+ */
 var componentFieldView = Ember.View.extend({
-  templateName: require('templates/main/host/component_filter'),
   classNames: ['btn-group'],
-  classNameBindings: ['open'],
-  multiple: true,
+  classNameBindings: ['isFilterOpen:open:'],
 
+  /**
+   * Whether popup is shown or not
+   */
   isFilterOpen: false,
 
-  btnGroupClass:function () {
-    return this.get('isFilterOpen') ? 'btn-group open' : 'btn-group';
-  }.property('isFilterOpen'),
-
+  /**
+   * We have <code>value</code> property similar to inputs <code>value</code> property
+   */
   valueBinding: 'parentView.value',
-  masterComponentsBinding: 'controller.masterComponents',
-  slaveComponentsBinding: 'controller.slaveComponents',
-  clientComponentsBinding: 'controller.clientComponents',
-
-  masterComponentsChecked:false,
-  toggleMasterComponents:function () {
-    this.get('masterComponents').setEach('checkedForHostFilter', this.get('masterComponentsChecked'));
-  }.observes('masterComponentsChecked'),
-
-  slaveComponentsChecked:false,
-  toggleSlaveComponents:function () {
-    this.get('slaveComponents').setEach('checkedForHostFilter', this.get('slaveComponentsChecked'));
-  }.observes('slaveComponentsChecked'),
-
-  clientComponentsChecked: false,
-  toggleClientComponents: function() {
-    this.get('clientComponents').setEach('checkedForHostFilter', this.get('clientComponentsChecked'));
-  }.observes('clientComponentsChecked'),
 
   /**
    * Clear filter to initial state
    */
-  clearFilter:function() {
-    this.set('masterComponentsChecked', false);
-    this.set('slaveComponentsChecked', false);
-    this.set('clientComponentsChecked', false);
-
-    this.get('masterComponents').setEach('checkedForHostFilter', false);
-    this.get('slaveComponents').setEach('checkedForHostFilter', false);
-    this.get('clientComponents').setEach('checkedForHostFilter', false);
-    this.set('value', []);
+  clearFilter: function(){
+    this.set('value', '');
   },
 
   /**
    * Onclick handler for <code>cancel filter</code> button
    */
-  closeFilters:function () {
+  closeFilter:function () {
     $(document).unbind('click');
     this.set('isFilterOpen', false);
   },
@@ -140,20 +151,7 @@ var componentFieldView = Ember.View.extend({
    * Onclick handler for <code>apply filter</code> button
    */
   applyFilter:function() {
-    this.closeFilters();
-
-    var chosenComponents = [];
-
-    this.get('masterComponents').filterProperty('checkedForHostFilter', true).forEach(function(item){
-      chosenComponents.push(item.get('displayName'));
-    });
-    this.get('slaveComponents').filterProperty('checkedForHostFilter', true).forEach(function(item){
-      chosenComponents.push(item.get('displayName'));
-    });
-    this.get('clientComponents').filterProperty('checkedForHostFilter', true).forEach(function(item){
-      chosenComponents.push(item.get('displayName'));
-    });
-    this.set('value', chosenComponents);
+    this.closeFilter();
   },
 
   /**
@@ -175,21 +173,38 @@ var componentFieldView = Ember.View.extend({
         firstClick = false;
       });
     }
-  },
-
-  didInsertElement:function () {
-    if (this.get('controller.comeWithFilter')) {
-      this.applyFilter();
-      this.set('controller.comeWithFilter', false);
-    } else {
-      this.clearFilter();
-    }
   }
 });
 
+/**
+ * Simple select control for wrapperView
+ */
+var selectFieldView = Ember.Select.extend({
+  selectionBinding: 'parentView.value',
+  contentBinding: 'parentView.content'
+});
+
+/**
+ * Result object, which will be accessible outside
+ * @type {Object}
+ */
 module.exports = {
+  /**
+   * You can access wrapperView outside
+   */
   wrapperView : wrapperView,
 
+  /**
+   * And also controls views if need it
+   */
+  textFieldView : textFieldView,
+  selectFieldView: selectFieldView,
+  componentFieldView: componentFieldView,
+
+  /**
+   * Quick create input filters
+   * @param config parameters of <code>wrapperView</code>
+   */
   createTextView : function(config){
 
     config.fieldType = config.fieldType || 'input-medium';
@@ -199,9 +214,12 @@ module.exports = {
 
     return wrapperView.extend(config);
   },
+
+  /**
+   * Quick create multiSelect filters
+   * @param config parameters of <code>wrapperView</code>
+   */
   createComponentView : function(config){
-    config.filterView = componentFieldView;
-    config.emptyValue = [];
     config.clearFilter = function(){
       this.forEachChildView(function(item){
         if(item.clearFilter){
@@ -211,6 +229,21 @@ module.exports = {
       return false;
     };
 
+    return wrapperView.extend(config);
+  },
+
+  /**
+   * Quick create select filters
+   * @param config parameters of <code>wrapperView</code>
+   */
+  createSelectView: function(config){
+
+    config.fieldType = config.fieldType || 'input-medium';
+    config.filterView = selectFieldView.extend({
+      classNames : [ config.fieldType ]
+    });
+    config.emptyValue = 'Any';
+
     return wrapperView.extend(config);
   }
 };

+ 150 - 270
ambari-web/app/views/main/apps_view.js

@@ -17,323 +17,221 @@
  */
 
 var App = require('app');
+var filters = require('views/common/filter_view');
 
 App.MainAppsView = Em.View.extend({
-  templateName:require('templates/main/apps'),
-
-  /**
-   * List of users
-   */
-  users:function () {
-    return this.get('controller.content').mapProperty("userName").uniq();
-  }.property('controller.content.length'),
+  templateName: require('templates/main/apps'),
 
   //Pagination left/right buttons css class
-  paginationLeft : Ember.View.extend({
+  paginationLeft: Ember.View.extend({
     tagName: 'a',
     template: Ember.Handlebars.compile('<i class="icon-arrow-left"></i>'),
     classNameBindings: ['class'],
-    class:"",
-    calculateClass: function(){
-      if(parseInt(this.get("controller.paginationObject.startIndex"))>1){
-        this.set("class","paginate_previous");
-      }else{
-        this.set("class","paginate_disabled_previous");
+    class: "",
+    calculateClass: function () {
+      if (parseInt(this.get("controller.paginationObject.startIndex")) > 1) {
+        this.set("class", "paginate_previous");
+      } else {
+        this.set("class", "paginate_disabled_previous");
       }
     }.observes("controller.paginationObject"),
-    click: function(event){
-      if(this.class == "paginate_previous"){
-        var startIndex=parseInt(this.get("controller.paginationObject.startIndex"))-1;
-        var showRows=parseInt(this.get("controller.filterObject.iDisplayLength"));
-        var startDisplayValue = Math.max(0,startIndex-showRows);
+    click: function (event) {
+      if (this.class == "paginate_previous") {
+        var startIndex = parseInt(this.get("controller.paginationObject.startIndex")) - 1;
+        var showRows = parseInt(this.get("controller.filterObject.iDisplayLength"));
+        var startDisplayValue = Math.max(0, startIndex - showRows);
         this.set("controller.filterObject.iDisplayStart", startDisplayValue);
       }
     }
   }),
-  paginationRight : Ember.View.extend({
+  paginationRight: Ember.View.extend({
     tagName: 'a',
     template: Ember.Handlebars.compile('<i class="icon-arrow-right"></i>'),
     classNameBindings: ['class'],
-    class:"",
-    calculateClass: function(){
-      if((parseInt(this.get("controller.paginationObject.endIndex"))+1)<parseInt(this.get("controller.paginationObject.iTotalDisplayRecords"))){
-        this.set("class","paginate_next");
-      }else{
-        this.set("class","paginate_disabled_next");
+    class: "",
+    calculateClass: function () {
+      if ((parseInt(this.get("controller.paginationObject.endIndex")) + 1) < parseInt(this.get("controller.paginationObject.iTotalDisplayRecords"))) {
+        this.set("class", "paginate_next");
+      } else {
+        this.set("class", "paginate_disabled_next");
       }
     }.observes("controller.paginationObject"),
-    click: function(event){
-      if(this.class == "paginate_next"){
+    click: function (event) {
+      if (this.class == "paginate_next") {
         var startDisplayValue = parseInt(this.get("controller.paginationObject.endIndex"));
         this.set("controller.filterObject.iDisplayStart", startDisplayValue);
       }
     }
   }),
 
-  wrapSorting:Ember.View.extend({
+  /**
+   * View for RunPerPage select component
+   */
+  runPerPageSelectView: Em.Select.extend({
+
+    selected: '10',
+    content: ['10', '25', '50', '100']
+  }),
+
+  wrapSorting: Ember.View.extend({
     tagName: 'tr'
   }),
 
   sortingColumns: Ember.View.extend({
     tagName: 'th',
-    classNameBindings: ['class','widthClass'],
-    class:"sorting",
-    widthClass:"",
+    classNameBindings: ['class', 'widthClass'],
+    class: "sorting",
+    widthClass: "",
     content: null,
-    didInsertElement:function(){
-      this.set("widthClass","col"+this.content.index);
+    didInsertElement: function () {
+      this.set("widthClass", "col" + this.content.index);
     },
-    click: function(event){
+    click: function (event) {
       console.log(this.class);
-      if(this.class == "sorting"){
+      if (this.class == "sorting") {
         this.resetSortClass();
-        this.setControllerObj(this.content.index,"ASC");
+        this.setControllerObj(this.content.index, "ASC");
         this.set("class", "sorting_asc");
-      }else if(this.class == "sorting_asc"){
-        this.setControllerObj(this.content.index,"DESC");
+      } else if (this.class == "sorting_asc") {
+        this.setControllerObj(this.content.index, "DESC");
         this.set("class", "sorting_desc");
-      }else if(this.class == "sorting_desc"){
-        this.setControllerObj("","");
+      } else if (this.class == "sorting_desc") {
+        this.setControllerObj("", "");
         this.set("class", "sorting");
       }
     },
-    resetSortClass:function(){
-      this.get("parentView.childViews").map(function(a,e){a.get("childViews")[0].set("class","sorting")});
+    resetSortClass: function () {
+      this.get("parentView.childViews").map(function (a, e) {
+        a.get("childViews")[0].set("class", "sorting")
+      });
     },
-    setControllerObj:function(col,dir){
+    setControllerObj: function (col, dir) {
       this.set("controller.filterObject.iSortCol_0", col);
       this.set("controller.filterObject.sSortDir_0", dir);
     }
   }),
 
-  /**
-   * Filter-field for AppId
-   */
-  appidFilterView:Em.TextField.extend({
-    classNames:['input-small-app'],
-    type:'text',
-    placeholder:'Any ID',
-    elementId:'appid_filter',
-    filtering:function () {
-      if (this.get('value') == '') {
-        this.$().closest('th').addClass('notActive');
-      }
-      else {
-        this.$().closest('th').removeClass('notActive');
-      }
-    }.observes('value')
-  }),
   /**
    * Filter-field for Search
    */
-  appSearchThrough:Em.TextField.extend({
-    classNames:['input-medium'],
-    type:'text',
-    placeholder:'Search',
-    filtering:function () {
-      if (this.get('value') == '') {
-        this.$().closest('th').addClass('notActive');
-      }
-      else {
-        this.$().closest('th').removeClass('notActive');
-      }
-    }.observes('value')
+  appSearchThrough: Em.TextField.extend({
+    classNames: ['input-medium'],
+    type: 'text',
+    placeholder: 'Search'
   }),
   /**
-   * Filter-field for name
+   * Filter-field for App ID.
+   * Based on <code>filters</code> library
    */
-  nameFilterView:Em.TextField.extend({
-    classNames:['input-small'],
-    type:'text',
-    placeholder:'Any Name',
-    filtering:function () {
-      if (this.get('value') == '') {
-        this.$().closest('th').addClass('notActive');
-      }
-      else {
-        this.$().closest('th').removeClass('notActive');
-      }
-    }.observes('value')
+  appIdFilterView: filters.createTextView({
+    valueBinding: "controller.filterObject.sSearch_0"
   }),
   /**
-   * dataTable filter views
+   * Filter-field for name.
+   * Based on <code>filters</code> library
    */
-  typeSelectView:Em.Select.extend({
-    classNames:['input-small'],
-    selected:'Any',
-    content:['Any', 'Pig', 'Hive', 'MapReduce'],
-    change:function (event) {
-      if (this.get('selection') === 'Any') {
-        this.$().closest('th').addClass('notActive');
-      }
-      else {
-        this.$().closest('th').removeClass('notActive');
-      }
-    }
+  nameFilterView: filters.createTextView({
+    valueBinding: "controller.filterObject.sSearch_1",
+    fieldType: 'input-small'
   }),
   /**
-   * dataTable filter views
+   * Filter-field for type.
+   * Based on <code>filters</code> library
    */
-  runPerPageSelectView:Em.Select.extend({
-
-    selected:'10',
-    content:['10', '25', '50', '100']
+  typeFilterView: filters.createSelectView({
+    fieldType: 'input-small',
+    valueBinding: "controller.filterObject.runType",
+    content: ['Any', 'Pig', 'Hive', 'MapReduce']
   }),
+
   /**
-   * Filter-list for User
+   * Filter-list for User.
+   * Based on <code>filters</code> library
    */
-  userFilterView:Em.View.extend({
-    classNames:['btn-group'],
-    classNameBindings:['open'],
-    multiple:true,
-    open:false,
-    users:function () {
-      var users = [];
-      for (var i = 0; i < this.get('parentView').get('users').length; i++)
-        users.push(Ember.Object.create({
-          name:this.get('parentView').get('users')[i],
-          checked:false
-        }));
-      return users;
-    }.property('parentView.users'),
-    templateName:require('templates/main/apps/user_filter'),
-    allComponentsChecked:false,
-    toggleAllComponents:function () {
-      var checked = this.get('allComponentsChecked');
-      this.get('users').forEach(function (item) {
-        item.set('checked', checked);
-      });
-    }.observes('allComponentsChecked'),
-    clickFilterButton:function (event) {
-      this.set('open', !this.get('open'));
-    },
-    clearFilter:function (self) {
-      self.set('allComponentsChecked', true);
-      self.set('allComponentsChecked', false);
-      jQuery('#user_filter').val([]);
-      self.get('parentView').get('controller.filterObject').set("sSearch_3","");
-      jQuery('#user_filter').closest('th').addClass('notActive');
-    },
-    closeFilter:function () {
-      this.set('open', false);
-    },
-    applyFilter:function () {
-      var chosenUsers = new Array();
-      this.set('open', !this.get('open'));
-      this.get('users').forEach(function (item) {
-        if (item.get('checked')) chosenUsers.push(item.get('name'));
-      });
+  userFilterView: filters.createComponentView({
+    /**
+     * Inner FilterView. Used just to render component. Value bind to <code>mainview.value</code> property
+     * Base methods was implemented in <code>filters.componentFieldView</code>
+     */
+    filterView: filters.componentFieldView.extend({
+      templateName:require('templates/main/apps/user_filter'),
 
-      /**
-       * Set filterObject
-       */
-      this.get('parentView').get('controller.filterObject').set("sSearch_3",chosenUsers)
+      usersBinding: 'controller.users',
 
-      jQuery('#user_filter').val(chosenUsers);
-      if (chosenUsers.length == 0) {
-        this.$().closest('th').addClass('notActive');
-      }
-      else {
-        this.$().closest('th').removeClass('notActive');
+      allComponentsChecked:false,
+      toggleAllComponents:function () {
+        var checked = this.get('allComponentsChecked');
+        this.get('users').setEach('checked', checked);
+      }.observes('allComponentsChecked'),
+
+      clearFilter:function() {
+        this.set('allComponentsChecked', false);
+
+        this.get('users').setEach('checked', false);
+
+        this._super();
+      },
+
+      applyFilter:function() {
+        this._super();
+
+        var chosenUsers = this.get('users').filterProperty('checked', true).mapProperty('name');
+        this.set('value', chosenUsers.toString());
       }
-    }
+    }),
+
+    valueBinding: 'controller.filterObject.sSearch_3'
   }),
   /**
-   * Filter-field for jobs
+   * Filter-field for jobs.
+   * Based on <code>filters</code> library
    */
-  jobsFilterView:Em.TextField.extend({
-    classNames:['input-mini'],
-    type:'text',
-    placeholder:'Any ',
-    elementId:'jobs_filter',
-    filtering:function () {
-      if (this.get('value') == '') {
-        this.$().closest('th').addClass('notActive');
-      }
-      else {
-        this.$().closest('th').removeClass('notActive');
-      }
-      //this.get('parentView').get('applyFilter')(this.get('parentView'), 6);
-    }.observes('value')
+  jobsFilterView: filters.createTextView({
+    fieldType: 'input-super-mini',
+    valueBinding: "controller.filterObject.jobs"
   }),
-
   /**
-   * Filter-field for Input
+   * Filter-field for Input.
+   * Based on <code>filters</code> library
    */
-  inputFilterView:Em.TextField.extend({
-    classNames:['input-mini'],
-    type:'text',
-    placeholder:'Any ',
-    elementId:'input_filter',
-    filtering:function () {
-      if (this.get('value') == '') {
-        this.$().closest('th').addClass('notActive');
-      }
-      else {
-        this.$().closest('th').removeClass('notActive');
-      }
-    }.observes('value')
+  inputFilterView: filters.createTextView({
+    fieldType: 'input-super-mini',
+    valueBinding: "controller.filterObject.input"
   }),
   /**
-   * Filter-field for Output
+   * Filter-field for Output.
+   * Based on <code>filters</code> library
    */
-  outputFilterView:Em.TextField.extend({
-    classNames:['input-mini'],
-    type:'text',
-    placeholder:'Any ',
-    elementId:'output_filter',
-    filtering:function () {
-      if (this.get('value') == '') {
-        this.$().closest('th').addClass('notActive');
-      }
-      else {
-        this.$().closest('th').removeClass('notActive');
-      }
-    }.observes('value')
+  outputFilterView: filters.createTextView({
+    fieldType: 'input-super-mini',
+    valueBinding: "controller.filterObject.output"
   }),
   /**
-   * Filter-field for Duration
+   * Filter-field for Duration.
+   * Based on <code>filters</code> library
    */
-  durationFilterView:Em.TextField.extend({
-    classNames:['input-mini'],
-    type:'text',
-    placeholder:'Any ',
-    elementId:'duration_filter',
-    filtering:function () {
-      if (this.get('value') == '') {
-        this.$().closest('th').addClass('notActive');
-      }
-      else {
-        this.$().closest('th').removeClass('notActive');
-      }
-    }.observes('value')
+  durationFilterView: filters.createTextView({
+    fieldType: 'input-super-mini',
+    valueBinding: "controller.filterObject.duration"
   }),
   /**
-   * Filter-field for RunDate
+   * Filter-field for RunDate.
+   * Based on <code>filters</code> library
    */
-  rundateSelectView:Em.Select.extend({
-    content:['Any', 'Past 1 Day', 'Past 2 Days', 'Past 7 Days', 'Past 14 Days', 'Past 30 Days'],
-    selected:'Any',
-    classNames:['input-medium'],
-    elementId:'rundate_filter',
-    change:function (event) {
-      if (this.get('selection') == 'Any') {
-        this.$().closest('th').addClass('notActive');
-      }
-      else {
-        this.$().closest('th').removeClass('notActive');
-      }
-    }
+  runDateFilterView: filters.createSelectView({
+    fieldType: 'input-medium',
+    valueBinding: "controller.filterObject.runDate",
+    content: ['Any', 'Past 1 Day', 'Past 2 Days', 'Past 7 Days', 'Past 14 Days', 'Past 30 Days']
   }),
 
   /**
    * Onclick handler for Show All/Filtered buttons
    */
-  clickViewType:function (event) {
+  clickViewType: function (event) {
     this.set("controller.filterObject.viewTypeClickEvent", true);
-    if($(event.target).hasClass("filtered")){
+    if ($(event.target).hasClass("filtered")) {
       this.set("controller.filterObject.viewType", "filtered");
-    }else{
+    } else {
       this.set("controller.filterObject.allFilterActivated", true);
       this.set("controller.filterObject.viewType", "all");
     }
@@ -341,7 +239,7 @@ App.MainAppsView = Em.View.extend({
   /**
    *
    */
-  onChangeViewType:function () {
+  onChangeViewType: function () {
     var tmpViewType = this.get('controller.filterObject.viewType');
     var filterButtons = $("#filter_buttons").children();
     filterButtons.each(function (index, element) {
@@ -358,9 +256,9 @@ App.MainAppsView = Em.View.extend({
    *
    * @param event
    */
-  clearFilters:function (event) {
+  clearFilters: function (event) {
     this._childViews.forEach(function (item) {
-      if(item.get("viewName") === "runPerPageSelectView"){
+      if (item.get("viewName") === "runPerPageSelectView") {
         return "";
       }
       if (item.get('tagName') === 'input') {
@@ -375,70 +273,52 @@ App.MainAppsView = Em.View.extend({
       }
     });
   },
-  /**
-   * Clear selected filter
-   * @param event
-   */
-  clearFilterButtonClick:function (event) {
-    var viewName = event.target.id.replace('view_', '');
-    var elementId = this.get(viewName).get('elementId');
-    if (this.get(viewName).get('tagName') === 'input') {
-      this.get(viewName).set('value', '');
-    }
-    if (this.get(viewName).get('tagName') === 'select') {
-      this.get(viewName).set('value', 'Any');
-      this.get(viewName).change();
-    }
-    if (this.get(viewName).get('multiple')) {
-      this.get(viewName).get('clearFilter')(this.get(viewName));
-    }
-  },
 
   /**
    * This Container View is used to render static table row(appTableRow) and additional dynamic content
    */
-  containerRow:Em.ContainerView.extend({
+  containerRow: Em.ContainerView.extend({
 
     /**
      * Unique row id
      */
-    id:function () {
+    id: function () {
       return this.get('run.id');
     }.property("run.id"),
 
     /**
      * Show/hide additional content appropriated for this row
      */
-    expandToggle:function () {
+    expandToggle: function () {
       //App.router.get('mainAppsItemController').set('jobsLoaded', false);
       App.router.get('mainAppsItemController').set('content', this.get('run'));
       App.ModalPopup.show({
-        classNames:['big-modal'],
-        header:Em.I18n.t('apps.dagCharts.popup'),
-        bodyClass:App.MainAppsItemView.extend({
-          controllerBinding:'App.router.mainAppsItemController'
+        classNames: ['big-modal'],
+        header: Em.I18n.t('apps.dagCharts.popup'),
+        bodyClass: App.MainAppsItemView.extend({
+          controllerBinding: 'App.router.mainAppsItemController'
         }),
-        onPrimary:function () {
+        onPrimary: function () {
           this.hide();
         },
-        secondary:null
+        secondary: null
       });
     }
   }),
   /**
    * Table-row view
    */
-  appTableRow:Em.View.extend({
-    templateName:require('templates/main/apps/list_row'),
-    classNames:['app-table-row'],
-    tagName:"tr",
-    mouseEnter:function (event, view) {
+  appTableRow: Em.View.extend({
+    templateName: require('templates/main/apps/list_row'),
+    classNames: ['app-table-row'],
+    tagName: "tr",
+    mouseEnter: function (event, view) {
       $(event.currentTarget).addClass("hover")
     },
-    mouseLeave:function (event, view) {
+    mouseLeave: function (event, view) {
       $(event.currentTarget).removeClass("hover");
     },
-    click:function (event, view) {
+    click: function (event, view) {
       this.get('parentView').expandToggle();
     }
 

+ 108 - 0
ambari-web/app/views/main/host.js

@@ -130,18 +130,30 @@ App.MainHostView = Em.View.extend({
 
   }),
 
+  /**
+   * Filter view for name column
+   * Based on <code>filters</code> library
+   */
   nameFilterView: filters.createTextView({
     onChangeValue: function(){
       this.get('parentView').updateFilter(8, this.get('value'));
     }
   }),
 
+  /**
+   * Filter view for ip column
+   * Based on <code>filters</code> library
+   */
   ipFilterView: filters.createTextView({
     onChangeValue: function(){
       this.get('parentView').updateFilter(2, this.get('value'));
     }
   }),
 
+  /**
+   * Filter view for Cpu column
+   * Based on <code>filters</code> library
+   */
   cpuFilterView: filters.createTextView({
     fieldType: 'input-mini',
     fieldId: 'cpu_filter',
@@ -150,6 +162,10 @@ App.MainHostView = Em.View.extend({
     }
   }),
 
+  /**
+   * Filter view for LoadAverage column
+   * Based on <code>filters</code> library
+   */
   loadAvgFilterView: filters.createTextView({
     fieldType: 'input-mini',
     fieldId: 'load_avg_filter',
@@ -158,6 +174,10 @@ App.MainHostView = Em.View.extend({
     }
   }),
 
+  /**
+   * Filter view for Ram column
+   * Based on <code>filters</code> library
+   */
   ramFilterView: filters.createTextView({
     fieldType: 'input-mini',
     fieldId: 'ram_filter',
@@ -166,7 +186,95 @@ App.MainHostView = Em.View.extend({
     }
   }),
 
+  /**
+   * Filter view for HostComponents column
+   * Based on <code>filters</code> library
+   */
   componentsFilterView: filters.createComponentView({
+    /**
+     * Inner FilterView. Used just to render component. Value bind to <code>mainview.value</code> property
+     * Base methods was implemented in <code>filters.componentFieldView</code>
+     */
+    filterView: filters.componentFieldView.extend({
+      templateName: require('templates/main/host/component_filter'),
+
+      /**
+       * Next three lines bind data to this view
+       */
+      masterComponentsBinding: 'controller.masterComponents',
+      slaveComponentsBinding: 'controller.slaveComponents',
+      clientComponentsBinding: 'controller.clientComponents',
+
+      /**
+       * Checkbox for quick selecting/deselecting of master components
+       */
+      masterComponentsChecked:false,
+      toggleMasterComponents:function () {
+        this.get('masterComponents').setEach('checkedForHostFilter', this.get('masterComponentsChecked'));
+      }.observes('masterComponentsChecked'),
+
+      /**
+       * Checkbox for quick selecting/deselecting of slave components
+       */
+      slaveComponentsChecked:false,
+      toggleSlaveComponents:function () {
+        this.get('slaveComponents').setEach('checkedForHostFilter', this.get('slaveComponentsChecked'));
+      }.observes('slaveComponentsChecked'),
+
+      /**
+       * Checkbox for quick selecting/deselecting of client components
+       */
+      clientComponentsChecked: false,
+      toggleClientComponents: function() {
+        this.get('clientComponents').setEach('checkedForHostFilter', this.get('clientComponentsChecked'));
+      }.observes('clientComponentsChecked'),
+
+      /**
+       * Clear filter.
+       * Called by parent view, when user clicks on <code>x</code> button(clear button)
+       */
+      clearFilter:function() {
+        this.set('masterComponentsChecked', false);
+        this.set('slaveComponentsChecked', false);
+        this.set('clientComponentsChecked', false);
+
+        this.get('masterComponents').setEach('checkedForHostFilter', false);
+        this.get('slaveComponents').setEach('checkedForHostFilter', false);
+        this.get('clientComponents').setEach('checkedForHostFilter', false);
+
+        this._super();
+      },
+
+      /**
+       * Onclick handler for <code>Apply filter</code> button
+       */
+      applyFilter:function() {
+        this._super();
+
+        var chosenComponents = [];
+
+        this.get('masterComponents').filterProperty('checkedForHostFilter', true).forEach(function(item){
+          chosenComponents.push(item.get('displayName'));
+        });
+        this.get('slaveComponents').filterProperty('checkedForHostFilter', true).forEach(function(item){
+          chosenComponents.push(item.get('displayName'));
+        });
+        this.get('clientComponents').filterProperty('checkedForHostFilter', true).forEach(function(item){
+          chosenComponents.push(item.get('displayName'));
+        });
+        this.set('value', chosenComponents.toString());
+      },
+
+      didInsertElement:function () {
+        if (this.get('controller.comeWithFilter')) {
+          this.applyFilter();
+          this.set('controller.comeWithFilter', false);
+        } else {
+          this.clearFilter();
+        }
+      }
+
+    }),
     fieldId: 'components_filter',
     onChangeValue: function(){
       this.get('parentView').updateFilter(9);