0

In a legacy project, I want to create a new directive that uses transclude.

A trimmed down version of the directive code is:

app.directive('controlWrap', function() {
    return {
        restrict: 'E',
        transclude: true,
        scope: { label: "@" },
        templateUrl: "control-wrap-template.html"
    }
})

And the template is:

<div>
    <label>{{label}}</label>
    <div>
        <ng-transclude></ng-transclude>
    </div>
</div>

This directive is used like this

<control-wrap label="Just a example">
    <input type="text" ng-model="input" />
</control-wrap>
Test: {{input}}

I know that the workaround is to use a object in the scope instead of primitive value (ng-model inside ng-transclude). But that is no option for me. It is a ugly, poorly coded, legacy code that relies in those attributes directly on the scope.

Is there a something I can do in the directive to make that html works without change?

Community
  • 1
  • 1
Fernando
  • 2,131
  • 3
  • 27
  • 46
  • Legacy code shouldn't have any relevance, why can't you use object? Or do you really need isolate scope in this directive? – charlietfl May 18 '15 at 00:01
  • It is of relevance because it is a awful amount of code to change. I would like to use isolated scope, but I am trying to rewrite the directive the other way. (keep in mind that I posted a simplification of my issue, not the actual directive) – Fernando May 18 '15 at 00:27
  • @Fernando I don't particularly advocate my hackish solution here, but if you're at your wit's end and you need _something_ that works and fits your requirements (i.e. no modifications to the main HTML), then have a look at - http://plnkr.co/edit/u4vMParXvdp26LDgFGxa?p=preview – miqh May 18 '15 at 01:16
  • I like that ! I know it is a hackish solution, but tackle my problem. Can you post your comment as a answer? – Fernando May 18 '15 at 01:36

2 Answers2

0

You can manually transclude (instead of using ng-transclude) and apply whatever scope (which is, in your case, scope.$parent) you need to the transcluded content:

transclude: true,
scope: { label: "@" },
template: '<div>\
             <label>{{label}}</label>\
             <placeholder></placeholder>\
           </div>',
link: function(scope, element, attrs, ctrls, transclude){
   transclude(scope.$parent, function(clone){
      element.find("placeholder").replaceWith(clone);
   });
}

Demo

New Dev
  • 48,427
  • 12
  • 87
  • 129
  • I do not know if it is a good thing to do, I lack de knowledge, but I am choosing this solution because it is the idea that I am using right now. But the answer by @gyantasaurus is very good and the comment from miqid have its merits too. – Fernando May 19 '15 at 00:55
-1

The cleanest solution is to do some refactoring and passing an object instead of a primitive value, but if for some reason you cannot do that, you're not out of the options.

However, I wouldn't recommend any of these options

1) Bind input from the parent scope, that prevents creating a new value on the child scope upon write - butt keep in mind that accessing the parent scope hurts reusability of your directive. Angular 1.2:

<input type="text" ng-model="$parent.input" />

Angular 1.3:

<input type="text" ng-model="$parent.$parent.input" />

(The difference is because the parent of the transcluded scope is the directive scope from 1.3)

2) Create some kind of wrapper object and pass that instead of the primitive value

$scope.inputWrapper = {};
Object.defineProperty($scope.inputWrapper, 'input', {
    get: function() { return $scope.input },
    set: function(newValue) { $scope.input = newValue; }
})

and pass this to the directive. But again, I would do some refactoring instead.

gyantasaurus
  • 447
  • 2
  • 9