0

I want to set ngModel attribute to any element which is transcluded into derective.

I use slot transcludes: `

  transclude: {
            'editor': 'editorControl'
        },`

so then in link method I add attribute of ng-model to child of editor-control using jquery.

problem is that despite that attribute is added to DOM ng-model is not binded to component (when I change value in input, model value is not changed in scope)

Here is full code:

    (function () {
    'use strict';

    var module = angular.module('nokia');

    module.directive('optionalEditor', function () {
        return {
            templateUrl: 'plugins/acc/app/common/optional-editor/optional-editor.html',
            restrict: 'E',
            require: '?ngModel',
            replace: true,
            transclude: {
                'editor': 'editorControl'
            },
            scope: true,
            link: function (scope, element, attrs, ngModelCtrl,transcludeFn) {
                scope.model = {};
                scope.model.viewValue = 'Not set';
                scope.model.beingEdited = false;

                if (ngModelCtrl) {
                    scope.toggleEditMode = function (doApply) {
                        if (scope.model.beingEdited) {
                            if (doApply) {
                                ngModelCtrl.$setViewValue(scope.model.editValue);
                            }
                        } else {
                            scope.model.editValue = ngModelCtrl.$viewValue;
                        }
                        scope.model.beingEdited = !scope.model.beingEdited;
                    };

                    ngModelCtrl.$render = function () {
                        scope.model.viewValue = this.$viewValue ? this.$viewValue : 'Not set';
                    };
                }
                transcludeFn(scope, function (clone, sc) {
                var eControl = clone.children();
                eControl.attr("ng-model", "model.editValue");
                element.find('editor-control').replaceWith(clone);
            });
            }
        }
    });
})(window);

template:

<div>
<a ng-show="!model.beingEdited" ng-click="toggleEditMode()" href>{{model.viewValue}}</a>

<div ng-show="model.beingEdited">
    <div class="row">
        <div class="col-xs-8">
            <ng-transclude ng-transclude-slot="editor"></ng-transclude>
        </div>
        <button type="button" class="btn btn-primary" title="Apply" ng-click="toggleEditMode(true)"><i
                class="fa fa-check"></i>
        </button>
        <button type="button" class="btn btn-default" title="Cancel" ng-click="toggleEditMode(false)"><i
                class="fa fa-ban"></i>
        </button>
    </div>
</div>

usage:

 <optional-editor ng-model="parameter.value" class="col-sm-6">
    <editor-control>
     <input class="form-control" type="number" id="{{metaDs.id}}" placeholder="integer value">
   </editor-control>
 </optional-editor>

I would like to input to have ngModel binded to value from directives scope. Maybe someone can suggest how to do that?

Edgar
  • 1,120
  • 4
  • 28
  • 53

1 Answers1

1

I think what you're looking for is the transcludeFn callback introduced in the directive link function. This allows you to specify a scope for your transcluded content, meaning you can add an ng-model attribute to an element in your transcluded content and still access it via your directive's scope.

Here's an example of how you would do this:

in the markup to be transcluded:

<input type="number" ... ng-model="ngModel">

in the transcluding directive:

...
link: function (scope, element, attrs, ngModelCtrl, transclude) {
...
  // this function applies the scope in it's first parameter to the 
  // transcluded markup, clone is the transcluded content
  transclude(scope, function(clone, scope) {

    // append the transclude content to your directive's element, 
    // or any other element you choose
    element.append(clone);
  }

using the transcludeFn is necessary because by default your transclude content will use the parent controller's scope and you won't be able to modify it directly in your directive.

update - dynamic ng-model

to dynamically add an ng-model attribute to your directive you'll need to add the attribute to your clone element in the transclude function and compile it:

transclude(scope, function(clone, scope) {
  var target = angular.element( document.querySelector('#transclude-contents') );
  clone.attr('ng-model', 'ng-model');
  $compile(clone)(scope);
  target.append(clone);
});

here's the updated plnkr: http://plnkr.co/edit/WKX8562vD0OvokR6Mujv?p=preview

paul trone
  • 702
  • 5
  • 10
  • Yea I tried this function. I would like to add `ng-model` attribute using `eControl.attr("ng-model", "model.editValue");` in transclude method, but then it stops working, no idea why – Edgar Feb 10 '16 at 18:41
  • I added an update to cover dynamic ng-model attributes – paul trone Feb 10 '16 at 19:43
  • I tried using compile, but it throws error: `expected `111111` to be a number` no idea how to get rid of it. – Edgar Feb 10 '16 at 21:47
  • you need to specify your input as type='number' instead of text – paul trone Feb 10 '16 at 21:50
  • it is 'number' see your example – Edgar Feb 11 '16 at 07:06
  • take a close look at the linked example, it doesn't throw this error, if you create a plnkr that does I'd be happy to look at it – paul trone Feb 11 '16 at 14:24
  • I use Chrome, and when open console during run of this: http://plnkr.co/edit/I5LlYVszy8oQiRsdNkEY?p=preview example it shows me error which I mentioned before. – Edgar Feb 11 '16 at 14:55
  • you're right, I guess it did some weird conversion on my initial value from number to string, I updated the plnkr, should be fixed. – paul trone Feb 11 '16 at 15:33
  • it throws error if there is already value set. ` $scope.parameter = { value: 1000 }` will give error – Edgar Feb 11 '16 at 16:59