You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by at...@apache.org on 2015/04/10 14:55:44 UTC

ambari git commit: AMBARI-10433 Create widget wizard -> Metrics & Expression page: Implement preview for the created expression. (atkach)

Repository: ambari
Updated Branches:
  refs/heads/trunk 7a8a7f71f -> 53da928ba


AMBARI-10433 Create widget wizard -> Metrics & Expression page: Implement preview for the created expression. (atkach)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/53da928b
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/53da928b
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/53da928b

Branch: refs/heads/trunk
Commit: 53da928baa8f0e14b8d1545835e27ce4993c0225
Parents: 7a8a7f7
Author: Andrii Tkach <at...@hortonworks.com>
Authored: Fri Apr 10 14:58:21 2015 +0300
Committer: Andrii Tkach <at...@hortonworks.com>
Committed: Fri Apr 10 14:58:21 2015 +0300

----------------------------------------------------------------------
 .../service/widgets/create/step2_controller.js  | 164 ++++++++++++++++++-
 ambari-web/app/messages.js                      |  20 ++-
 ambari-web/app/mixins/common/widget_mixin.js    |  36 ++++
 .../app/styles/enhanced_service_dashboard.less  |  40 ++++-
 ambari-web/app/templates.js                     |   3 +
 .../main/service/widgets/create/expression.hbs  |   3 +
 .../main/service/widgets/create/step2.hbs       |  21 +--
 .../main/service/widgets/create/step2_graph.hbs |  46 ++++++
 .../service/widgets/create/step2_number.hbs     |  29 ++++
 .../service/widgets/create/step2_template.hbs   |  46 ++++++
 .../main/service/widgets/create/wizard.hbs      |   8 +
 .../views/common/widget/gauge_widget_view.js    |   7 -
 .../views/common/widget/graph_widget_view.js    |  17 +-
 .../views/common/widget/number_widget_view.js   |   9 +-
 .../views/common/widget/template_widget_view.js |  11 +-
 .../service/widgets/create/expression_view.js   |   1 +
 .../main/service/widgets/create/step2_view.js   | 100 ++++++++++-
 .../main/service/widgets/create/wizard_view.js  |  47 +++++-
 .../widgets/create/expression_view_test.js      |   5 +-
 19 files changed, 557 insertions(+), 56 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/53da928b/ambari-web/app/controllers/main/service/widgets/create/step2_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service/widgets/create/step2_controller.js b/ambari-web/app/controllers/main/service/widgets/create/step2_controller.js
