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

How to invoke AngularJS $compile from outside AngularJS module/code?

提问人:tarekahf 提问时间:6/1/2021 最后编辑:tarekahf 更新时间:8/4/2021 访问量:436

问:

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

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 应用程序添加元素。

假设我想插入一个输入元素,例如带有属性的文本框名称,假设我想从 JavaScript 控制台插入这个具有条件必需功能的元素,以便进行测试和验证。另外,假设我想做同样的事情,但我想修改属性,如果现有元素(例如 ,我该怎么做?driversLincesng-required="age > 21"ng-requiredage

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

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

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

更新:

我将添加更多信息来解释我的目标或用例。 我想从 JavaScript 向 AngularJS 表单添加自定义验证规则和错误。我正在使用的平台使用 AngularJS,但不允许我轻松访问 AngularJS 代码来添加指令,或者至少目前,我没有为此目的所需的资源。但是,这个平台使我能够在单击按钮时触发我的自定义 JavaScript 代码,该按钮可以在表单加载时自动触发(加载事件)。此外,我可以传递单击的按钮的 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."
    }
]

然后,在表单加载事件中,我想将上述规则加载到表单上并使其生效。这怎么可能?考虑到我可以访问范围对象,我只能使用 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);

虽然上述内容没有引发任何错误,但是,它并没有正常工作。如果我将该字段设置为空,它不会触发错误,尽管我可以看到该属性是在执行命令后添加的。我注意到每次更新字段值时都必须执行服务,以便验证规则反映出来,但我没有看到字段的对象正在更新。它始终是空的。elmng-requiredrequired$compile$compilectrl.$error

然后我尝试了以下方法:

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

...我收到错误.Uncaught Error: [$injector:unpr] Unknown provider: $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']);

...第一次没有抛出任何错误,但是当我再次尝试时,我得到了错误。所以我坚持这项服务。The view engine is already initialized and cannot be further extended$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;
    }

以上似乎工作正常,但是,我仍然有兴趣通过将验证规则或指令注入 HTML 代码,然后在该元素上运行服务来使用。我认为将所需的部分注入 HTML 并运行会更好。$compile$compile$compile

更新 3

在@PhineasJ的帮助下,我设法准备了一个使用和服务的小样品。这工作很成功,但相同的方法在目标应用程序上不起作用。AngularJS injector$compile

W3School 原始样本:https://www.w3schools.com/angular/tryit.asp?filename=try_ng_ng-required

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

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

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

  1. 如果与应用名称一起使用,则会出现以下错误:const injector = angular.injector(['ng', 'myApp'])Uncaught Error: [$injector:unpr] Unknown provider: $rootElementProvider
  2. 如果我不添加应用名称,则不会引发错误,但不会遵守验证规则。

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

感谢您的帮助和反馈。

更新 4

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

塔里克

JavaScript AngularJS 验证 编译 angularjs-forms

评论

1赞 charlietfl 6/1/2021
您需要$compile外部控制器上下文访问哪些用例?您是否考虑过自定义指令甚至“ng-include”?
0赞 tarekahf 6/1/2021
我在一个使用 AngularJS 的平台上,上面有一个额外的层。我没有时间和资源来挖掘和弄清楚如何添加控制器和指令,因为这将需要很长时间,而且我认为我无法将 HTML 链接到指令。我可以轻松编写 JavaScript 函数,并且可以使用脚本按钮访问活动页面上的元素。我能够访问使用 .我需要修改和形成某些 HTML 现有元素的 JavaScript。我希望能够从控制台测试它并在表单加载时触发它。scopeangular.element()ng-readonlyng-required
0赞 charlietfl 6/1/2021
将 html 链接到指令的多种方法。远程模板文件、模板函数、模板字符串、$templateCache服务等指令也可以使用 $compile
0赞 charlietfl 6/1/2021
最坏的情况是,你可以有一个编译函数来访问,但这对我来说似乎很笨拙angular.element().scope
0赞 tarekahf 6/1/2021
...继续。。。现有的 HTML 和 AngularJS 表单可以通过建模器 UI 访问(我无法修改原始 HTML 源代码)。我还可以在页面上添加自己的自定义 HTML 代码,并且可以在页面加载时触发 JavaScript 代码,如果模型变量值发生变化,并且单击按钮即可。我可以添加元素并将它们链接到.我不知道我是否可以将现有的 HTML 表单部件与我自己的客户 HTML 容器一起包含,这是将其链接到指令所必需的。我需要尽快做一些事情,因为我没有太多的试错空间。nd-model

答:

1赞 PhineasJ 7/23/2021 #1

要在 AngularJS 外部调用$compile函数,yon 可以使用:

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

若要添加动态验证规则,可以引用自定义验证。 也可以从您提到的中检索。controller Scopeangular.element().scope

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

评论

0赞 tarekahf 7/24/2021
匪夷所思!我将使用您提供的信息摆弄测试模型。你是说我可以使用从中检索到的范围来修改表单字段验证规则吗?我注意到我可以从范围访问angular表单元素/控制器,但我不知道是否可以修改验证规则。您为自定义验证提供的参考是指通过在 HTML 中添加规则并使用指令来控制它们来进行自定义验证,而我无权访问这些规则。您能提供您的反馈吗?angular.element.scope
0赞 PhineasJ 7/26/2021
你好,@tarekahf。我认为您可以加载验证指令 javascript 文件,然后使用 $compile 函数动态链接指令。我修改了答案,以提供基于官方验证器演示的演示。
0赞 tarekahf 7/27/2021
我试过了,但我得到了错误。我现在正在研究它,看起来我必须在初始化 AngularJS 应用程序时可用。同时,如果你能告诉我如何做到这一点,那就太好了。const injector = window.angular.injector(['ng', 'your-module']);Uncaught Error: [$injector:unpr] Unknown provider: $rootElementProvider$rootElementProvider
0赞 tarekahf 7/27/2021
你好@PhineasJ,我在这里找到了这个答案 stackoverflow.com/a/28400389/4180447。按照控制台中的步骤操作,我第一次没有收到错误,我尝试再次重复这些步骤,现在我收到错误。步骤: 1. 2.Uncaught Error: The view engine is already initialized and cannot be further extendedvar mockApp = angular.module('mockApp', []).provider({ $rootElement:function() { this.$get = function() { return angular.element('<div ng-app></div>'); }; } });var injector = window.angular.injector(['ng', 'mockApp', 'oneModule']);
0赞 tarekahf 7/27/2021
感谢您的帮助,或者让我知道我可以创建一个新问题。我将很快用最新状态更新问题。
1赞 tarekahf 8/4/2021 #2

解决方法是使用以下命令获取喷油器:

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

其中 id 定义 HTML 元素的元素 ID。myAngularAppIDng-app

injector 语句的以下变体不起作用:

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

一旦实施了上述更正,问题就解决了。

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

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

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

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