Question

How can I change the element associated with a call to transclude()?

In my app, I dynamically load an entire SVG file from the server and display it. I need to add behaviors to the loaded content.

Currently, I have something like this:

<div svg-canvas="urlToSVGContent"></div>

This loads an SVG tag inside the div. This works great, but what if I want to add an ng-click to every <path>, <circle>, etc? ng-click already works on svg paths out of the box, it's just a question of referencing the element somehow.

I can already make a directive using transclude that will run once for each path:

<div svg-canvas="urlToSVGContent">
    <svg-each-path>
        <!-- call transclude once per path found -->
    </svg-each-path>
</div>

But inside svg-each-path, while I have a separate scope for each element, the el parameter to the directive is meaningless. Or it still points to the parent div or something.

I would like to do this:

<div svg-canvas="urlToSVGContent">
    <svg-each-path ng-click="onPathClick()">
    </svg-each-path>
</div>

This is what svg-each-path looks like currently:

function svgEachPath() {
    return {
        restrict: 'E',
        transclude: 'element',
        priority: 1000,
        terminal: true,
        link: link,
    }    

    function link(scope, el, attrs, ctrl, $transclude) {
        // scope.paths was set by the svg-canvas directive
        scope.paths.forEach(function(path) {
            var childScope = <InnerScope> scope.$new()
            childScope.path = path

            // how can I change "el" to point to path?
            // or get the clone to be a clone of the path instead of the parent element?
            $transclude(childScope, function(clone) {

            })
        })
    }
}
Was it helpful?

Solution

I was looking for the $compile service. It lets you take any html string or element, and bind it to a scope to run directives. It doesn't require transclude at all.

function svgEachPath($compile) {
    return {
        restrict: 'E',

        // should stop processing directives. we don't want ng-click to apply to the fake element
        terminal: true,
        priority: 1000,

        link: link,
    }    

    function link(scope, el, attrs) {
        scope.paths.forEach(function(path) {
            // copy in all my attributes to the element itself
            Object.keys(attrs)
            .filter((key) => key[0] != "$")
            .forEach((key) => {
                // use snake case name, not camel case
                path.attr(attrs.$attr[key], attrs[key])                
            })

            // "compile" the element - attaching directives, etc
            var link = $compile(path)
            link(scope)
        })
    }
}

Usage:

<div svg-canvas="urlToSVGContent">
    <svg-each-path ng-click="onPathClick(...)">
    </svg-each-path>
</div>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top