jsexauer
jsexauer

Reputation: 681

Binding attributes using Directives

I'm trying to change the fill color of attributes in an in-line SVG using Angular and TypeScript. The idea is that the SVG have a "TA" attribute; any svg element with one of these "TA" attriubtes, I want to change the fill color to be bound to a user-defined color from the dropdown. However, I'm having a hard time figuring out how to change this attribute to be dynamicall bound.

Here is how I'm trying to do it:

export class TaDirective implements ng.IDirective {

static create_instance() : ng.IDirective {
    return new TaDirective();
}

constructor(){
}

public bindToController = true;
public controllerAs = "ctrl";
public scope = { name: '=' };

public compile(element: ng.IAugmentedJQuery, attrs: ng.IAttributes, transclude: ng.ITranscludeFunction) {
    var ta = attrs.$attr["ta"] as string

    var ele = element[0];
    attrs.$set

    // Make all visable (works as expected)
    attrs.$set('visibility', "visible")

    // Attempt to bind to the controller's fill
    attrs.$set('fill', "{{ ctrl.fill }}")
    //attrs.$set('fill', 'cyan') // Verify idea works
}

public link(scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes, controller: any, transclude: ng.ITranscludeFunction) {
    //Tried code here
}


public controller(scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes){
    // Tried code here
}

}
app.directive('ta', TaDirective.create_instance);

Here is a Plunker with the TypeScript and complied JavaScript.

EDIT So I figured out how to do it but it's still not elegant, as the name of the scope is hard coded. I'm open to suggestions for how to decouple to two. (Plunker also updated)

export class TaDirective implements ng.IDirective {
    static create_instance() : ng.IDirective {
        return new TaDirective();
    }

    constructor(){
    }

    public link(scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes, controller: any, transclude: ng.ITranscludeFunction) {
        var ta = attrs.$attr["ta"] as string

        var ele = element[0];

        // Make all visable
        attrs.$set('visibility', "visible")

        // Attempt to bind to the controller's fill
        scope.$watch('vm.fill', function(newFill) {
            attrs.$set('fill', newFill)
        })
    }
}

Upvotes: 1

Views: 258

Answers (1)

Monica Olejniczak
Monica Olejniczak

Reputation: 1156

A common practice for decoupling the controller with the directive watch expression is to instead watch a specified attribute.

Instead of the following:

JS:

scope.$watch('vm.fill', function (fill) {
    attrs.$set('fill', fill);
});

HTML:

<text ta="1">Text</text>

You should have:

JS:

scope.$watch(attrs.ta, (fill: string): void => {
    attrs.$set('fill', fill);
});

HTML:

<text ta="vm.fill">Text</text>

This practice ensures that your directive is more scalable since the vm.fill watch expression isn't bound to the directive link function, but is instead passed into the directive through the angular template. Here is an updated plunkr.

Upvotes: 1

Related Questions