Skip to Content

This is part of a tutorial series on creating extension components for Design Studio.

Now that we have a handle on how properties are kept synchronized between the client and server, we can go about making it happen.  We’re not yet making use of the the additional properties sheet (APS), we don’t have any machinisms yet for altering the properties at runtime and changes made in the canvas can’t be posted back to the sever copy, property synchronization only goes in one direction; from server copy to canvas.  Therefore, all of our changes will be happening in the component.js file of our gauge component.

We’re going to do four things:

  • We need to initialize all properties on the client side.  Recall that only changes from the default state are sent from the server to the client, so we’ll need to perform a separate initialization on the client side.
  • We’ll adapt the configurable javascript code that we created in Part 3a to use our properties.
  • Whenever the server sends an updated property value, we’ll need to update the property on the client side.
  • Whenever a property changes on the client side, we’re going to need to redraw the gauge to reflect the property updates.   The best way to do this is to encapsulate the redrawing of the gauge, so that it can be done in initialization, or whenever a property value changes.

Property Initialization

This is the easy part.  For every property, we’re going to need a counterpart in the component subclass, within the component.js file.  We’re going to use the naming convention _<propertyName> for these properties, so that if we have a property, called foo, we’d have a variable in the component subclass, called _foo.

In the following code, we do two things.  We declare a variable, called me, which is a pointer to this.  This is an alternative to the variable named that, which we’ve been using up to now.  We’re also going to declare all property variables.

var me = this;

//Properties

me._colorCode = ‘blue’;

me._innerRad = 0.0;

me._outerRad = 0.0;

me._endAngleDeg = 90.0;

me._startAngleDeg = -90.0;

me._paddingTop = 0.0;

me._paddingBottom = 0.0;

me._paddingLeft = 0.0;

me._paddingRight = 0.0;

me._offsetLeft = 0.0;

me._offsetDown = 0.0;

Important!  Make sure that you initialize them to the same values as are in the properties sheet!  Otherwise, you’ll have discordant property values on the client and server.  Properties with no defaultValue defined in component.xml default to 0.

Repackaged Redraws

This is rather simple.  Let’s start with the last time we worked on the component.js, in the component.  First, we’re going to create a new function, called redraw().  We can cut and paste everything from init().  Then we can simply tell init() to call redraw().

me.redraw = function() {

    //Draw the gauge

};

me.init = function() {

    me.redraw();

};

The new redraw() function

Now let’s update redraw to incorporate the configurable gauge code from Part 3a.  We’re going to do a couple of refactors on that code, while doing this.

  • All of the properties that are actually called me._<propertyName> need to be renamed.
  • Remove the check on inner radius size.  We’ll move that check to the setter function.
  • Redraw means that we remove any existing copies of the gauge and draw a new copy.  We’ll use D3’s remove() method to do this.  So the following will remove any existing svg objects from the component and add a new one for the redrawn gauge.

var myDiv = me.$()[0];

// Clear any existing gauges.  We’ll redraw from scratch

d3.select(myDiv).selectAll(“*”).remove();

var vis = d3.select(myDiv).append(“svg:svg”).attr(“width”, “100%”).attr(“height”, “100%”);

Our redraw() function now looks like this:

me.redraw = function() {

  var myDiv = me.$()[0];

  // Clear any existing gauges.  We’ll redraw from scratch

  d3.select(myDiv).selectAll(“*”).remove();

  var vis = d3.select(myDiv).append(“svg:svg”).attr(“width”, “100%”).attr(“height”, “100%”);

  var pi = Math.PI;

  // Find the larger left/right padding

  var lrPadding = me._paddingLeft + me._paddingRight;

  var tbPadding = me._paddingTop + me._paddingBottom;

  var maxPadding = lrPadding;

  if (maxPadding < tbPadding){

  maxPadding = tbPadding

  }

  me._outerRad = (me.$().width() – 2*(maxPadding))/2;

  //The offset will determine where the center of the arc shall be

  me._offsetLeft = me._outerRad + me._paddingLeft;

  me._offsetDown = me._outerRad + me._paddingTop;

  var arcDef = d3.svg.arc()

  .innerRadius(me._innerRad)

  .outerRadius(me._outerRad)

  .startAngle(me._startAngleDeg * (pi/180)) //converting from degs to radians

  .endAngle(me._endAngleDeg * (pi/180)); //converting from degs to radians

  var guageArc = vis.append(“path”)

     .style(“fill”, me._colorCode)

     .attr(“width”, me.$().width()).attr(“height”, me.$().height()) // Added height and width so arc is visible

     .attr(“transform”, “translate(” + me._offsetLeft + “,” + me._offsetDown + “)”)

     .attr(“d”, arcDef);

};

