开发者问题收集

如何从 AngularJS 模块/代码外部调用 AngularJS $compile?

2021-05-31
605

假设我的 HTML 带有 AngularJS 模块/控制器,如下所示:

angular
.module("myModule", [])
.controller("myController", ['$scope', '$compile', function ($scope, $compile) {
    $scope.txt = "<b>SampleTxt</b>";
    $scope.submit = function () {
        var html = $compile($scope.txt)($scope);
        angular.element(document.getElementById("display")).append(html);
    }
}]);

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myModule" >
    <div ng-controller="myController">
        <form name="myForm">
            <span>Age:</span><input type="number" name="age" ng-model="age"/>
            <textarea ng-model="txt" ></textarea>
            <input type="button" value="submit" ng-click="submit()" />
        </form>
        <div id="display"></div>
    </div>
</body>

上述示例允许在运行时使用 $compile 向 AngularJS 应用添加元素。

假设我想插入一个输入元素,例如文本框名称 driversLinces ,其属性为 ng-required="age > 21" ,假设我想从 JavaScript 控制台插入此元素,并附带条件必需功能,以进行测试和验证。此外,假设我想执行相同操作,但我想修改 ng-required 属性(如果存在 age 等现有元素),我该怎么做?

我正在考虑创建一个以某种方式访问​​ $compile 的函数,但不确定如何操作。您能帮助我吗?我只能从控制器内部访问 $compile 服务。

注意:由于某些限制和缺乏信息/资源,我对完整 HTML 代码的访问受到限制。我可以使用 UI Modeler 访问 HTML 和 AngularJS 表单。我可以添加自定义 HTML 代码,但我不知道是否可以用自己的自定义 HTML 容器封装现有表单部分,这是添加指令以访问内部部分所必需的。

我可以使用 angular.element() 访问 AngularJS 范围和 ng-form 元素。我可以在 Form-Load、单击按钮或模型值更改时触发 JavaScript。我可以添加表单元素并将其链接到 AngularJS 模型。我无法弄清楚如何从 JavaScript 访问 $compile 服务。

更新:

我将添加更多信息来解释我的目标或用例。 我想从 JavaScript 向 AngularJS 表单添加自定义验证规则和错误。我使用的平台使用 AngularJS,但不允许我轻松访问 AngularJS 代码来添加指令,或者至少目前,我没有为此目的所需的资源。但是,该平台使我能够通过单击按钮触发自定义 JavaScript 代码,该代码可以在表单加载时自动触发(on-load 事件)。此外,我可以传递被单击按钮的 ID。有了这个,我能够使用 angular.element('#id').scope() 访问范围。这使我能够访问几乎所有其他元素。我可以在范围对象中看到所有 ng-modelsng form controllers 及其所有父级。有时,我必须访问 $parent 才能到达根目录,但我认为最终我能够从范围对象访问几乎任何内容。

现在,我需要能够找到表单元素并添加自定义验证规则。我可以从范围对象遍历所有表单元素,并且可以弄清楚如何获取元素 ID 及其绑定详细信息。我现在需要的是如何添加自定义验证规则和表单加载事件上的错误消息。

例如,我可以使用 JSON 来表示 AngularJS 表单的验证规则,如下所示:

[
    {
        "id": "employee-name",
        "required": true,
        "msg": "Employee name is required."
    },
    {
        "id": "degree",
        "customValidation": "someJSFunctionName",
        "msg": "The provided degree is invalid. Please review the rules and try again."
    }
]

然后,在 form-load 事件中,我想在表单上加载上述规则并使其生效。这怎么可能呢?假设我有权访问范围对象,我只能使用 JavaScript,并且不能使用 AngularJS 指令。


更新 2:

根据下面 PhineasJ 提供的答案,我使用以下命令的控制台:

var injector = window.angular.injector(['ng']);
var $compile = injector.get('$compile');
var elm = angular.element("my-element-selector");
var elmScope = elm.scope();
elm.attr('ng-required', true);
var elmCompile = $compile(elm[0])(elmScope);

虽然上述操作没有引发任何错误,但它并没有正常工作。如果我将字段 elm 留空,它不会触发 ng-required 错误,但我可以看到在执行 $compile 命令后添加了 required 属性。我注意到每次更新字段值时都必须执行 $compile 服务,这样验证规则才会反映出来,但我没有看到字段的 ctrl.$error 对象被更新。它始终为空。

