首页 > 解决方案 > AngularJS 动态添加到页面

问题描述

我们在电子浏览器中有这个 AngularJS SP 应用程序(智能镜像),它具有用户可创建的扩展。

扩展是使用角度指令并使用控制器和服务的 html 小片段。

要安装扩展,必须编辑主页并插入用于控制器和服务功能的脚本标签以及<div ng-include= ...>用于 HTML 片段的

硬编码这个单页应用程序效果很好。

但我想向这个应用程序(开源)添加功能以某种方式动态加载这些元素......

将标签添加到 dom 有效,但未正确处理。在运行脚本(来自插入的标签)之前处理 HTML,当 ng-include 插入 HTML 片段时,控制器尚未定义......

正文(硬编码位置的扩展被注释掉)

    <body ng-controller="MirrorCtrl" ng-cloak> 
          <div class="top">
            <div class="top-left">
            <!--  <div ng-include="'plugins/datetime/index.html'"></div>
              <div ng-include="'plugins/calendar/index.html'"></div> -->
            </div>
            <div class="top-right">
            <!--  <div ng-include="'plugins/weather/index.html'"></div>
              <div ng-include="'plugins/traffic/index.html'"></div>
              <div ng-include="'plugins/stock/index.html'"></div>
              <div ng-include="'plugins/tvshows/index.html'"></div>
              <div ng-include="'plugins/ha-display/index.html'"></div> -->
            </div>
          </div>
    ...
    ...
    <script src="filename.service"/>
    <script src= filename.controller"/>
</body>

日历扩展 html(插入页面的特定 div 区域)

<ul ng-controller="Calendar" class="calendar fade" ng-show="focus == 'default'" ng-class="config.calendar.showCalendarNames ? 'show-calendar-names' : ''">
    <li class="event" ng-repeat="event in calendar" ng-class="(calendar[$index - 1].label != event.label) ? 'day-marker' : ''">
        <div class="event-details">
            <span class="day">
                      <span ng-bind="event.startName"></span>
            <span ng-if="event.startName != event.endName"> - <span ng-bind="event.endName"></span></span>
            </span>
            <div class="details calendar-name" ng-bind="event.calendarName"></div>
            <span class="summary" ng-bind="event.SUMMARY"></span>
            <div class="details" ng-if="event.start.format('LT') != event.end.format('LT')">
                <span ng-if="event.startName != event.endName"><span ng-bind="event.start.format('M/D')"></span> <span ng-bind="event.start.format('LT')"></span>                - <span ng-bind="event.end.format('M/D')"></span> <span ng-bind="event.end.format('LT')"></span></span>
                <span ng-if="event.startName == event.endName"><span ng-bind="event.start.format('LT')"></span> - <span ng-bind="event.end.format('LT')"></span></span>
            </div>
            <div class="details" ng-if="event.start.format('LT') == event.end.format('LT')">All day</div>
        </div>
    </li>
</ul>

日历扩展控制器(由 html 使用)

function Calendar($scope, $http, $interval, CalendarService) {

    var getCalendar = function(){
        CalendarService.getCalendarEvents().then(function () {
            $scope.calendar = CalendarService.getFutureEvents();
        }, function (error) {
            console.log(error);
        });
    }

    getCalendar();
    $interval(getCalendar, config.calendar.refreshInterval * 60000 || 1800000)
}
console.log("registering calendar controller")
angular.module('SmartMirror')
    .controller('Calendar', Calendar);

日历扩展服务(由控制器使用,在此讨论中缩短)

(function () {
    'use strict';

    function CalendarService($window, $http, $q) {
  ...
  ...
        return service;
    }
console.log("registering calendar service")
    angular.module('SmartMirror')
    .factory('CalendarService', CalendarService);

} ());

所以想要添加扩展的用户必须创建这些文件,编辑主页 HTML 并插入它们

<div ng-include src="filename.html"></div>

在正确的位置,然后添加

<script src="filename.service" >

<script src="filename.controller">

