0
sap.ui.core.Control.extend("control.linechart", { 
    /* the control API */
    metadata : {
        properties : {
            "items" : { type: "any" },
            "height" : {type: "int"},
            "width" : {type: "int"},
            "popup" : {type: "any"}
        },
        events: {
            "select" : {},
                "selectEnd": {}             
            }           
        },

        // the part creating the HTML:
        renderer : function(oRm, oControl) {    // static function, so use the given "oControl" instance instead of "this" in the renderer function
            oRm.write("<div"); 
            oRm.writeControlData(oControl);     // writes the Control ID and enables event handling - important!
            oRm.addClass("lineChart");              // add a CSS class for styles common to all control instances
            oRm.writeClasses();                 // this call writes the above class plus enables support for my.Bubble.addStyleClass(...)               
            oRm.write(">");
            oRm.write("</div>");
        },  
        onAfterRendering: function() {
            data = this.getItems();
                //alert(JSON.stringify(this.getItems()));
                var passedheight = this.getHeight();
                //var containerWidth = jQuery.sap.byId(this.oParent.sId).width() || 800; // gets super parent width
                var containerWidth = $("#"+this.getId()).parent().width(); // gets immediate parent width
                var margin = {top: 15, right: 30, bottom: 20, left: 30},
                width = containerWidth- margin.left - margin.right,
                 height = passedheight - margin.top - margin.bottom;

                var parseDate = d3.time.format("%d-%b-%y %H:%M %p").parse;

                var x = d3.time.scale().range([0, width]);

                var y = d3.scale.linear()
                    .range([height, 0]);

                var color = d3.scale.category10();

                var xAxis = d3.svg.axis()
                    .scale(x)
                    .orient("bottom");

                var yAxis = d3.svg.axis()
                    .scale(y)
                    .orient("left").ticks(4).tickSize(-width, 0, 0);

                var line = d3.svg.line()
                    .x(function(d) { return x(d.date); })
                    .y(function(d) { return y(d.tonneValue); });

                var svg = d3.select("#"+this.getId()).append("svg")
                    .attr("width", width + margin.left + margin.right)
                    .attr("height", height + margin.top + margin.bottom)
                    .append("g")
                    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

                color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; }));

                data.forEach(function(d) {
                    d.date = parseDate(d.date);
                });

                var wsfs = color.domain().map(function(name) {
                    return {
                        name: name,
                        values: data.map(function(d) {
                            return {date: d.date, tonneValue: +d[name]};
                        })
                    };
                });

                x.domain(d3.extent(data, function(d) { return d.date; }));

                y.domain([
                    d3.min(wsfs, function(c) { return d3.min(c.values, function(v) { return v.tonneValue; }); }),
                    d3.max(wsfs, function(c) { return d3.max(c.values, function(v) { return v.tonneValue; }); })
                ]);

                svg.append("g")
                    .attr("class", "x axis")
                    .attr("transform", "translate(0," + height + ")")
                    .call(xAxis);

                svg.append("g")
                    .attr("class", "y axis")
                    .call(yAxis);

                var wsf = svg.selectAll(".wsf")
                    .data(wsfs)
                    .enter().append("g")
                    .attr("class", "wsf");

                wsf.append("path")
                    .attr("class", "line")
                    .attr("d", function(d) { return line(d.values); })
                    .style("stroke", function(d) { return color(d.name); });

                var legendNames = d3.keys(data[0]).filter(function(key) { return key !== "date" });

                data.forEach(function(d) {
                    d.ages = legendNames.map(function(name) { return {name: name, value: +d[name]}; });
                });
                var legend = svg.selectAll(".legend")
                    .data(legendNames.slice())
                    .enter().append("g")
                    .attr("class", "legend")
                    .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });

                legend.append("rect")
                    .attr("x", width - 18)
                    .attr("width", 18)
                    .attr("height", 4)
                    .style("fill", function(d) {return color(d); });

                legend.append("text")
                    .attr("x", width - 24)
                    .attr("y", 6)
                    .attr("dy", ".35em")
                    .style("text-anchor", "end")
                    .text(function(d) {  return d; });
                },
});

In this whole program why Right hand side value is changing i.e. when I do data = this.getItems(); I assume this.getItems is assigned to data. But data is manipulated in rest of the program but when oAfterRendering is invoked again I get the manipulated data thats there in variable data. How is this.getItems() i.e. items property is manipulated when data is the one that is manipulated.Due to use of Openui5 and custom controls there is no way to save the data in some temporary variable.

Georgios Syngouroglou
  • 18,813
  • 9
  • 90
  • 92
SiddP
  • 1,603
  • 2
  • 14
  • 36

1 Answers1

2

When you return an object from a function, JavaScript does not copy that object, but return a reference to it (similar a pointer in C).

This means that if it's modified, it will be reflected in all variables that point to it.

This is the same behaviour for Arrays and other objects.

Anything to avoid that.

You could return a clone of the object. Depending on how complex your object is, a deep clone could be necessary. Keep in mind that this shouldn't be done often, as it can likely affect performance.

An easy way of creating a shallow clone (really just copying properties, cloning any object is more nuanced in JavaScript) in ES6 is...

var cloned = Object.assign({}, objectToBeCloned); 
Community
  • 1
  • 1
alex
  • 479,566
  • 201
  • 878
  • 984
  • Anything to avoid that. – SiddP Apr 06 '16 at 09:13
  • @SiddP: If you want a *copy* of the object, make a copy of the object. – T.J. Crowder Apr 06 '16 at 09:15
  • Well I tried this but the results are the same `var cloned = Object.assign(this.getItems()); data = cloned;` I am using openui5 so onAfterRendering cant be explicitly so I have no way to store var outside it and if I write inside it the result is same. – SiddP Apr 06 '16 at 09:44
  • 1
    @SiddP You didn't use the code example correctly. You must have that empty object as the first argument. – alex Apr 06 '16 at 09:48
  • I didn't use that to as I needed JSON array as feed for d3. But anyway I tried using the first argument too resulting in same output . `var cloned = Object.assign({},this.getItems()); var data = $.map(cloned, function(el) { return el });` – SiddP Apr 06 '16 at 10:07
  • 1
    If you point `data` directly to `cloned`, you are pointing both to the same object. The `$.map()` call you're doing is just a roundabout way of copying the object properties to a new object. jQuery has `$.extend()` anyway for that. – alex Apr 06 '16 at 10:20
  • In any way all the values related to data are changing. – SiddP Apr 06 '16 at 12:12
  • @SiddP You need to consider what the object contains too. Does it contain other objects? If so, the same problem will occur. You would need to use a deep clone. – alex Apr 06 '16 at 13:09