Being Defensive

When setting the inner radius, we’re going to want to sanity check the value and warn the designer if it is nonsensical.  So we’ll add function for checking this; which we can call from the setters that need it.  If the new value of inner radius is greater than the calculated outer radius, then post an alert.  We won’t be able to modify the property sheet value from component.js, so instruct the designer to change the last value with another alert.

//Validate the Inner and Outer Radii

me.validateRadii = function(inner, outer) {

     if (inner <= outer) {

          return true;

     } else {

          alert(“Warning!  The gauge arc can’t have a negative radius!  Please decrease the inner radius, or increase the size of the control.  Height & width (including subtraction for padding) must me at least twice as large as Internal Radius!”);

          return false;

     }

};

Whenever the designer attempts to change one of the bounding box properties (height, width, padding), we also need to recalculate the new outer radius and perform a sanity check.  If the newly calculated radius is valid, then we can apply it.  If not, then we’ll have to reject the change.

//Recalculate Outer Radius.  Also, double check that the new value fits with me._innerRad

me.recalculateOuterRadius = function(paddingLeft, paddingRight, paddingTop, paddingBottom){

  // Find the larger left/right padding

  var lrPadding = paddingLeft + paddingRight;

  var tbPadding = paddingTop + paddingBottom;

  var maxPadding = lrPadding;

  if (maxPadding < tbPadding){

  maxPadding = tbPadding

  }

  var newOuterRad = (me.$().width() – 2*(maxPadding))/2;

  var isValid = me.validateRadii(me._innerRad, newOuterRad);

  if (isValid === true){

  me._outerRad = newOuterRad;

  return true;

  }

  else {

  return false;

  }

}

Property Updates

Whenever a property change is initiated on the server side, the corresponding setter is fired on the client side.  The Design Studio SDK framework uses a standard pattern for property getters and setters.  There should be a single function called <property>(), with a single parameter, value.  It returns either the value of the property, or the parent object (this).


  • If the getter/setter is called without a value, it is being used as a getter and should return the value of the property.  E.g. this returns the value of foo.

var propertyValue = this.foo();

  • If the getter/setter is called with a value, it is being used as a setter.  It should update the property value and return the parent object.  E.g. this changes the value of foo.

this.foo(updatedValue);

If the property is relevant to the inner or outer radius of the gauge, we need to validate the change before actually setting it.  For these five properties, innerRadius, paddingLeft, paddingRight, paddingTop and paddingBottom (hieght and width are also relvant, but are standard properties and we can’t control them), we need to perform a sanity check before actually setting the value.  If it is valid, we set the value, as on the other setters.  If it is invalid, we won’t change the value and we’ll need to alert the designer to this fact, so that they can modify the value.

If the setter is being called (and sanity checks are valid), be sure to call me.redraw(), before returning the object!

//Getters and Setters

me.colorCode = function(value) {

  if (value === undefined) {

  return me._colorCode;

  } else {

  me._colorCode = value;

  me.redraw();

  return me;

  }

};

me.innerRad = function(value) {

  if (value === undefined) {

  return me._innerRad;

  } else {

  var isValid = me.validateRadii(value, me._outerRad);

  if (isValid === false){

  alert(“Warning!  The gauge arc can’t have a small inner radius than outer!  Inner Radius must be equal to or less than ” + me._outerRad);

  alert(“Please decrease the inner radius, or increase the size of the control.  Height & width (including subtraction for padding) must me at least twice as large as Internal Radius!”);

  } else {

  me._innerRad = value;

  me.redraw();

  }

  return this;

  }

};

