简体   繁体   中英

Dynamic controller for directives with ECMA6

I'm trying to set a controller dynamically to my directive using the name property. So far this is my code.

html

<view-edit controller-name="vm.controller" view="home/views/med.search.results.detail.resources.audios.html" edit="home/views/med.media.resources.edit.html"></view-edit>

js

export default class SearchResultsCtrl extends Pageable {

  /*@ngInject*/
  constructor($injector, $state, api) {

    super(
      {
        injector: $injector,
        endpoint: 'mediaMaterialsList',
        selectable:{
          itemKey: 'cid',
          enabled:true,
          params: $state.params
        },
        executeGet: false
      }
    );

    this.controller = SearchResultsResourcesAudiosCtrl;
  }
}

Directive

export default class ViewEditDirective {

  constructor() {
    this.restrict = 'E';
    this.replace = true;
    this.templateUrl = 'home/views/med.view.edit.html';
    this.scope = {};
    this.controller = "@";
    this.name = "controllerName";
    this.bindToController = {
      'view': '@?',
      'edit': '@?'
    };

    this.open = false;
    this.controllerAs = 'ctrl';
  }
}

I get undefined for vm.controller . I guess that it's rendering before the controller can assign the controller to the variable (I debbuged it, and it's setting the controller in the variable).

I'm following this answer to achieve this, but no luck so far. How to set the dynamic controller for directives?

Thanks.

The problem is not related to ES6 (which is a sugar syntax coating over ES5), this is how Angular scope life cycle works.

This directive may show what's the deal with attribute interpolation

// <div ng-init="a = 1"><div sum="{{ a + 1 }}"></div></div>
app.directive('sum', function () {
  return {
    scope: {},
    controller: function ($attrs) {
      console.log($attrs.sum) // {{ a + 1 }}
      // ...
    },
    link: function (scope, element, attrs) {
      console.log(attrs.sum) // 2
    }
  };
});

And $attrs.sum may still not be 2 in link if a value was set after that (ie in parent directive link ).

It is unsafe (and wrong per se) to assume that the value on one scope can be calculated based on the value from another scopes at some point of time. Because it may be not. That is why watchers and data binding are there.

All that controller: '@' magic value does is getting uninterpolated attribute value and using it as controller name. So no, it won't interpolate controller name from vm.controller and will use 'vm.controller' string as controller name.

An example of a directive that allows to set its controller dynamically may look like

// dynamic-controller="{{ ctrlNameVariable }}"
app.directive('dynamicController', function () {
    return {
        restrict: 'A',
        priority: 2500,
        controller: function ($scope, $element, $attrs, $interpolate, $compile) {
            var ctrlName = $interpolate($attrs.dynamicController)($scope);

            setController(ctrlName);
            $attrs.$observe('dynamicController', setController);

            function setController (ctrlName) {
                if (!ctrlName || $attrs.ngController === ctrlName) {
                    return;
                }

                $attrs.$set('ngController', ctrlName);
                $compile($element)($scope);
            }
        }
    };
});

with all the side-effects that re-compilation may bring.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM