Loren Cahlander
Loren Cahlander

Reputation: 1347

Looking to pass a callback function with a parameter to child component in Polymer

I want to create a general Polymer component that calls a callback function in a parent Polymer component for each instance of a data element in the child component.

Here is the general parent:

<dom-module id="nested-parent">
  <template>
    <style>
      :host {
        display: block;
      }
    </style>
    <nested-child callback="nodedesign(node)"></nested-child>
  </template>

  <script>
    Polymer({

      is: 'nested-parent',

      properties: {
      },

      nodedesign: function(node) {
      }

    });
  </script>
</dom-module>

Here is the general child:

<dom-module id="nested-child">
  <template>
    <style>
      :host {
        display: block;
      }
    </style>
  </template>

  <script>
    Polymer({

      is: 'nested-child',

      properties: {
        callback: {
          type: String
        }
      }

    });
  </script>
</dom-module>

I would like for the node-child call the nodedesign(node) {} function in the node-parent component for each instance of a node in the child.

UPDATE:

I am trying to create a d3 force graph component that the parent element will have the functions for drawing the nodes and the links and so the general zoom and pan capabilties are in the general component.

    <link rel="import" href="../../bower_components/polymer/polymer.html"/>
    <script src="../../bower_components/d3/d3.min.js"></script>  

    <dom-module id="d3-graph">
        <template>
            <style>

            </style>
            <div id="generalGraph"></div>
        </template>

        <script>

            var d3graphSVG;

            Polymer({
            is: "d3-graph",
            properties: {
            graph: { type: Object, notify: true, observer: '_graphChanged' }
            },
            ready: function() {
            this.scopeSubtree(this.$.generalGraph, true);

            d3graphSVG = d3.select(this.$.generalGraph).append("svg")
            .attr("width", "100%")
            .attr("height", "100%");
            },
            _graphChanged: function() {
            if (this.graph == null) return;
            if (this.graph.nodes.length == 0) return;
            var width = 800;
            var height = 800;

            this.scopeSubtree(this.$.generalGraph, true);

            var force = d3.layout.force()
            .gravity(0.05)
            .distance(100)
            .charge(function(d) { return (d.mode == 1) ? 50 : -100;})
            .size([width, height]);

            force
            .nodes(this.graph.nodes)
            .links(this.graph.links)
            .start();


            var link = d3graphSVG.selectAll(".link")
            .data(this.graph.links)
            .enter().append("line")
            .attr("class", "link");

            var node = d3graphSVG.selectAll(".node")
            .data(this.graph.nodes)
            .enter().append("g")
            .attr("class", "node")
            .on("click", function(d) {
            console.log("foo");
            })
            .call(force.drag);

            node.append("image")
            .attr("xlink:href", "https://github.com/favicon.ico")
            .attr("x", -8)
            .attr("y", -8)
            .attr("width", 16)
            .attr("height", 16);

            node.append("text")
            .attr("dx", 12)
            .attr("dy", ".35em")
            .text(function(d) { return d.name });

            force.on("tick", function() {
            link.attr("x1", function(d) {
                            if (d.source.mode == 1) { d.source.x = 50; }
                            return d.source.x; })
            .attr("y1", function(d) { return d.source.y; })
            .attr("x2", function(d) { 
                            if (d.target.mode == 1) { d.target.x = 50; }
                            return d.target.x; })
            .attr("y2", function(d) { return d.target.y; });

                node.attr("transform", function(d) { 
                if (d.mode == 1) {
                    d.fixed = true;
                    d.x = 50;
                }
                return "translate(" + d.x + "," + d.y + ")"; 
                });
            });

            }
            })
        </script>
    </dom-module>

The following would be in a function in the parent that would draw the node based on the content of the data:

var node = d3graphSVG.selectAll(".node")
            .data(this.graph.nodes)
            .enter().append("g")
            .attr("class", "node")
            .on("click", function(d) {
                console.log("foo");
            })
            .call(force.drag);

node.append("image")
    .attr("xlink:href", "https://github.com/favicon.ico")
    .attr("x", -8)
    .attr("y", -8)
    .attr("width", 16)
    .attr("height", 16);

node.append("text")
    .attr("dx", 12)
    .attr("dy", ".35em")
    .text(function(d) { return d.name });

ADDITIONAL UPDATE:

I am going to try adding a function to the data while in the parent:

    graph.nodes.forEach( function (node)
    {
        node._draw = function() {
            return '<g class="node"><image xlink:href="https://github.com/favicon.ico" x="-8" y="-8" width="16" height="16"></image><text dx="12" dy=".35em">'
            + this.name + '</text></g>'
        };
    });

And the change the child component function code to:

    var node = d3graphSVG.selectAll(".node")
    .data(this.graph.nodes)
    .enter().html(this._draw()).call(force.drag);

Upvotes: 1

Views: 954

Answers (1)

adaliszk
adaliszk

Reputation: 604

You think in a wrong way, the child elemet should not be avere of the parent element, because it's wont be reusable. Better way to do it it's to have the child a notify property:

properties: {
    data: {type:Object, notify: true}
}

And in the parent element listen it's changed event eather with event listeners:

this.addEventListener('data-changed', this.nodedesign.bind(this));
nodedesign: ( event ) => {
    // event.detail should have the data and the element in it
    let item = event.detail.item;
    let data = event.detail.data;
},

Or using Polymer Data Binding system:

<nested-child data="{{parentData}}"></nested-child>
properties: {
    parentData: {type: Object, observer: 'nodedesign'}
},

nodedesign: ( newValue, oldValue ) => {
    // do stuff
},

Of corse you can call the parent function with a little trick, but I do not recommend it:

let parent = null;
while ((parent = parent.parentElement) && parent.tagName != 'NESTED-PARENT');
if( parent != null ) parent.nodedesign( this )

Upvotes: 1

Related Questions