1

I'm new to angular, so forgive me if I missed anything or misunderstand the docs.

I have a directive, which converts the element to jquery plugin

.directive('myDir', function($compile) {
    return {
        link: function(scope, element, attributes) {
            // $('.someDiv').jqueryPlugin(...);
            element.jqueryPlugin();
            var el = $compile("<span translate>{{ 'label' }}</span>")(scope);
            element.find('.label').html(el);
        }
    }
})

as you can see, first I create a jquery plugin in html element (it creates its dom inside element, including some div with label class - lets say it contains some string that should be dynamic, and should be translateable globally) and then I replace static jquery-generated label to interpolated one. I should be able to manage it from controller.

the problem is, that I can have many directives in one controller, let's say

<div my-dir class="label-inside-1"></div>
<div my-dir class="label-inside-2"></div>
<div my-dir class="label-inside-3"></div>
<div my-dir class="label-inside-4"></div>

after directive and jquery is run it would give something, like, let's say

<div my-dir class="label-inside-1">
    <div class="label">
        <span>{{label|translate}}</span>
    </div>
</div>
<div my-dir class="label-inside-2">
    <div class="label">
        <span>{{label|translate}}</span>
    </div>
</div>
<div my-dir class="label-inside-3">
    <div class="label">
        <span>{{label|translate}}</span>
    </div>
</div>
<div my-dir class="label-inside-4">
    <div class="label">
        <span>{{label|translate}}</span>
    </div>
</div>

how, from the controller, can I manage a particular directive? how to access the scope for a chosen one?

i assume that

// controller
$scope.label = "some content";

is going to change all of the labels

is there a way to achieve the goal? Or should I review the approach to the problem?

thanks in advance!

EDIT

I will also have dom elements, that would need to have directive attached from the controller level. they should also be maintainable from this level. so my idea is to provide a service, that would be some kind of facade with api, that would work on jquery-plugin'ed dom elements.

so lets say I would need something

.provider('facade', function() {
    this.$get = function($rootScope, $compile) {
        return {
            createPlugin: function(domElement, defaultLabel) {
                domElement.attr('my-dir', defaultLabel);
                $compile(domElement)($rootScope);
            },
            changeLabel(domElement, newLabel) {
                // get a scope of myDir for provided dom element
                scope.label = newLabel;
            }
        }
    };
})

it works for createPlugin, but no idea how to get changeLabel working...

Best usage of the facade would be from controller:

toBePlugined = $('div.tbp');

facade.createPlugin(toBePlugined, 'label');
facade.changeLabel(toBePlugined, 'label2');

why do I need a service for that? because I want to be able to amend pluginned elements configuration from various places in the script. that could include various divs, body tag, etc.

and now - my problem is to access the directive scope by provading its dom object reference. is it possible? I was trying to play with my-dir attribute on dom object with no effect.

John Slegers
  • 45,213
  • 22
  • 199
  • 169
hopsey
  • 1,383
  • 1
  • 13
  • 23

1 Answers1

2

There could be multiple ways to solve this, here are a couple of ways. You could use isolated scoped directive (scope:{}) with 2-way (=), based on how you need it. You could also use scope:true, i.e creating a child scope from the directive (if using with ng-repeat you can even use it with no scope since it already creates a child scope). But this approach would be less reusable/flexible as compared to the directive with its own contract(isolate scoped).

So you could change your directive to:

.directive('myDir', function($compile) {
    return {
        scope:{
           label:'=myDir' //Set up 2 way binding
        },
        link: function(scope, element, attributes) {
            // $('.someDiv').jqueryPlugin(...);
            element.jqueryPlugin();
            var el = $compile("<span translate>{{ 'label' }}</span>")(scope);
            element.find('.label').html(el);
        }
    }
});

and bind from your controller, say you have a list of labels.

 $scope.labels = [{label:'label1'}, {label:'label2'}, {label:'label3'}]

then you could just do:

<div ng-repeat="item in labels" my-dir="item.label"></div>
PSL
  • 123,204
  • 21
  • 253
  • 243
  • PSL, why would you use = here instead of &? Seems like that sets up an unnecessary watch. – Joe Enzminger Mar 11 '15 at 02:41
  • @JoeEnzminger I dont like the idea of using/hacking function binding (&) for explicitly bound property. Also note `lets say it contains some string that should be dynamic`, if you really want a one-time binding you could as well just use `@` but that don't seem to be the purpose of OP. and well remember `::` binding in 1.3+, removing unwanted watch is not really a problem that needs to be hacked with a different purpose implementation IMHO. And so why would u use `=` or `@` because that is more appropriate to me than using `&` for this specific one. – PSL Mar 11 '15 at 02:44
  • I don't think that's correct (or that it is a hack). @ binding forces interpolation - which means the value will always be a string (you can't bind to objects with @). = creates an implicit watch on the directive scope value that modifies the corresponding parent scope expression if it is assignable. & creates a one way binding - which prevents the directive from modifying the parent scope - I think it is more than appropriate here. – Joe Enzminger Mar 11 '15 at 02:49
  • @JoeEnzminger OP wants the label to be dynamic, very evident from the question (unless i interpretted it wrongly). And if your problem is a watch there are right ways by using one-time binding (try it out if you are in doubt), You do not have to always bind the object with `@` as well. you could get the bound property names and evaluate against parent scope to get the values to be bound. – PSL Mar 11 '15 at 02:50
  • In interpreted it that he wants the directive value to change if the parent scope changes, but not the other way around. label: "&myVal" + {{'label()'}} does this without the overhead of the implicit watch, but now that I'm realizing label will always evaluate to a string I think @ is the most appropriate choice. – Joe Enzminger Mar 11 '15 at 02:55
  • @JoeEnzminger it meas that `{{'label()'}}` creates a watch :) so you aren't removing it anyways. But `my-dir="::item.label` also does create just one only to be removed, with that you could as well have `{{ '::label' }}` as well so ultimately you end up with no watchers. `& bindings are ideal for binding callback functions to directive behaviors.` according to me there isn't any need to (ab)use it inorder to remove a watch, when there are already ways. – PSL Mar 11 '15 at 02:55
  • But just one. = creates two :) – Joe Enzminger Mar 11 '15 at 02:55
  • @JoeEnzminger see above, and also providing a `&` binding, you (another developer) could even just bind another function that returns a value, and remember that function gets invoked every digest cycle when used inside the interpolation. So it can open a flood for whole lot of other problems especially if the developer who uses it does whole lot of extensive operations inside the bound function. So it probably just opens a flood gate as well. – PSL Mar 11 '15 at 03:05
  • 1
    Just like any other watch (that's why I like to minimize watches :). [Check this out](http://plnkr.co/edit/MtK26mnjRuq39GZdQz61?p=preview), using ::label you lose your dynamic properties. I think @ is indeed the correct way to go here. Just nitpicking. I agree with your quoted text - they are ideal for binding callback functions to directives, but that's not all they are ideal for. They are great for removing unnecessary watches as well and isolating directives from parent scopes ;) – Joe Enzminger Mar 11 '15 at 03:15
  • 1
    @JoeEnzminger Yeah as i said earlier, better ways than using `&` because it can open another flood gate (I have had experience of that being misused when not used with purpose :( ).. Overall a good discussion though.. :) Thx!! – PSL Mar 11 '15 at 03:20
  • Guys, thank you very much for your help. I'm having another problem with this context - I don't know how to manage 'directived' element from controller by its dom. I want to have some kind of service, that would handle that, and all I can pass to them is DOM. I've edited the question and described the problem. thanks! – hopsey Mar 11 '15 at 13:40