index d0a28b1..cfa555f 100644
--- a/ambari-web/app/controllers/main/service/widgets/create/step2_controller.js
+++ b/ambari-web/app/controllers/main/service/widgets/create/step2_controller.js
@@ -21,14 +21,50 @@ var App = require('app');
 App.WidgetWizardStep2Controller = Em.Controller.extend({
   name: "widgetWizardStep2Controller",
 
+  /**
+   * @type {Array}
+   */
   widgetProperties: [],
   widgetValues: {},
 
+  expressionData: {
+    values: [],
+    metrics: []
+  },
+  widgetPropertiesData: {},
+
+  propertiesMap: {
+    "warning_threshold": {
+      name: 'threshold',
+      property: 'smallValue'
+    },
+    "error_threshold": {
+      name: 'threshold',
+      property: 'bigValue'
+    },
+    "display_unit": {
+      name: 'display-unit',
+      property: 'value'
+    },
+    "graph_type": {
+      name: 'graph_type',
+      property: 'value'
+    },
+    "time_range": {
+      name: 'time_range',
+      property: 'value'
+    }
+  },
+
   //TODO: Following computed property needs to be implemented. Next button should be enabled when there is no validation error and all required fields are filled
   isSubmitDisabled: function () {
     return this.get('widgetProperties').someProperty('isValid', false);
   }.property('widgetProperties.@each.isValid'),
 
+  /**
+   * metrics filtered by type
+   * @type {Array}
+   */
   filteredMetrics: function () {
     var type = this.get('content.widgetType');
     return this.get('content.widgetMetrics').filter(function (metric) {
@@ -40,14 +76,140 @@ App.WidgetWizardStep2Controller = Em.Controller.extend({
     }, this);
   }.property('content.widgetMetrics'),
 
+  /**
+   * update preview widget with latest expression data
+   * @param {Em.View} view
+   */
+  updateExpressions: function (view) {
+    var widgetType = this.get('content.widgetType');
+    var expressionData = {
+      values: [],
+      metrics: []
+    };
+    if (view.get('expressions').length > 0 && view.get('dataSets').length > 0) {
+      switch (widgetType) {
+        case 'GAUGE':
+        case 'NUMBER':
+          expressionData = this.parseExpression(view.get('expressions')[0]);
+          expressionData.values = [
+            {
+              value: expressionData.value
+            }
+          ];
+          break;
+        case 'TEMPLATE':
+          expressionData = this.parseTemplateExpression(view);
+          break;
+        case 'GRAPH':
+          expressionData = this.parseGraphDataset(view);
+          break;
+      }
+    }
+    this.set('expressionData', expressionData);
+  },
+
+  /**
+   * parse Graph data set
+   * @param {Ember.View} view
+   * @returns {{metrics: Array, values: Array}}
+   */
+  parseGraphDataset: function (view) {
+    var metrics = [];
+    var values = [];
+
+    view.get('dataSets').forEach(function (dataSet) {
+      var result = this.parseExpression(dataSet.get('expression'));
+      metrics.pushObjects(result.metrics);
+      values.push({
+        name: dataSet.get('label'),
+        value: result.value
+      });
+    }, this);
+
+    return {
+      metrics: metrics,
+      values: values
+    };
+  },
+
+  /**
+   * parse expression from template
+   * @param {Ember.View} view
+   * @returns {{metrics: Array, values: {value: *}[]}}
+   */
+  parseTemplateExpression: function (view) {
+    var metrics = [];
+    var self = this;
+    var expression = view.get('templateValue').replace(/\{\{Expression[\d]\}\}/g, function (exp) {
+      var result;
+      if (view.get('expressions').someProperty('alias', exp)) {
+        result = self.parseExpression(view.get('expressions').findProperty('alias', exp));
+        metrics.pushObjects(result.metrics);
+        return result.value;
+      }
+      return exp;
+    });
+    return {
+      metrics: metrics,
+      values: [
+        {
+          value: expression
+        }
+      ]
+    };
+  },
+
+  /**
+   *
+   * @param {object} expression
+   * @returns {{metrics: Array, value: string}}
+   */
+  parseExpression: function (expression) {
+    var value = '';
+    var metrics = [];
+
+    if (expression.data.length > 0) {
+      value = '${';
+      expression.data.forEach(function (element) {
+        if (element.isMetric) {
+          metrics.push(element.name);
+        }
+        value += element.name;
+      }, this);
+      value += '}';
+    }
+
+    return {
+      metrics: metrics,
+      value: value
+    };
+  },
+
+  /**
+   * update properties of preview widget
+   */
+  updateProperties: function () {
+    var propertiesMap = this.get('propertiesMap');
+    var result = {};
+    var widgetProperty;
+
+    for (var i in propertiesMap) {
+      widgetProperty = this.get('widgetProperties').findProperty('name', propertiesMap[i].name);
+      if (widgetProperty && widgetProperty.get('isValid')) {
+        result[i] = widgetProperty.get(propertiesMap[i].property);
+      }
+    }
+    this.set('widgetPropertiesData', result);
+  }.observes('widgetProperties.@each.value', 'widgetProperties.@each.bigValue', 'widgetProperties.@each.smallValue'),
+
   /*
    * Generate the thresholds, unit, time range.etc object based on the widget type selected in previous step.
    */
   renderProperties: function () {
     var widgetType = this.get('content.widgetType');
-    this.set("widgetProperties", {});
     var widgetProperties = App.WidgetType.find().findProperty('name', widgetType).get('properties');
     var properties = [];
+
     switch (widgetType) {
       case 'GAUGE':
         properties = this.renderGaugeProperties(widgetProperties);

http://git-wip-us.apache.org/repos/asf/ambari/blob/53da928b/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index 5233087..cf0f61e 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -258,6 +258,10 @@ Em.I18n.translations = {
   'common.seconds': "Seconds",
   'common.milliseconds': "Milliseconds",
   'common.configGroup': 'Config Group',
+  'common.expression': 'Expression',
+  'common.dataSet': 'Data Set',
+  'common.label': 'Label',
+  'common.preview': 'Preview',
 
   'models.alert_instance.tiggered.verbose': "Occured on {0} <br> Checked on {1}",
   'models.alert_definition.triggered.verbose': "Occured on {0}",
@@ -2251,12 +2255,6 @@ Em.I18n.translations = {
   'dashboard.widgets.create': 'Create New Widget',
   'dashboard.widgets.layout.import': 'Import a layout',
   'dashboard.widgets.layout.save': 'Save a layout',
-  'dashboard.widgets.wizard.step2.addMetrics': 'Add Metrics and operators here...',
-  'dashboard.widgets.wizard.step2.newMetric': '+ New Metric',
-  'dashboard.widgets.wizard.step2.newOperator': '+ New Operator',
-  'dashboard.widgets.wizard.step2.selectComponent': 'Select a Component',
-  'dashboard.widgets.wizard.step2.selectMetric': 'Select a Metric',
-  'dashboard.widgets.wizard.step2.addMetric': 'Add Metric',
 
   'dashboard.widgets.NameNodeHeap': 'NameNode Heap',
   'dashboard.widgets.NameNodeCpu': 'NameNode CPU WIO',
@@ -2488,8 +2486,18 @@ Em.I18n.translations = {
   'widget.create.wizard.step1.body.text': 'What type of widget do you want to create?',
   'widget.create.wizard.step2.header': 'Metrics and Expression',
   'widget.create.wizard.step2.body.text':'Define the expression with any metrics and valid operators. </br>Use parentheses when necessary.',
+  'widget.create.wizard.step2.body.template':'Define the template first, a template can have any number of {{expression}} and any string.',
   'widget.create.wizard.step2.body.warning':'Note: Valid operators are +, -, *, /',
   'widget.create.wizard.step3.header': 'Name and Description',
+  'widget.create.wizard.step2.addExpression': 'Add Expression',
+  'widget.create.wizard.step2.addDataset': 'Add data set',
+
+  'dashboard.widgets.wizard.step2.addMetrics': 'Add Metrics and operators here...',
+  'dashboard.widgets.wizard.step2.newMetric': '+ New Metric',
+  'dashboard.widgets.wizard.step2.newOperator': '+ New Operator',
+  'dashboard.widgets.wizard.step2.selectComponent': 'Select a Component',
+  'dashboard.widgets.wizard.step2.selectMetric': 'Select a Metric',
+  'dashboard.widgets.wizard.step2.addMetric': 'Add Metric',
 
   'restart.service.all': 'Restart All',
   'restart.service.all.affected': 'Restart All Affected',

http://git-wip-us.apache.org/repos/asf/ambari/blob/53da928b/ambari-web/app/mixins/common/widget_mixin.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mixins/common/widget_mixin.js b/ambari-web/app/mixins/common/widget_mixin.js
index 5ed262c..0fe7958 100644
--- a/ambari-web/app/mixins/common/widget_mixin.js
+++ b/ambari-web/app/mixins/common/widget_mixin.js
@@ -60,6 +60,16 @@ App.WidgetMixin = Ember.Mixin.create({
   },
 
   /**
+   * draw widget
+   */
+  drawWidget: function () {
+    if (this.get('isLoaded')) {
+      this.calculateValues();
+      this.set('value', this.get('content.values')[0] && this.get('content.values')[0].computedValue);
+    }
+  },
+
+  /**
    * callback on metrics loaded
    */
   onMetricsLoaded: function () {
@@ -240,4 +250,30 @@ App.WidgetMixin = Ember.Mixin.create({
     //TODO push data to metrics after response structure approved
   }
 
+});
+
+App.WidgetPreviewMixin = Ember.Mixin.create({
+  beforeRender: Em.K,
+  isLoaded: true,
+  metrics: [],
+  content: Em.Object.create({
+    widgetName: 'mock-widget',
+    values: []
+  }),
+  drawWidget: function () {
+    this.loadMetrics();
+    this.set('content.values', this.get('controller.expressionData.values'));
+    this.set('content.properties', this.get('controller.widgetPropertiesData'));
+    this._super();
+  }.observes('controller.widgetPropertiesData', 'controller.expressionData'),
+  loadMetrics: function () {
+    var metrics = [];
+    this.get('controller.expressionData.metrics').forEach(function (metric) {
+      metrics.push({
+        name: metric,
+        data: this.get('MOCK_VALUE')
+      });
+    }, this);
+    this.set('metrics', metrics);
+  }
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/53da928b/ambari-web/app/styles/enhanced_service_dashboard.less
----------------------------------------------------------------------
diff --git a/ambari-web/app/styles/enhanced_service_dashboard.less b/ambari-web/app/styles/enhanced_service_dashboard.less
index b9a54bf..0578ee7 100644
--- a/ambari-web/app/styles/enhanced_service_dashboard.less
+++ b/ambari-web/app/styles/enhanced_service_dashboard.less
@@ -48,8 +48,10 @@
   }
 }
 
-#widget_layout {
+#widget_layout,
+#widget-preview {
   .frame {
+    overflow: hidden;
     height: 150px;
     width: 90%;
   }
@@ -111,6 +113,15 @@
   }
 }
 
+#widget-preview {
+  max-width: 200px;
+  .graph-widget {
+    .title {
+      height: 0;
+    }
+  }
+}
+
 #add-widget-wizard {
   .step2 {
     .badge-container {
@@ -132,6 +143,14 @@
         background-color: @health-status-red;
       }
     }
+    .remove-link {
+      padding: 5px;
+      cursor: pointer;
+      color: #555555;
+      right: 10px;
+      bottom: 10px;
+      position: absolute;
+    }
     .icon-asterisk {
       color: red;
       font-size: 8px;
@@ -142,7 +161,7 @@
     }
     .metric-container {
       position: relative;
-      margin-bottom: 70px;
+      margin-bottom: 50px;
       height: 200px;
       border: 1px solid @border-color;
       button,
@@ -189,6 +208,23 @@
         border-color: red;
       }
     }
+    .template {
+      margin-bottom: 10px;
+      textarea {
+        width: ~"calc(100% - 10px)";
+      }
+    }
+    fieldset {
+      position: relative;
+      border: 2px groove #ddd;
+      padding: 0 1.4em 1.4em 1.4em;
+      margin: 0 0 1.5em 0;
+    }
+    legend {
+      width: inherit;
+      padding: 0 10px;
+      border-bottom: none;
+    }
   }
 }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/53da928b/ambari-web/app/templates.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates.js b/ambari-web/app/templates.js
index 0157613..af5a2c0 100644
--- a/ambari-web/app/templates.js
+++ b/ambari-web/app/templates.js
@@ -22,4 +22,7 @@
 
 require('templates/main/service/info/summary/base');
 require('templates/common/progress');
+require("templates/main/service/widgets/create/step2_number");
+require("templates/main/service/widgets/create/step2_template");
+require("templates/main/service/widgets/create/step2_graph");
 require('templates/common/configs/widgets/controls');

http://git-wip-us.apache.org/repos/asf/ambari/blob/53da928b/ambari-web/app/templates/main/service/widgets/create/expression.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/service/widgets/create/expression.hbs b/ambari-web/app/templates/main/service/widgets/create/expression.hbs
index 55a9537..e57942c 100644
--- a/ambari-web/app/templates/main/service/widgets/create/expression.hbs
+++ b/ambari-web/app/templates/main/service/widgets/create/expression.hbs
@@ -44,6 +44,9 @@
   </div>
 {{else}}
   <a {{action startEdit target="view"}} class="edit-link"><i class="icon-edit"></i></a>
+  {{#if view.expression.isRemovable}}
+    <a {{action removeExpression view.expression target="view.parentView"}} class="remove-link"><i class="icon-trash"></i></a>
+  {{/if}}
   {{#if view.expression.data.length}}
     <div class="metric-field">
       {{#each element in view.expression.data}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/53da928b/ambari-web/app/templates/main/service/widgets/create/step2.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/service/widgets/create/step2.hbs b/ambari-web/app/templates/main/service/widgets/create/step2.hbs
index 5b39032..f8f3848 100644
--- a/ambari-web/app/templates/main/service/widgets/create/step2.hbs
+++ b/ambari-web/app/templates/main/service/widgets/create/step2.hbs
@@ -17,17 +17,18 @@
 }}
 
 <div class="step2">
-  <h2>{{t widget.create.wizard.step2.header}}</h2>
-  <div class="alert alert-info">
-    {{t widget.create.wizard.step2.body.text}}
-    <div class="alert alert-warning">
-      {{t widget.create.wizard.step2.body.warning}}
-    </div>
-  </div>
 
-  {{#each expression in view.expressions}}
-    {{view App.WidgetWizardExpressionView expressionBinding="expression"}}
-  {{/each}}
+  {{#if view.templateType.isNumber}}
+    {{template "templates/main/service/widgets/create/step2_number"}}
+  {{/if}}
+
+  {{#if view.templateType.isGraph}}
+    {{template "templates/main/service/widgets/create/step2_graph"}}
+  {{/if}}
+
+  {{#if view.templateType.isTemplate}}
+    {{template "templates/main/service/widgets/create/step2_template"}}
+  {{/if}}
 
   <div>
     <form class="form-horizontal">

http://git-wip-us.apache.org/repos/asf/ambari/blob/53da928b/ambari-web/app/templates/main/service/widgets/create/step2_graph.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/service/widgets/create/step2_graph.hbs b/ambari-web/app/templates/main/service/widgets/create/step2_graph.hbs
new file mode 100644
index 0000000..b27c451
--- /dev/null
+++ b/ambari-web/app/templates/main/service/widgets/create/step2_graph.hbs
@@ -0,0 +1,46 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+<h2>{{t widget.create.wizard.step2.header}}</h2>
+<div class="alert alert-info">
+  {{t widget.create.wizard.step2.body.text}}
+  <div class="alert alert-warning">
+    {{t widget.create.wizard.step2.body.warning}}
+  </div>
+</div>
+
+{{#each dataSet in view.dataSets}}
+  <fieldset>
+    <legend>{{t common.dataSet}}&nbsp;{{dataSet.id}}</legend>
+    <h5>
+      {{t common.label}}:&nbsp;{{view Ember.TextField valueBinding="dataSet.label"}}
+    </h5>
+    <h5>{{t common.expression}}:</h5>
+    {{view App.WidgetWizardExpressionView expressionBinding="dataSet.expression"}}
+    {{#if dataSet.isRemovable}}
+      <a {{action removeDataSet dataSet target="view"}} class="remove-link"><i class="icon-trash"></i></a>
+    {{/if}}
+  </fieldset>
+{{/each}}
+
+<div class="align-right">
+  <a href="#" {{action addDataSet target="view"}}>
+    <i class="icon-plus"></i>
+    {{t widget.create.wizard.step2.addDataset}}
+  </a>
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/53da928b/ambari-web/app/templates/main/service/widgets/create/step2_number.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/service/widgets/create/step2_number.hbs b/ambari-web/app/templates/main/service/widgets/create/step2_number.hbs
new file mode 100644
index 0000000..ec44798
--- /dev/null
+++ b/ambari-web/app/templates/main/service/widgets/create/step2_number.hbs
@@ -0,0 +1,29 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+<h2>{{t widget.create.wizard.step2.header}}</h2>
+<div class="alert alert-info">
+  {{t widget.create.wizard.step2.body.text}}
+  <div class="alert alert-warning">
+    {{t widget.create.wizard.step2.body.warning}}
+  </div>
+</div>
+
+{{#each expression in view.expressions}}
+  {{view App.WidgetWizardExpressionView expressionBinding="expression"}}
+{{/each}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/53da928b/ambari-web/app/templates/main/service/widgets/create/step2_template.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/service/widgets/create/step2_template.hbs b/ambari-web/app/templates/main/service/widgets/create/step2_template.hbs
new file mode 100644
index 0000000..6d82e9b
--- /dev/null
+++ b/ambari-web/app/templates/main/service/widgets/create/step2_template.hbs
@@ -0,0 +1,46 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+<h2>{{t widget.create.wizard.step2.header}}</h2>
+
+<div class="alert alert-info">
+  {{t widget.create.wizard.step2.body.template}}
+</div>
+
+<div class="template">
+  {{view Ember.TextArea valueBinding="view.templateValue"}}
+</div>
+
+<div class="alert alert-info">
+  {{t widget.create.wizard.step2.body.text}}
+  <div class="alert alert-warning">
+    {{t widget.create.wizard.step2.body.warning}}
+  </div>
+</div>
+
+{{#each expression in view.expressions}}
+  <h5>{{view.EXPRESSION_PREFIX}}{{expression.id}}</h5>
+  {{view App.WidgetWizardExpressionView expressionBinding="expression"}}
+{{/each}}
+
+<div class="align-right">
+  <a href="#" {{action addExpression target="view"}}>
+    <i class="icon-plus"></i>
+    {{t widget.create.wizard.step2.addExpression}}
+  </a>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/53da928b/ambari-web/app/templates/main/service/widgets/create/wizard.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/service/widgets/create/wizard.hbs b/ambari-web/app/templates/main/service/widgets/create/wizard.hbs
index 883d153..6d17b7f 100644
--- a/ambari-web/app/templates/main/service/widgets/create/wizard.hbs
+++ b/ambari-web/app/templates/main/service/widgets/create/wizard.hbs
@@ -30,6 +30,14 @@
               <li {{bindAttr class="isStep3:active view.isStep3Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep3 target="controller"}}>{{t widget.create.wizard.step3.header}}</a></li>
             </ul>
           </div>
+          {{#if view.showPreview}}
+            <div class="preview" id="widget-preview">
+              <h5>{{t common.preview}}:</h5>
+              <div class="widget">
+                {{view view.previewWidgetClass controllerBinding="App.router.widgetWizardStep2Controller"}}
+              </div>
+            </div>
+          {{/if}}
         </div>
         <div class="wizard-content well span9">
           {{outlet}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/53da928b/ambari-web/app/views/common/widget/gauge_widget_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/widget/gauge_widget_view.js b/ambari-web/app/views/common/widget/gauge_widget_view.js
index 46d6966..25ec38e 100644
--- a/ambari-web/app/views/common/widget/gauge_widget_view.js
+++ b/ambari-web/app/views/common/widget/gauge_widget_view.js
@@ -32,13 +32,6 @@ App.GaugeWidgetView = Em.View.extend(App.WidgetMixin, {
    */
   metrics: [],
 
-  drawWidget: function () {
-    if (this.get('isLoaded')) {
-      this.calculateValues();
-      this.set('value', this.get('content.values')[0].computedValue);
-    }
-  },
-
   chartView: App.ChartPieView.extend({
     stroke: '#D6DDDF', //light grey
     innerR: 25,

http://git-wip-us.apache.org/repos/asf/ambari/blob/53da928b/ambari-web/app/views/common/widget/graph_widget_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/widget/graph_widget_view.js b/ambari-web/app/views/common/widget/graph_widget_view.js
index b440984..e886b9b 100644
--- a/ambari-web/app/views/common/widget/graph_widget_view.js
+++ b/ambari-web/app/views/common/widget/graph_widget_view.js
@@ -58,11 +58,16 @@ App.GraphWidgetView = Em.View.extend(App.WidgetMixin, {
     var seriesData = [];
 
     this.get('content.values').forEach(function (value) {
-      var computedExpressions = this.computeExpression(this.extractExpressions(value)[0], metrics);
-      seriesData.push({
-        name: value.name,
-        data: computedExpressions[value.value.match(this.get('EXPRESSION_REGEX'))[0]]
-      });
+      var expression =  this.extractExpressions(value)[0];
+      var computedExpressions;
+
+      if (expression) {
+        computedExpressions = this.computeExpression(expression, metrics);
+        seriesData.push({
+          name: value.name,
+          data: computedExpressions[value.value.match(this.get('EXPRESSION_REGEX'))[0]]
+        });
+      }
     }, this);
     return seriesData;
   },
@@ -201,4 +206,4 @@ App.GraphWidgetView = Em.View.extend(App.WidgetMixin, {
       this._refreshGraph(this.get('parentView.data'));
     }.observes('parentView.data')
   })
-});
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/53da928b/ambari-web/app/views/common/widget/number_widget_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/widget/number_widget_view.js b/ambari-web/app/views/common/widget/number_widget_view.js
index c0a85cb..2bab561 100644
--- a/ambari-web/app/views/common/widget/number_widget_view.js
+++ b/ambari-web/app/views/common/widget/number_widget_view.js
@@ -48,12 +48,5 @@ App.NumberWidgetView = Em.View.extend(App.WidgetMixin, {
     } else {
       return 'red';
     }
-  }.property('value', 'content.properties.warning_threshold', 'content.properties.error_threshold'),
-
-  drawWidget: function () {
-    if (this.get('isLoaded')) {
-      this.calculateValues();
-      this.set('value', this.get('content.values')[0].computedValue);
-    }
-  }
+  }.property('value', 'content.properties.warning_threshold', 'content.properties.error_threshold')
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/53da928b/ambari-web/app/views/common/widget/template_widget_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/widget/template_widget_view.js b/ambari-web/app/views/common/widget/template_widget_view.js
index 62d043c..b7b54a6 100644
--- a/ambari-web/app/views/common/widget/template_widget_view.js
+++ b/ambari-web/app/views/common/widget/template_widget_view.js
@@ -30,12 +30,5 @@ App.TemplateWidgetView = Em.View.extend(App.WidgetMixin, {
    * common metrics container
    * @type {Array}
    */
-  metrics: [],
-
-  drawWidget: function () {
-    if (this.get('isLoaded')) {
-      this.calculateValues();
-      this.set('value', this.get('content.values')[0].computedValue);
-    }
-  }
-});
\ No newline at end of file
+  metrics: []
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/53da928b/ambari-web/app/views/main/service/widgets/create/expression_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/service/widgets/create/expression_view.js b/ambari-web/app/views/main/service/widgets/create/expression_view.js
index 9bc8427..ba4c47e 100644
--- a/ambari-web/app/views/main/service/widgets/create/expression_view.js
+++ b/ambari-web/app/views/main/service/widgets/create/expression_view.js
@@ -150,6 +150,7 @@ App.WidgetWizardExpressionView = Em.View.extend({
     }
 
     this.set('isInvalid', isInvalid);
+    if (!isInvalid) this.get('parentView').updatePreview();
   }.observes('expression.data.length'),
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/53da928b/ambari-web/app/views/main/service/widgets/create/step2_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/service/widgets/create/step2_view.js b/ambari-web/app/views/main/service/widgets/create/step2_view.js
index 0cf04a3..d6711c2 100644
--- a/ambari-web/app/views/main/service/widgets/create/step2_view.js
+++ b/ambari-web/app/views/main/service/widgets/create/step2_view.js
@@ -20,18 +20,108 @@ App.WidgetWizardStep2View = Em.View.extend({
 
   templateName: require('templates/main/service/widgets/create/step2'),
 
+  EXPRESSION_PREFIX: 'Expression',
+
+  /**
+   * content of template of Template widget
+   * @type {string}
+   */
+  templateValue: '',
+
+  /**
+   * calculate template by widget type
+   */
+  templateType: function () {
+    switch (this.get('controller.content.widgetType')) {
+      case 'GAUGE':
+      case 'NUMBER':
+        return {
+          isNumber: true
+        };
+      case 'TEMPLATE':
+        return {
+          isTemplate: true
+        };
+      case 'GRAPH':
+        return {
+          isGraph: true
+        }
+    }
+  }.property('controller.content.widgetType'),
+
   /**
    * @type {Array}
    */
-  expressions: [
-    Em.Object.create({
-      data: []
-    })
-  ],
+  expressions: [],
+
+  /**
+   * used only for GRAPH widget
+   * @type {Array}
+   */
+  dataSets: [],
+
+  /**
+   * Add data set
+   * @param {object|null} event
+   * @param {boolean} isDefault
+   */
+  addDataSet: function(event, isDefault) {
+    var id = (isDefault) ? 1 :(Math.max.apply(this, this.get('dataSets').mapProperty('id')) + 1);
+
+    this.get('dataSets').pushObject(Em.Object.create({
+      id: id,
+      label: '',
+      isRemovable: !isDefault,
+      expression: Em.Object.create({
+        data: []
+      })
+    }));
+  },
+
+  /**
+   * Remove data set
+   * @param {object} event
+   */
+  removeDataSet: function(event) {
+    this.get('dataSets').removeObject(event.context);
+  },
+
+  /**
+   * Add expression
+   * @param {object|null} event
+   * @param {boolean} isDefault
+   */
+  addExpression: function(event, isDefault) {
+    var id = (isDefault) ? 1 :(Math.max.apply(this, this.get('expressions').mapProperty('id')) + 1);
+
+    this.get('expressions').pushObject(Em.Object.create({
+      id: id,
+      isRemovable: !isDefault,
+      data: [],
+      alias: '{{' + this.get('EXPRESSION_PREFIX') + id + '}}'
+    }));
+  },
+
+  /**
+   * Remove expression
+   * @param {object} event
+   */
+  removeExpression: function(event) {
+    this.get('expressions').removeObject(event.context);
+  },
+
+  updatePreview: function() {
+    this.get('controller').updateExpressions(this);
+  }.observes('templateValue', 'dataSets.@each.label'),
 
   didInsertElement: function () {
     var controller = this.get('controller');
     controller.renderProperties();
+    this.get('expressions').clear();
+    this.get('dataSets').clear();
+    this.addExpression(null, true);
+    this.addDataSet(null, true);
+    controller.updateExpressions(this);
   }
 });
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/53da928b/ambari-web/app/views/main/service/widgets/create/wizard_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/service/widgets/create/wizard_view.js b/ambari-web/app/views/main/service/widgets/create/wizard_view.js
index 327c573..4283e82 100644
--- a/ambari-web/app/views/main/service/widgets/create/wizard_view.js
+++ b/ambari-web/app/views/main/service/widgets/create/wizard_view.js
@@ -20,5 +20,50 @@ var App = require('app');
 
 App.WidgetWizardView = Em.View.extend(App.WizardMenuMixin, {
 
-  templateName: require('templates/main/service/widgets/create/wizard')
+  templateName: require('templates/main/service/widgets/create/wizard'),
+
+  /**
+   * @type {App.Widget}
+   */
+  previewWidgetClass: function () {
+    switch (this.get('controller.content.widgetType')) {
+      case 'GRAPH':
+        return App.GraphWidgetView.extend(App.WidgetPreviewMixin, {
+          MOCK_VALUE: function () {
+            var nowTime = App.dateTime();
+            var mock = [];
+
+            for (var i = 0; i < 240; i++) {
+              mock.push([
+                1,
+                (nowTime + (15 * i))
+              ])
+            }
+            return mock;
+          }.property()
+        });
+      case 'TEMPLATE':
+        return App.TemplateWidgetView.extend(App.WidgetPreviewMixin, {
+          MOCK_VALUE: 50
+        });
+      case 'NUMBER':
+        return App.NumberWidgetView.extend(App.WidgetPreviewMixin, {
+          MOCK_VALUE: 50
+        });
+      case 'GAUGE':
+        return App.GaugeWidgetView.extend(App.WidgetPreviewMixin, {
+          MOCK_VALUE: 0.5
+        });
+      default:
+        return Em.View;
+    }
+  }.property('controller.content.widgetType'),
+
+  /**
+   * Widget preview should be shown on 2nd step of wizard
+   * @type {boolean}
+   */
+  showPreview: function () {
+    return this.get('controller.currentStep') == "2";
+  }.property('controller.currentStep')
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/53da928b/ambari-web/test/views/main/service/widgets/create/expression_view_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/service/widgets/create/expression_view_test.js b/ambari-web/test/views/main/service/widgets/create/expression_view_test.js
index 768bac4..2dd0f73 100644
--- a/ambari-web/test/views/main/service/widgets/create/expression_view_test.js
+++ b/ambari-web/test/views/main/service/widgets/create/expression_view_test.js
@@ -23,7 +23,10 @@ describe('App.WidgetWizardExpressionView', function () {
   var view = App.WidgetWizardExpressionView.create({
     expression: {
       data: []
-    }
+    },
+    parentView: Em.Object.create({
+      updatePreview: Em.K
+    })
   });
 
   describe("#validate()", function() {