在正确的位置和顺序中,服务需要在控制器之前完成,因为控制器使用服务。

无论如何,很容易添加代码来定位所有扩展并将元素动态插入到 dom 中各自的位置......但是......

在硬编码中,脚本是在正文中的 html 之后添加的

所以,我添加了一个新脚本(在加载页面时处理),它定位并插入所有元素以在正确的位置支持扩展..

然后脚本结束......(硬编码HTML中的最后一个)并且HTML指令被处理并繁荣,动态添加的脚本尚未加载或处理,因此找不到控制器......

我可以创建一个包含所有这些信息的临时 HTML 文件并加载它而不是处理动态加载,但我认为最好解决这个问题

我尝试创建自己的角度指令并将其编译,但陷入循环

<divinc src="filename.service"></divinc>

插入的 div 是正确的,作为 divinc 指令的子项

angular.module('SmartMirror')
.directive("divincl", ["$compile" ,function($compile){
      return {
        priority: 100, 
        terminal: true,
        compile: function(scope, element, attrs) {
                var html =  "<div ng-include=\"" + element['incl']+ "\" onload='function(){console.log(\'html loaded\')}'></div>"

                var templateGoesHere = angular.element(document.getElementById(element['id']));
                templateGoesHere.html(html);
                //document.body.innerHTML='';
               var v= $compile(templateGoesHere);
                //scope.$apply();
              return function linkFn(scope) {
                 v(scope) // Link compiled element to scope
              }
        }
      }
}]);

关于如何解决这个问题的建议..谢谢

标签: htmlangularjs

解决方案


为了使 angularjs 1.7 应用程序动态加载扩展,有两种方法:

  • 要么使用“嵌套的 angularjs 应用程序”,这显然是 angularjs 的高级用法,并且需要您在 2 个 angularjs 应用程序之间进行通信,使用 $scope.$apply 告诉另一个应用程序进行更新等。
  • 要么不要在前端动态加载它们,而是在生成包含应用程序的 html 页面时在后端加载它们。尝试从一开始就列出所有扩展名。

我建议您忘记使用ng-includetoo 以及尝试在<script></script>应用程序的指令中添加的事实。

首先,你需要重新理解一个 angularjs 应用程序是如何启动的。

  • 当您加载您的主应用程序时,您有一个脚本,其中进行了angular.module, angular.directive, angular.value, angular.config, angular.run... 调用。这是声明步骤
  • 如果你声明了一个模块MyApp并且在你的 html 中有一个 DOM 元素ng-app="MyApp",angularjs 将自动angular.bootstrap()在这个 DOM 元素上运行以便启动MyApp。应用程序从这里execution开始。您不能再在模块中声明任何内容MyApp

其次,我认为<script></script>模板内的代码已被 Angular 清理和删除。另外,即使你执行了代码,由于declaration step已经完成,你不应该创建新的指令或注册新的服务,它不会工作。

一个好方法是,当您加载插件时,您:

  • 从一开始就加载插件的脚本,它必须声明一个新模块,如MyPlugin1.
  • 在包含插件的指令中,输入我发送给您的链接代码,这样可以插入子应用程序。最后,您将<div ng-app="MyPlugin1"></div>在指令的模板中拥有一个
  • 然后调用angular.bootstrap该节点,这将使启动子应用程序成为可能。

如果你这样做,你可以运行子应用程序,但你没有传递参数。为了给它传递参数,你可以把模块的代码MyPlugin1放在一个函数里面,以便有一个应用工厂。然后用于app.value('param1', parameter1)初始化应用程序。

例如:

function declarePlugin1(myParam1, myParam2) {
  var app = angular.module('MyPlugin1', []);
  // app.directive();

  app.value('myParam1', myParam1);
  app.value('myParam2', myParam2);
}

在指令 call 内部declarePlugin1("test", 42);,它将MyPlugin1使用初始化值声明应用程序,然后angular.bootstrap告诉 angularjs 启动该应用程序。

您也可以传递回调,以便在 2 个应用程序之间进行通信。


推荐阅读