0

Suppose I have something like this:

<div my-custom-root="root">
    <input type="text" my-custom-Path="path.to.somewhere" />
</div>

And now I want to translate this to something which essentially behaves equivilant to:

<input type="text" ng-model="root.path.to.somewhere" />

I get so far as to specify the two directives, get all the objects etc. These supply me with the root object and the path, but how do create a binding from those? I am missing the generation of the appropriate ng-model directive or the use of the NgModelController directly.

I created a plunkr here with the stuff I managed to put together so far.

For easier reference here is my code of the directives just like they can be found in my plunkr as well:

app.directive('myCustomRoot', function() {
    var container;

    return {
        controller: function($scope) {
            this.container = function() {
                return container;
            };
        },

        compile: function() {
            return function ($scope, elm, attrs) {
                $scope.$watch(attrs.myCustomRoot, function(newVal) {
                    container = newVal;
                });
            };
        }
    };
});

app.directive('myCustomPath', function($parse) {
    return {
        require: '^myCustomRoot',

        link: function(scope, element, attrs, containerController) {
            var getter = $parse(attrs.myCustomPath);
            scope.$watch(function() {
                    return containerController.container();
                },
                function(newVal) {
                    if (newVal) {
                        alert('The value to be edited is: ' + getter(newVal) + '\nIt is the property "' + attrs.myCustomPath + '"\non Object: ' + JSON.stringify(newVal) + '\n\nNow how do I bind this value to the input box just like ng-model?');
                    }
                }
            );
        }
    };
});

You can see that I have all the things available in my alert-Box, but I don't know how to do the binding.

yankee
  • 38,872
  • 15
  • 103
  • 162
  • There's a setter method which pretty much similar to the getter that you have already used... – harishr Jul 17 '14 at 12:55
  • @HarishR: You mean the setter for my property? Yes, I could use it to update the value, but I need to do so everytime something in the `` element changes. Yes I could register listeners on the input und do so, but the ng-model directive does all of this. So I hope that I can somehow utilize it. – yankee Jul 17 '14 at 13:01
  • seems overly complex to me, you only need the string value of `my-custom-root` so you can set the `ng-model` attribute and compile it – charlietfl Jul 17 '14 at 14:12
  • Can the `my-custom-root` and `my-custom-root` be dynamically changed? – runTarm Jul 18 '14 at 07:54
  • @charlietfl: Actually I tried that. I inserted `attrs.$set('ngModel', 'some.Path');` at the point where the alert is. When I look at the DOM in Firebug the `ng-model` attribute gets inserted properly but my input box remains empty, even if my model is not. I also tried to make sure that the attribute is created in-time by playing with priority levels and moving the $set() attribute to another position (not inside $watch), but I could not get this to work. – yankee Jul 18 '14 at 10:28
  • @runTarm: The string which refers to a property does not change. However the value it refers to my change. Especially the root-object may be loaded asynchronously and may not yet be present when rendering. – yankee Jul 18 '14 at 10:30
  • I see, so the `root` and `path.to.somewhere` both are literal strings, not expressions right? – runTarm Jul 18 '14 at 10:35
  • @runTarm: Well yes, the attribute values themselves are literal strings. Of course at some point they will need to be handled as expression so find the object they refer to. – yankee Jul 18 '14 at 11:07

1 Answers1

0

I hoped that there was some way to write someBindingService.bindInput(myInput, myGetter, mySetter), but I have done quite a lot of source code reading and unfortunately the binding is coupled closely to the compilation.

However thanks to the question "Add directives from directive in AngularJS" I managed to figure out a way which is less elegant but it is compact and effective:

app.directive('myCustomPath', function($compile, $parse) {
      return {
        priority: 1000,
        terminal: true,

        link: function(scope, element, attrs, containerController) {
          var containerPath = element.closest('[my-custom-root]').attr('my-custom-root');
          attrs.$set('ngModel', containerPath + '.' + attrs['myCustomPath']);
          element.removeAttr('my-custom-path');
          $compile(element)(scope);
        }
      }
    });

This uses a little bit of jQuery, but it should not be to hard to do it with plain jQLite as well.

Community
  • 1
  • 1
yankee
  • 38,872
  • 15
  • 103
  • 162
  • You could require `^myCustomRoot` and set/get its attribute in controller, that would eliminate the use of jQuery. – runTarm Jul 21 '14 at 06:08
  • @runTarm: No I can't. The controller is not available during link-time (if I misunderstood you, plz provide an example) – yankee Jul 27 '14 at 18:16
  • Please see `my-custom-path2` directive in this http://plnkr.co/edit/QFDmIXhzaF2plKw2hYpw?p=preview – runTarm Jul 28 '14 at 07:56