然后我尝试了以下操作:

var injector = window.angular.injector(['ng', 'myApp']);
var $compile = injector.get('$compile');

... 我收到错误 未捕获错误:[$injector:unpr] 未知提供程序:$rootElementProvider

然后我尝试了以下操作:

var mockApp = angular.module('mockApp', []).provider({
  $rootElement:function() {
     this.$get = function() {
       return angular.element('<div ng-app></div>');
    };
  }
});
var injector = window.angular.injector(['ng', 'mockApp', 'myApp']);

... 第一次没有抛出任何错误,但是当我再次尝试时,我收到错误 视图引擎已初始化,无法进一步扩展 。所以我只能使用 $compile 服务。

我确实尝试直接使用 $validators() 添加规则,并且成功了。请参阅以下详细信息:

//The elm form controller is found on the $parent scope and this is beyond my control.
//The ng-form element names are generated by the back-end and I have no control over this part. In this case the HTML element 'elm' is the form element name 'ewFormControl123'.
elmScope.$parent.ewFormControl123.$validators.required = 
    function (modelValue, viewValue) {
        console.log(modelValue, viewValue);
        var result = !!(modelValue??null);
        console.log("result = ", result);
        return result;
    }

上述方法似乎运行良好,但是,我仍然对使用 $compile 感兴趣,方法是将验证规则或指令注入 HTML 代码,然后在该元素上运行 $compile 服务。我认为将所需部分注入 HTML 并运行 $compile 更好。

更新 3

在 @PhineasJ 的帮助下,我设法准备了一个使用 AngularJS 注入器$compile 服务的小示例。这可以成功运行,但是相同的方法在目标应用程序上不起作用。

w3school 原始示例: https://www.w3schools.com/angular/tryit.asp?filename=try_ng_ng-required

JS Fiddle 示例: https://jsfiddle.net/tarekahf/5gfy01k2/

按照这种方法,只要有一个选择器来抓取元素,我应该能够在运行时为任何字段加载验证规则。

现在,我正在努力解决在目标应用程序上应用相同方法时遇到的错误。我有两个问题:

  1. 如果我使用 const injectionor = angular.injector(['ng', 'myApp']) 和应用程序名称,我会收到错误: 未捕获错误:[$injector:unpr] 未知提供商:$rootElementProvider
  2. 如果我不添加应用程序名称,则不会引发任何错误,但不遵守验证规则。

但是,如果我使用 formController.$validators 对象,我可以添加验证规则,并且它会被遵守。我不确定为什么没有人推荐这种方法。

感谢您的帮助和反馈。

更新 4

我找到了解决方案。查看我下面添加的答案。

Tarek

2个回答

修复方法是使用以下命令来获取注入器:

var injectionor = angular.element("#myAngularAppID").injector();

其中 myAngularAppID id 是定义 ng-app 的 HTML 元素的元素 ID。

注入器语句的以下变体不起作用:

//Without using the app
var injector = window.angular.injector(['ng']);
//With the app
var injector = window.angular.injector(['ng', 'myApp'])

实施上述修正后,问题就解决了。

特别感谢@PhineasJ。此外,以下参考资料对我有帮助:

  1. Injector 返回未定义的值?
  2. angularjs 编译 ng-controller 和插值

查看 JS Fiddle,它具有使用 $compile 动态添加 HTML 元素并将其绑定到 AngularJS 范围的所有可能变体:

https://jsfiddle.net/tarekahf/5gfy01k2/

tarekahf
2021-08-03

要在 AngularJS 之外调用 $compile 函数,您可以使用:

const injector = window.angular.injector(['ng', 'your-module']);
injector.invoke(function($rootScope, $compile) {
  $compile('<div custom-directive></div>')($rootScope);
})

要添加动态验证规则,您可以参考 自定义验证 。 正如您提到的, controller Scope 也可以从 angular.element().scope 中检索。

这是一个关于如何动态加载验证器并对其进行编译的演示。 https://plnkr.co/edit/4ay3Ig2AnGYjLQ92?preview

PhineasJ
2021-07-23