4

I encountered a problem with clashing directive and attribute names. This is a simplified version of my problem: there are two directives, where the name of the first directive is an attribute name of the second directive:

angular.module('mymodule').directive('property', function() {
    return {
        template: '<div>Property Directive</div>'
    };
});

angular.module('mymodule').directive('fail', function() {
    return {
        scope: {
            property: '='
        },
        template: '<div>{{property}}</div>'
    }
});

When I try to add my second directive to an html file:

<fail property="'test'"></fail>

I get the following error:

Error: [$compile:multidir] Multiple directives [fail, property] asking for template on: <fail property="'test'">http://errors.angularjs.org/1.3.0-rc.4/$compile/multidir?p0=fail&p1=property&p2=template&p3=%3Cfail%20property%3D%22'test'%22%3E

Now, this wouldn't be a problem if both directives were in my modules, since renaming them would be easy. But I have clashing directive/attribute names from different external modules that I use in my application.

How can I tell angular, that the attribute property is not meant to be a directive in this particular case?

forrert
  • 4,109
  • 1
  • 26
  • 38
  • Probably you can decorate these 2 directives and change their restrict to different ones? Or change their priorities to different, making directive fail higher priority with terminal option? – PSL Oct 17 '14 at 01:50
  • http://plnkr.co/edit/5Cfn5T?p=preview Depending upon what those directives are doing you can change their restrict value. – PSL Oct 17 '14 at 01:59
  • Thanks for your help and sample code. I am still trying to work this out, since changing directives from other modules can have a number of side effects. I'm quite new to Angular and I feel like I am missing an important point here. It seems to me like lots of people should run into these kinds of problems with directive and attribute names clashing... Do you know of a way to rename (or add prefixes to) directive names from other modules? – forrert Oct 21 '14 at 17:54
  • `lots of people should run into these kinds...` Not really. It is because directives are poorly named. Generally directives gets prefixed with the product name. ex `ng-` angular `ui-` angular ui components, ex:- `sc-` some component apps directives, even when we write directives which is made a reusable they must have some prefix that specifies the product name. You wont run into all these issues. If the same component is having the attribute name in one directive and another directive name with the attribute name then there is some problem there.. Consumer should not ideally work around that. – PSL Oct 21 '14 at 17:57
  • try something like this to rename (recreate).. http://plnkr.co/edit/ADsefY?p=preview Just an example. If you can provide a minimal realistic example i can try leverage this. – PSL Oct 21 '14 at 23:10
  • I don't think the problem him is name collision. The problem is that both directives are trying to template the element. This would happen whether they are named the same or not. – New Dev Oct 22 '14 at 03:42
  • @NewDev I dont know what did you understand by `name collision` :D `The problem is that both directives are trying to template the element` --> That is because of name collision because attribute name expected by one directive is the name of another directive. Watch closely to the comments and OP example you will understand that. If both are isolated scopes then it will be resulting in an error for scope, in this case it is template. – PSL Oct 22 '14 at 11:01
  • Yes, it's exactly what I understood, but I think what you're both are saying is that the other directive was inadvertently invoked, whereas I at first understood it as both the directives were desired. – New Dev Oct 22 '14 at 16:30
  • @NewDev Nopes that is the whole problem.. You were initially only looking at the symptom.. :) – PSL Oct 22 '14 at 17:21

1 Answers1

2

Just extending my comment to answer, Instead of renaming the directive on way i could think of is to create a copy of the same directive and invalidate the existing one. That way you could have a proper naming conventions for poorly named directives that you are consuming from another module. Here you need

  • $compileProvider

    • To register a new directive, overriding the angular directive constructor app.directive.
  • $provide service

    • To decorate the directive with poor name, get its definition and return back just a blank no operation factory.
    • You can decorate a directive postFixing with Directive keyword. They are also registered as a factory.

You need to make sure this configuration, especially the decoration part appears after the targeted directives are registered.

app.config(['$compileProvider', function ($compileProvider) {
    //Override directive constructor
    app.directive = function (name, dirObject) {
        //Register a directive
        $compileProvider.directive(name, function() {
           return dirObject[0];
        });
    };
}]).config(['$provide', function($provide){
    //Decorate target directive
    $provide.decorator('propertyDirective', ['$delegate', function($delegate){
        //Just register a new directive with source's definition
        app.directive('cmProperty', $delegate);
        //return a no operation factory as directive constructor, to make it inactive
        return function() { return angular.noop };
    }]);
}]);

Demo

You could automate this by placing the target directive names in a constant and running a loop of decorators to automatically prefix/rename(recreate with another name) it.


Update

See a generic solution in my repository

PSL
  • 123,204
  • 21
  • 253
  • 243
  • 1
    @przemo_li when you have 2 directives with the same name the factories are additive which means when the directive is used it will render both the directive compilation in the order of priority also you cant have 2 different templates and so on. Ofcourse this will be special purpose and intentionally created. For example ng-required directive and some other directives in the angular framework itself. Hope this helps! – PSL Oct 15 '15 at 16:47
  • PSL - if you're still following this question, would you possibly be able to have a look at my implementation of your code on this question? http://stackoverflow.com/questions/36049473/renaming-3rd-party-angular-directive-using-provide-not-working?noredirect=1#comment59837412_36049473 – jonhobbs Mar 19 '16 at 13:22
  • @jonhobbs I have made the solution more modular, abstract and easy to use with one set up. Also i have also added the explanation inline the source code in answer to your question. – PSL Mar 19 '16 at 17:06