【问题标题】:How can I augment an isolate directive scope before a directive-controller is instantiated?如何在实例化指令控制器之前增加隔离指令范围?
【发布时间】:2015-08-19 16:23:27
【问题描述】:

我正在编译和链接具有隔离范围的指令(请注意,这是出于此问题范围之外的原因手动编译和链接):

    outerElement = angular.element(domElement);
    $injector = outerElement.injector();
    $compile = $injector.get('$compile'); 
    getTheIsolateScopeForMyDirectiveInstance().myProperty = 'foo'; // Pseudocode. I want myProperty to be available on the scope from inside the controller constructor function.
    link = $compile(angular.element('<my-directive></my-directive>'));
    // IIUC, the following line will instantiate 
    // the controller for the directive, injecting 
    // the isolate scope. I want to augment the 
    // isolate scope that is injected *before* it 
    // is injected. 
    // The value to augment the scope resides in 
    // *this* execution context.
    // How can I do so?
    renderedElement = link(outerElement.scope());
    element.append(renderedElement);

MyDirective 有一个隔离作用域(我想扩充的那个),以及一个与之关联的控制器。

MyDirectiveController 控制器利用注入器来注入其隔离作用域。

MyDirectiveController.$inject = [ '$scope' ];

我想扩大隔离范围注入MyDirectiveController 的实例之前,其值仅在运行时在上述代码的执行上下文中知道。

我该怎么做?

我的指令

function MyDirective() {
    return {
        scope: {}, // I want to augment this before it is injected into  MyDirectiveController
        restrict: 'E',
        template: template,
        controller: 'myDirectiveController',
        controllerAs: 'ctrl',
        replace: true,
    };
}

MyDirectiveController

function MyDirectiveController($scope) {
    console.log($scope.myProperty); // Should be 'foo'.
}

MyDirectiveController.$inject = ['$scope'];

如果这是不可能的,是否有另一种方法可以使控制器和/或隔离指令范围内的实例特定信息可用?

我现在能想到的唯一方法是扩大上面提供给link(outerElement.scope()) 的范围,然后在指令的隔离范围上定义= 属性。

编辑:

这就是我现在正在做的事情,myProperty 值最终出现在控制器的隔离范围的父级上:

var isolate = outerElement.scope().$new(true);
isolate.myProperty = 'foo';
renderedElement = link(isolate);
element.append(renderedElement);

鉴于此,当 MyDirectiveController 被实例化时:

function MyDirectiveController($scope) {
  $scope.myProperty; // undefined
  $scope.$parent.myProperty; // 'foo'
}

【问题讨论】:

    标签: javascript angularjs


    【解决方案1】:

    这里的假设

    控制器 MyDirectiveController 利用注入器具有 注入了它的隔离作用域。

    MyDirectiveController.$inject = ['$scope'];

    我想在将隔离范围注入到 MyDirectiveController 的实例,其值仅在 在上面代码的执行上下文中运行时。

    错了。 $inject 只是注解,只有缩小代码才会有不同。

    在指令被链接之前,你不能将任何东西“注入”到隔离作用域中。一些丑陋的猴子补丁可以解决问题:

      var _new = scope.$new;
      scope.$new = function () {
        return angular.extend(_new.apply(this, arguments), {
          myProperty: 'foo'
        })
      };
      $compile(angular.element('<my-directive></my-directive>'))(scope);
      scope.$new = _new;
    

    但即使可能,它也适合编写测试,但不适用于生产代码。

    这里唯一直接的方法(以及使用隔离作用域的原因)是

    function MyDirective() {
        return {
            scope: {
                myProperty: '='
            },
            ...
        };
    }
    

    $compile(angular.element('<my-directive my-property="myProperty"></my-directive>'))(scope);
    

    其中scope.myProperty 等于“foo”。

    【讨论】:

    • 我也得出了同样的结论。但我还有另一个问题:即使使用父作用域绑定方法 { myProperty: '=' },myProperty 在指令隔离作用域上仍然是undefined。即使isolateScope.$parent.myProperty === 'foo'。这可能是我手动编译或链接指令的方式的问题吗?编辑:啊哈,属性需要在 DOM 节点中。
    • 是的,这是一个陷阱:控制器是从绑定属性中获取范围变量的不好地方,只是had an answer。来自隔离范围的插值myProperty 将存在于预链接和(后)链接中,但不在控制器中。但是,如果您在控制器中执行$scope.$watch('myProperty', ...),这不是问题。假设数据绑定在 Angular 中会随着时间而改变(事实上确实如此),这是一个好习惯。
    【解决方案2】:

    UPDATE2:如果您需要隔离范围,请创建new scope

     var myparentscope = outerElement.scope()
     var myscope = myparentscope.$new(true) // or $rootScope.$new(true, myparentscope), see true here, it isolates scope from parent.
     myscope.prop = 'new prop' // define on child scope.
     renderedElement = link(myscope);
     element.append(renderedElement);
    

    更新: 代码中的最后四行:

    getTheIsolateScopeForMyDirectiveInstance().myProperty = 'foo'; 
    link = $compile(angular.element('<my-directive></my-directive>'));
    renderedElement = link(outerElement.scope());
    element.append(renderedElement);
    

    应该是:

    var myscope = outerElement.scope()
    scope.myProperty = 'foo'
    renderedElement = link(myscope);
    

    原答案

     .directive('myDirective',['$compile', function($compile) {
          return {
             link:link,
             scope: {
             }
          }
    
          function link(scope, element) {
             scope.prop = 'new value'  // new property on scope           
             var renderedElement = $compile('... html ...')(scope);
             element.append(renderedElement);
          }
     })
    

    还有

    $compile = $injector.get('$compile'); // 我假设这个实例化了 在幕后与 myDirective 关联的控制器?

    $inject.get('$compile') 是获取$compile 服务的复杂方式。如果编译服务不(并且它不)依赖于您的服务,您可以像通常使用依赖注入一样指定它。

    另外我认为你需要阅读这部分关于指令生命周期中的编译和链接步骤。

    编译步骤不是$compile 服务。编译步骤在应用程序的每个生命周期执行一次,将其视为使用指令的所有实例的准备。

    链接步骤(前链接和后链接)正在获取您准备好的指令并实际将其实例化为您应用中的特定位置/范围等。

    由于您需要更新范围 - 这是您要使用的链接步骤。

    关于该主题的好视频 - https://egghead.io/lessons/angularjs-compile-pre-and-post-link

    【讨论】:

    • 我正在手动驱动一个指令的渲染,该指令在角度应用程序引导后的某个任意点可用,因此是卷积。如果您知道更好的方法,请告诉我。我的用例 AFAIK 禁止常规使用注射器。
    • @BenAston 我不知道你为什么不能只使用链接。无论如何,它只会在您第一次实际呈现指令时发生。此外,如果您的问题更大 - 创建 js fiddle 或更多细节的东西。
    • "编译步骤不是 $compile 指令" - 这肯定是指令的编译,准备使用。在这种情况下,这是正在运行的 Angular 应用程序第一次看到该指令的实例。
    • @BenAston $compile 服务 - 这是错字。
    • 好的,我没有完全理解你的答案。我会试试你的建议。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多