me.endAngleDeg = function(value) {

  if (value === undefined) {

  return me._endAngleDeg;

  } else {

  me._endAngleDeg = value;

  me.redraw();

  return this;

  }

};

me.startAngleDeg = function(value) {

  if (value === undefined) {

  return me._startAngleDeg;

  } else {

  me._startAngleDeg = value;

  me.redraw();

  return this;

  }

};

me.paddingTop = function(value) {

  if (value === undefined) {

  return me._paddingTop;

  } else {

  var isValid =me.recalculateOuterRadius(me._paddingLeft, me._paddingRight, value, me._paddingBottom);

  if (isValid === false){

  alert(“Warning!  The gauge arc can’t have a small inner radius than outer!  Outer Radius must be equal to or greater than ” + me._innerRad);

  alert(“Please decrease the inner radius, or increase the size of the control.  Height & width (including subtraction for padding) must me at least twice as large as Internal Radius!”);

  } else {

  me._paddingTop = value;

  me.redraw();

  }

  return this;

  }

};

me.paddingBottom = function(value) {

  if (value === undefined) {

  return me._paddingBottom;

  } else {

  var isValid = me.recalculateOuterRadius(me._paddingLeft, me._paddingRight, me._paddingTop, value);

  if (isValid === false){

  alert(“Warning!  The gauge arc can’t have a small inner radius than outer!  Outer Radius must be equal to or greater than ” + me._innerRad);

  alert(“Please decrease the inner radius, or increase the size of the control.  Height & width (including subtraction for padding) must me at least twice as large as Internal Radius!”);

  } else {

  me.me._paddingBottom = value;

  me.redraw();

  }

  return this;

  }

};

me.paddingLeft = function(value) {

  if (value === undefined) {

  paddingLeft = me._paddingLeft;

  return paddingLeft;

  } else {

  var isValid = me.recalculateOuterRadius(value, me._paddingRight, me._paddingTop, me._paddingBottom);

  if (isValid === false){

  alert(“Warning!  The gauge arc can’t have a small inner radius than outer!  Outer Radius must be equal to or greater than ” + me._innerRad);

  alert(“Please decrease the inner radius, or increase the size of the control.  Height & width (including subtraction for padding) must me at least twice as large as Internal Radius!”);

  } else {

  me._paddingLeft = value;

  me.redraw();

  }

  return this;

  }

};

me.paddingRight = function(value) {

  if (value === undefined) {

  paddingRight = me._paddingRight;

  } else {

  var isValid = me.recalculateOuterRadius(me._paddingLeft, value, me._paddingTop, me._paddingBottom);

  if (isValid === false){

  alert(“Warning!  The gauge arc can’t have a small inner radius than outer!  Outer Radius must be equal to or greater than ” + me._innerRad);

  alert(“Please decrease the inner radius, or increase the size of the control.  Height & width (including subtraction for padding) must me at least twice as large as Internal Radius!”);

  } else {

  me._paddingRight = value;

  me.redraw();

  }

  return this;

  }

};

The new component.js

The new component.js, in its entirety:

sap.designstudio.sdk.Component.subclass(“com.sap.sample.scngauge.SCNGauge”, function() {

  var me = this;

//Properties

  me._colorCode = ‘blue’;

  me._innerRad = 0.0;

  me._outerRad = 0.0;

  me._endAngleDeg = 90.0;

  me._startAngleDeg = -90.0;

  me._paddingTop = 0;

  me._paddingBottom = 0;

  me._paddingLeft = 0;

  me._paddingRight = 0;

  me._offsetLeft = 0;

  me._offsetDown = 0;

//Validate the Inner and Outer Radii

  me.validateRadii = function(inner, outer) {

  if (inner <= outer) {

  return true;

  } else {

  return false;

  }

  };

//Recalculate Outer Radius.  Also, double check that the new value fits with me._innerRad

  me.recalculateOuterRadius = function(paddingLeft, paddingRight, paddingTop, paddingBottom){

  // Find the larger left/right padding

  var lrPadding = paddingLeft + paddingRight;

  var tbPadding = paddingTop + paddingBottom;

  var maxPadding = lrPadding;

  if (maxPadding < tbPadding){

  maxPadding = tbPadding

  }

  var newOuterRad = (me.$().width() – 2*(maxPadding))/2;

  var isValid = me.validateRadii(me._innerRad, newOuterRad);

  if (isValid === true){

  me._outerRad = newOuterRad;

  return true;

  }

  else {

  return false;

  }

  }

//Getters and Setters

  me.colorCode = function(value) {

  if (value === undefined) {

  return me._colorCode;

  } else {

  me._colorCode = value;

  me.redraw();

  return me;

  }

  };

  me.innerRad = function(value) {

  if (value === undefined) {

  return me._innerRad;

  } else {

  var isValid = me.validateRadii(value, me._outerRad);

  if (isValid === false){

  alert(“Warning!  The gauge arc can’t have a small inner radius than outer!  Inner Radius must be equal to or less than “ + me._outerRad);

  alert(“Please decrease the inner radius, or increase the size of the control.  Height & width (including subtraction for padding) must me at least twice as large as Internal Radius!”);

  } else {

  me._innerRad = value;

  me.redraw();

  }

  return this;

  }

  };

  me.endAngleDeg = function(value) {

  if (value === undefined) {

  return me._endAngleDeg;

  } else {

  me._endAngleDeg = value;

  me.redraw();

  return this;

  }

  };

  me.startAngleDeg = function(value) {

  if (value === undefined) {

  return me._startAngleDeg;

  } else {

  me._startAngleDeg = value;

  me.redraw();

  return this;

  }

  };

  me.paddingTop = function(value) {

  if (value === undefined) {

  return me._paddingTop;

  } else {

  var isValid =me.recalculateOuterRadius(me._paddingLeft, me._paddingRight, value, me._paddingBottom);

  if (isValid === false){

  alert(“Warning!  The gauge arc can’t have a small inner radius than outer!  Outer Radius must be equal to or greater than “ + me._innerRad);

  alert(“Please decrease the inner radius, or increase the size of the control.  Height & width (including subtraction for padding) must me at least twice as large as Internal Radius!”);

  } else {

  me._paddingTop = value;

  me.redraw();

  }

  return this;

  }

  };

  me.paddingBottom = function(value) {

  if (value === undefined) {

  return me._paddingBottom;

  } else {

  var isValid = me.recalculateOuterRadius(me._paddingLeft, me._paddingRight, me._paddingTop, value);

  if (isValid === false){

  alert(“Warning!  The gauge arc can’t have a small inner radius than outer!  Outer Radius must be equal to or greater than “ + me._innerRad);

  alert(“Please decrease the inner radius, or increase the size of the control.  Height & width (including subtraction for padding) must me at least twice as large as Internal Radius!”);

  } else {

  me.me._paddingBottom = value;

  me.redraw();

  }

  return this;

  }

  };

  me.paddingLeft = function(value) {

  if (value === undefined) {

  paddingLeft = me._paddingLeft;

  return paddingLeft;

  } else {

  var isValid = me.recalculateOuterRadius(value, me._paddingRight, me._paddingTop, me._paddingBottom);

  if (isValid === false){

  alert(“Warning!  The gauge arc can’t have a small inner radius than outer!  Outer Radius must be equal to or greater than “ + me._innerRad);

  alert(“Please decrease the inner radius, or increase the size of the control.  Height & width (including subtraction for padding) must me at least twice as large as Internal Radius!”);

  } else {

  me._paddingLeft = value;

  me.redraw();

  }

  return this;

  }

  };

  me.paddingRight = function(value) {

  if (value === undefined) {

  paddingRight = me._paddingRight;

  } else {

  var isValid = me.recalculateOuterRadius(me._paddingLeft, value, me._paddingTop, me._paddingBottom);

  if (isValid === false){

  alert(“Warning!  The gauge arc can’t have a small inner radius than outer!  Outer Radius must be equal to or greater than “ + me._innerRad);

  alert(“Please decrease the inner radius, or increase the size of the control.  Height & width (including subtraction for padding) must me at least twice as large as Internal Radius!”);

  } else {

  me._paddingRight = value;

  me.redraw();

  }

  return this;

  }

  };

  me.redraw = function() {

  var myDiv = me.$()[0];

  // Clear any existing gauges.  We’ll redraw from scratch

  d3.select(myDiv).selectAll(“*”).remove();

  var vis = d3.select(myDiv).append(“svg:svg”).attr(“width”, “100%”).attr(“height”, “100%”);

  var pi = Math.PI;

  // Find the larger left/right padding

  var lrPadding = me._paddingLeft + me._paddingRight;

  var tbPadding = me._paddingTop + me._paddingBottom;

  var maxPadding = lrPadding;

  if (maxPadding < tbPadding){

  maxPadding = tbPadding

  }

  me._outerRad = (me.$().width() – 2*(maxPadding))/2;

  //Don’t let the innerRad be greater than outer rad

  if (me._outerRad <= me._innerRad){

  alert(“Warning!  The gauge arc can’t have a negative radius!  Please decrease the inner radius, or increase the size of the control.  Height & width (including subtraction for padding) must me at least twice as large as Internal Radius!”);

  }

  //The offset will determine where the center of the arc shall be

  me._offsetLeft = me._outerRad + me._paddingLeft;

  me._offsetDown = me._outerRad + me._paddingTop;

  var arcDef = d3.svg.arc()

  .innerRadius(me._innerRad)

  .outerRadius(me._outerRad)

  .startAngle(me._startAngleDeg * (pi/180)) //converting from degs to radians

  .endAngle(me._endAngleDeg * (pi/180)); //converting from degs to radians

  var guageArc = vis.append(“path”)

      .style(“fill”, me._colorCode)

      .attr(“width”, me.$().width()).attr(“height”, me.$().height()) // Added height and width so arc is visible

      .attr(“transform”, “translate(“ + me._offsetLeft + “,” + me._offsetDown + “)”)

      .attr(“d”, arcDef);

  };

  me.init = function() {

  me.redraw();

  };

});

Testing the Component

So when the component comes up, it should look like this in the designer, with a height and width of 200em, a starting angle of -90 degrees (relative to 12 O’clock) and an end angle of 90 degrees.  It should also be blue.

/wp-content/uploads/2015/11/3d_1_836222.png

Altering the start and end angles to -45 and +45 respectively and choosing red from the color picker should result in a component that looks like this in the canvas.

/wp-content/uploads/2015/11/3d_2_836167.png

Epilogue

Testing the Padding properties makes me realize that the gauge is left justified by default.  Either we need more sophisticated placement options, or we need to drop the padding rules entirely.  If this were a normal project, I’d opt for the latter as controlling the gauge size via height and width is simpler.  Instead, we’ll use it as an opportunity to introduce the Additional Properties Sheet (APS), by building a small “Padding Visualizer”.

As always, the completed extension (as of part 3) is available as a Github repository.

To report this post you need to login first.

3 Comments

You must be Logged on to comment or reply to a post.

  1. keshav kushwaha

    Hi,

    I have a scenario where I am trying to develop Search Box Design Studio SDK (Java Plugin – Eclipse) Component using Sencha Technology.

    Figure1 : Search Box with Suggestion drop down with check box filter enabled developed using sencha technology with BW as Backend using Ajax call:



    I am trying to implement the same in design studio with suggestion drop down and check box filter on the top of suggestions.

    Do anyone have any idea how to achieve Search Box with suggestion drop down enabled with Check Box filter on the top of suggestion and will sencha libraries (Ext-all.js,Ext-all.js,Ext-all.css) supports design Studio.

    Please help me if anyone have any idea on the above scenario.

    Figure1:


    /wp-content/uploads/2016/01/demosearch_864042.png

    (0) 
  2. Mike Lundquist

    Double check the me.paddingBottom function.  The above code returns

         me.me._paddingBottom

    This will cause an error.

    (1) 
  3. Ceyhun Alp

    Hello,

    What is the difference between “return me” and “return this”?

    In getters and setters part, colorCode has “return me” but forexample, innerRad has “return this”.

    Thank you…

    (0) 

Leave a Reply