【问题标题】:AngularJS dynamic additions to pageAngularJS 动态添加到页面
【发布时间】:2019-05-21 14:43:15
【问题描述】:

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

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

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

对这个单页应用进行硬编码效果很好。

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

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

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

    <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
              }
        }
      }
}]);

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

【问题讨论】:

  • 您要做的是嵌套的 angularjs 应用程序。这里有个问题:stackoverflow.com/questions/18184617/…。我使用 Martin Thurau 的评论使它起作用:这是他的示例:codepen.io/mthur/pen/QvaoQY。我个人使用这种系统来运行子应用程序,今晚会写一个完整的答案。
  • 你看过我之前写的吗?你需要我写一个答案还是你认为这不是你需要的?
  • 对不起,走了一整天......我创建了链接示例中的“插件”元素```
  • 我不知道你如何在 cmets 中格式化...用code 表示括号,但这不起作用
  • 上述代码 a[0].appendChild(ss) 将 div 树放在主应用程序 html 树的右侧元素中 var p = document.getElementById(plugin) angular.module(plugin, []); var e=angular.element(p) e.injector = function() { return false; } angular.bootstrap(e, [插件]);删除 e.injector(); `

标签: html angularjs


【解决方案1】:

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

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

我建议您也忘记使用ng-include,以及尝试在应用程序的指令中添加&lt;script&gt;&lt;/script&gt;

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

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

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

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

  • 从一开始就加载插件的脚本,它必须声明一个新的模块,如MyPlugin1
  • 在包含插件的指令中,输入我发送给您的链接代码,这样可以插入子应用程序。最后,您的指令模板中将有一个&lt;div ng-app="MyPlugin1"&gt;&lt;/div&gt;
  • 然后在该节点上调用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);
}

在指令中调用declarePlugin1("test", 42);,它将用初始化值声明应用程序MyPlugin1,然后angular.bootstrap告诉angularjs启动这个应用程序。

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

【讨论】:

  • 谢谢...我得出了同样的结论。我可以轻松地进行方法 2,但我想尝试使动态加载工作。认为这会很复杂......但我们不能进行所有这些串音显式通信。
  • 我现在在启动之前已经创建了 html 内容,所以它是“动态的”,因为没有人需要再编辑文件了
  • 不错。这不是“在飞行中”,但如果它成功了,那就太酷了:)
猜你喜欢
相关资源
最近更新 更多
热门标签