使用 Angular 加载部分页面并编译控制器

Posted

技术标签:

【中文标题】使用 Angular 加载部分页面并编译控制器【英文标题】:Loading Partial Page With Angular and Compile The Controller 【发布时间】:2012-10-10 19:41:53 【问题描述】:

在大型应用程序中,我们的 Web 应用程序可能会被组织成单独的部分页面,以增加我们应用程序的模块化。在某些情况下,使用 Angular $http.get 或 JQuery $.load 编译通过 XHR 或 Ajax 请求加载的部分页面会引入错误。

以我的场景为例,我正在使用 Kohana php 框架,因此我可以在服务器级别控制我的 Web 应用程序的模块化。像往常一样,所有模板和页面都分开显示,将所有 html、JS 和 CSS 放在表示层上。

这将为我在客户端处理上实现 javascript MVW/MVC 堆栈提供很大的灵活性,因为我的 Web 应用程序严重依赖 AJAX 请求来从后端应用程序获取数据。在我使用 AngularJS 的场景中,下面是关于如何将模型中的数据呈现给客户端的简单伪代码。

Kohana 模型 > Kohana 控制器 > Kohana 视图 > XHR > JQuery\Angular > DOM

在我的申请中,我的一部分确实让我大吃一惊,让我喝了几瓶新陈代谢饮料来解决申请。是我有一个模态对话框的地方,部分页面通过 XHR 从服务器加载并将其附加到选定的 DOM。

问题是当 Angular 尝试编译部分页面时,当它找到 ng-controller 指令时,它会寻找引用已处理指令的函数。由于 DOM 解析器尚未对其进行评估,因此在未找到控制器的情况下会产生错误。但是,当您在加载部分页面之前在应用程序中的某个位置预先声明该功能时,一切正常。下面是我如何设置对话框服务的示例,当我单击所述链接时,该服务将从链接指令中调用。

var dialogService = angular.module('dialog.service', []);
dialogService.factory('Dialog', function($http,$compile)
    var dialogService = ;
    dialogService.load = function(url, scope)
        $("#dialog:ui-dialog").dialog( "destroy" );
        $("#dialog").attr('title','Atlantis');

        $http.get(url).success(function (data) 
            html = $compile(data)(scope);
            $('#dialog-content').html(html);

            $("#dialog").dialog(
                width: '600px',
                buttons: 
                    "Ok": function() 
                        $( this ).dialog( "close" );
                        return true;
                    ,
                ,
                close: function()
                    if (typeof (onClose) == 'function')  onClose(); 
                ,
            );
        );
    

    return dialogService;
);

经过一些研究,我找到了一些解决方案,并与其他人分享了我对像我这样的初学者的答案。 (对不起我的英语)。

【问题讨论】:

【参考方案1】:

在这个设置上 AngularJS 没有任何问题,其他 JS 大师可能已经知道解决方案,并且非常忙于与我们分享,同时发明另一​​个很酷的 Web 开发工具或框架。没关系,继续这样做。这可能不是一个很酷或最后通牒的解决方案,请与我们分享任何改进或提示!

为了克服这个问题,我们需要设置策略,让我从一个示例代码开始,这样我们的大脑就会在信息流过时进行消化。下面的代码是我使用 JQuery 创建模态对话框的占位符,并将插入 Ajax 内容。

<div ng-app="asng" id="dialog" title="" style="display:none">
     <div id="dialog-content"></div>
</div>

作为基础知识,我们必须了解 DOM 解析器是如何工作的。我们可能认为DOMP(DOM Parser)是一个多线程的,这就是我们可以并行加载多个外部资源的原因。实际上,DOMP 在从上到下解析 DOM 元素索引时是单线程的。下面是我将加载到#dialog-content DIV 元素中的部分页面上的示例。

<script language="JavaScript" type="text/javascript">
    function Transaction ($scope,$http)
        $scope.items = ["country":"VN","quantity":"100"];
        $scope.country_name = $scope.items;
    
</script>

<style>
</style>

<div id="transaction-panel" class="user" data-ng-controller="Transaction">
        <form id=" form_name " action="">
        Country : [[ items.country ]] </br>
        Total : [[ items.quantity ]]
    </form>
</div>

实际上这些部分仍然给出错误,尽管我们已经将脚本块放在带有 ng-controller 指令的元素之前。实际上并非如此,我们需要解决的部分是 AngularJS 编译服务是如何编译部分 DOM 的。让我们回到上面的问题部分,检查我们在哪里进行编译。

html = $compile(data)(scope);
$('#dialog-content').html(html);

上面的第一行将编译数据变量中的 DOM,并插入到根 DOM 不幸的是第一行会报错:找不到控制器事务。

发生这种情况是因为,您的部分页面中的 脚本块 尚未被 DOMP 解析器评估,因为它没有插入到根 DOMP 中。现在你看到亮了,所以我们必须稍微改变一下编译策略,通过插入新的 DOM,然后我们将解析回插入的 DOM 示例如下:-

html = $('#dialog-content').html(data);
$compile(html)(scope);

简单而简单的解决方案,因为忽略了 DOM 解析的简单概念,我花了几个早上才解决这个问题。

【讨论】:

我正在尝试使用这种方法,但它告诉我 $compile 未定义,我需要添加其他内容吗? 我想知道你是如何初始化AngularJS的,你能在JsFiddle分享吗? 通过查看您的代码,我不知道您的代码是如何工作的,但这可能是因为您加载 js 的方式。你有没有使用任何脚本加载器?在运行应用控制器之前,您应该验证是否加载了 AngularJS。 有人有这个工作的具体例子吗?我在这个 SO 中有一个类似的问题:***.com/questions/19779687/…【参考方案2】:

如果我了解您要做什么,这里有一个简单的示例。

我想通过 AJAX 发布到 Django 表单,然后用返回的标记替换页面中的表单内容。返回的标记包括一个 ng-controller,我需要在它加载时执行它:

.controller('MyForm', function($element, $compile, $scope)
    var scope = $scope;
    var $theForm = $element;
    var $formBlock = $element.find('.the_form');  // is replaced by the form response
    $element.find('.submit_the_form').click(function()
        // submit the form and replace contents of $formBlock
        $.post($theForm.attr('action'), $theForm.serialize(), function(response)
            var newstuff = $formBlock.html(response);
            $compile(newstuff)(scope); // loads the angular stuff in the new markup
        );
    );
)

我认为您感兴趣的行是 $compile(newstuff)(scope);

编辑: Crikey,今天早上用其他一些标记尝试了这个,但没有奏效,我无缘无故弄明白了。原来,如果我没有在新标记中分配 ng-model 的字段,则 $compile 不会执行。补充:

<input type="hidden" name="dummy" value="0" ng-model="dummy"/>

...现在它可以编译了。

【讨论】:

以上是关于使用 Angular 加载部分页面并编译控制器的主要内容,如果未能解决你的问题,请参考以下文章

如何在页面加载时执行 AngularJS 控制器功能?

Visualforce 重新渲染和 Angular

在Angular Js中重新加载后,如何将复选框(选中或未选中)从javascript控制器更新到html页面?

CI - 使用 Javascript 按钮加载控制器

仅在 Angular SPA 中的页面加载之间缓存会话数据

Angular Electron,重新加载页面后出现白屏