2

I have a custom directive, and its purpose is to present a widget and bind it to a variable. Every variable has different data type, so different widgets will be presented depending on the data type.

My problem is that I can pass the data of the variable, but I can't manage to bind the widget to it.

To simplify the problem, my widget is just a simple text input. When I try to $compile the widget, Angular uses the value of the variable instead of binding to it.

HTML:

<body ng-app="app" ng-controller="myCtrl">
  <input type="text" ng-model="resource.name"></div>
  <div custom-widget widget-type="widget" bind-to="resource"></div>
</body>

Javascript:

angular.module('app', [])
  .directive('customWidget', function($compile) {
    return {
      replace: true,
      template: '<div></div>',
      controller: function($scope) {

      },
      scope: {
        bindTo: "=bindTo",
        widgetType: "=widgetType"
      },
      link: function(scope, iElem, iAttrs) {
        var html = '<div>' + scope.widgetType.label + ':<input ng-bind="' + scope.bindTo[scope.widgetType.id] + '" /></div>';
        iElem.replaceWith($compile(html)(scope));
      }
    };
  })
  .controller('myCtrl', function($scope) {
    $scope.widget = {
      id: 'name',
      label: 'Text input',
      type: 'text'
    };
    $scope.resource = {
      name: 'John'
    };
  });

Plunker demo: http://plnkr.co/edit/qhUdNhjSN7NlP4xRVcEA?p=preview

I'm still new to AngularJS and my approach may not be the best, so any different ideas are of course appreciated!

GuyB7
  • 606
  • 1
  • 6
  • 15

2 Answers2

1

Since you're using an isolate scope one issue is that resource is on the parents scope and not visible within the directive. And I think you're looking for ng-model rather than ng-bind.

Also, since you want to bind to namein resource, we need to tie that in somehow.

So here's one approach to your template html (note the addition of $parent to get around the scope issue and the addition of .name(which you could add programatically using a variable if you preferred, or specify it as part of the attribute))

var html = '<div>' + scope.widgetType.label + ':<input ng-model="' + '$parent.' + iAttrs.bindTo +'.name'+ '" /></div>';

Updated plunker

KayakDave
  • 24,636
  • 3
  • 65
  • 68
  • Thanks! Can you please explain what $parent means? Does it just points to the parent scope of the current one? – GuyB7 Dec 05 '13 at 19:02
0

Well, when you have a isolated scope within your directive and use the "=" operator you already have two-way data binding.

My suggestion would be to use the "template" more like a view so the operations are clearer.

I would change your directive to the following:

Using ng-model instead of ng-bing mainly because as the Documentation reveals:

The ngModel directive binds an input,select, textarea (or custom form control) to a property on the scope using NgModelController, which is created and exposed by this directive. [...]

Changed directive:

angular.module('app', [])
  .directive('customWidget', function($compile) {
    return {
      replace: true,
      template: '<div> {{widgetType.label}} <input ng-model="bindTo[widgetType.id]" /></div>',
      scope: {
        bindTo: "=bindTo",
        widgetType: "=widgetType"
      }
   };
 });

EDIT: Ops forgot the Updated Plunker

Lucas Lazaro
  • 1,516
  • 9
  • 13
  • It is a lot clearer, but my main goal is to have different custom widgets, depending on the type of the data. Is it possible to have different templates depending on widgetType.type? – GuyB7 Dec 05 '13 at 19:27
  • @GuyB7 Hmm, got it. Well it is possible to change the template according to the type, but that would involve using $http and $compile in the linking function to get the url's of the different widget [(like this dude suggests)](http://stackoverflow.com/questions/14632260/can-you-change-templateurl-on-the-fly), or maybe take a look at the [ngInclude directive](http://docs.angularjs.org/api/ng.directive:ngInclude). Sorry if it didn't help much, best of luck! Cheers. – Lucas Lazaro Dec 05 '13 at 19:43