0

I have to write a directive that would have the following behavior :

Controller:

vm.mymodel = 'Hello World'

Html:

<custom-input mydirective="{propbind: 'data-value', propmodel: vm.mymodel}"></custom-input>

I would like my custom-input to be transformed as follow:

<custom-input data-value="{{vm.mymodel}}"></custom-input>
  • When I update vm.mymodel from the controller, the data-value attribute has to change.
  • When I update the data-value, the vm.mymodel has to be updated too.
  • I can't use ng-mmodel directive on the custom-input (it uses data-value, and has internal functions applied to it)

So here's what I tried to do :

  • In the compile function of my directive, set scope.propbind attribute value to "{{propmodel}}", then remove "mydirective" attribute. But I don't seem to have access to the scope here. However, if I use element.attr('data-value', 'hello world') I can see my data-value correctly set
  • I tried to do the same in the link function, but my scope's values are undefined
  • I also tried in the controller function, but my scope is also empty, plus I don't have access to attr or element.

var app = angular.module('myApp', []);
app.controller('myctrl', function($scope) {
  var vm = this;
  this.mymodel = 'Hello world !';
});

app.directive('mydirective', function () {
      return {
        restrict: 'A',
        replace: false,
        transclude: false,
        scope: {
          bindingvalue: '='
        },
        compile: function(element) {
          //remove the element for clarity
          element.removeAttr('mydirective'); 
        },
        link: function($scope, elem, attr) {
          //set the attribute
          attr.$set($scope.bindingvalue.propbind, $scope.bindingvalue.propmodel);
          //Observe the attribute value for changes
          attr.$observe($scope.bindingvalue.propbind, function(value) {
            $scope.bindingvalue.propmodel = value;
          })
        },
        controller: function($scope, $element) {
          //Nothing here yet
        }
      }
    });
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>

<div ng-app="myApp" ng-controller="myctrl as vm">
  <input mydirective="{propbind: 'data-value', propmodel: vm.mymodel}" type="text"/>
  model value : {{vm.mymodel}}
</div>

Can you please help me ? Is my logic good for my problem ? I think that if I manage to get my scope values in my link function, everythink could work.

Thanks

2 Answers 2

0

This should work :

var app = angular.module('myApp', []);
app.controller('myctrl', function($scope) {
  var vm = this;
  vm.mymodel = 'Hello world !';
});

app.directive('customInput', function () {
      return {
        restrict: 'EA',
        scope: {
          value: '='
        },
        template: '<input type="text" ng-model="value" />',
        controller: function($scope, $element) {
          //Nothing here yet
        }
      }
    });
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>

<div ng-app="myApp" ng-controller="myctrl as vm">
  <custom-input data-value="vm.mymodel"> </custom-input>
  model value : {{vm.mymodel}}
</div>

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, but unfortunately, I have to use the custom-input with its data-value. Your solution would replace my custom-input by a basic input linked with ng-model, right ?
I would like to, but I can't. It is a custom library of webcomponent which I don't have access to the sources. As the custom-input actually creates multiples div, I cannot just add a ng-model property (it would be placed on the top div instead of the actual input element). Plus there is a custom validation mechanism built in, but everything is based on the data-value attribute. I'm not sure if it is very clear, I struggle to explain it in english, sorry
0

I finally managed to get it working. Here's my solution :

var app = angular.module('myApp', []);
app.controller('myctrl', function ($scope) {
  var vm = this;
  vm.mymodel = 'Hello world !';
  vm.changeModel = function() {
    vm.mymodel = 'New Value'
    console.log(document.getElementById('myinput'))
  }
  vm.changeAttribute = function() {
    document.getElementById('myinput').setAttribute('data-value', '123456789');
    console.log(document.getElementById('myinput'))
  }
});

app.directive('mydirective', function ($timeout, $interval) {
  return {
    restrict: 'A',
    replace: false,
    transclude: false,
    scope: {
      propbind: '@',
      model: '='
    },
    link: function (scope, elem, attr) {
      attr.$set(scope.propbind, scope.model);

      //Watch model for changes, and update attribute
      scope.$watch(function () {
        return scope.model
      }, function () {
        attr.$set(scope.propbind, scope.model);
      });

      //Watch for changes in attribute, and update model
      scope.$watch(function () {
        return elem[0].getAttribute(scope.propbind);
      }, function (value) {
        $timeout(function () {
          scope.model = value;
        });
      });

      //Get attribute value for angularjs to reevaluate dom
      $interval(function () {
        var val = elem[0].getAttribute(scope.propbind)
      }, 100);
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>

<div ng-app="myApp" ng-controller="myctrl as vm">
  <input id="myinput" mydirective propbind="data-value" model="vm.mymodel" type="text"/>
  model value : {{vm.mymodel}}
  <div>
  <button ng-click="vm.changeModel()">change model value</button>
  <button ng-click="vm.changeAttribute()">change model value</button>
  </div>
  {{document.getElementById('myinput')}}
</div>

The demo uses a basic input instead of my custom input, so it is not actually bind to the data-value attribute. However, you can click the two buttons to update the model in the controller, or the data-value attribute. You can see that if I change the value in my controller, the data-value gets updated. Also, if you update the data-value of the input, the model gets updated in the controler.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.