【问题标题】:AngularJS UI Router: Where to include the controller scriptAngularJS UI 路由器:在哪里包含控制器脚本
【发布时间】:2024-04-20 10:00:01
【问题描述】:

我有一个非常简单的 AngularJS SPA,使用 UI Router 在两个视图之间切换。单击主页链接后,用户将看到一行文本。 About 链接应该做同样的事情,但它的路由被配置为使用 templateUrl 而不是模板字符串:

  $stateProvider
  .state("home", {
    url: "/home",
    template: "<h3>This is the Home content</h3>"
  })
  .state("about", {
    url: "/about",
    templateUrl: "about.html",
    controller: "aboutController"
  });

about.html 尽可能简单:

<!DOCTYPE HTML>
<html ng-app="simpleApp">
<head>
  <title>About</title>
  <script src="aboutcontroller.js"></script>
</head>
<body ng-controller="aboutController">
  <h3>{{message}}</h3>
</body>
</html>

... 它的控制器也是普通的人为样板:

console.log("Registered aboutController"); ///////////
angular.module("simpleApp")
.controller("aboutController", ["$scope", function ($scope) {
  console.log("Created aboutController"); ///////////
  $scope.message = "This is the About content!";
}]);

注意帮助我诊断问题的两个控制台语句。

问题:当控制器脚本文件执行时,控制器本身从未被调用。因此,用户不会看到文本,而是看到:

{{消息}}

这似乎与我在模板文件(about.html)中包含脚本文件有关。如果我将脚本标签移动到 index.html,问题就会消失,一切都按预期工作。

Here's a plunker that shows my problem and workaround.

真正的问题: 在这个非常简单和人为的示例中,将所有控制器脚本包含在 SPA 的主页中是合理的。但即使是一个非常简单的 Web 应用程序,也可能有几十个页面和几十个控制器。将所有控制器添加到主页不会很好地扩展性能,也不会很好地扩展以进行维护。

那么设置它的适当方法是什么?应该如何/在哪里包含控制器脚本?

编辑:我忘了提及我收到的错误(是的,我知道)。使用 about.html 中的脚本标签,我收到一条消息,说

参数 'aboutController' 不是函数,未定义

这直接发生在控制台消息“Registered aboutController”之后。如果我移动脚本标记以将控制器包含在 index.html 中,则不会显示错误。同样,对我来说,这表明控制器根本无法及时调用。移到 index.html,在需要的时候已经完全注册了。

【问题讨论】:

  • 如果您在页面上看到字面意义上的{{message}},这意味着该模板根本没有被处理。如果 Angular 确实处理了模板,那么如果没有填充 message,您将什么也看不到。这表明某些东西会产生致命错误。你检查过你的 Javascript 控制台是否有错误?
  • 为什么你的 about 模板包含一个完整的 HTML 文件?它应该只包含与特定视图相关的 sn-p。您也不能在文件中使用ng-controller,因为您已经在路由器中使用controller: ...。您必须将带有控制器的文件包含到您的主文件中,作为您的主 module 的依赖项,否则路由器在尝试加载您的路由时找不到控制器。
  • @deceze 我花了一些时间在 plunker 的 readme.md 文档中制作了一个更完整的描述,但不知何故忘记在这里提及错误。是的,有一个错误说 aboutController 不是一个函数。这直接显示在我的控制台消息“Registered aboutController.”之后。如果我将脚本标签移动到 index.html,消息就会消失。因此,即使注册控制器的脚本及时执行,也无法创建实际的控制器。我会把这个添加到问题中。感谢您指出。
  • @deceze 我在模板中有一个完整的 HTML 文件,主要是因为那是我完成所有修改的地方。原版只有script和h3标签,但对结果没有影响。
  • @deceze 关于使用 ng-controller:同样,这是修补的结果,对结果没有影响(经过测试)。您所说的这一部分:“您必须将带有控制器的文件包含到主文件中,作为主模块的依赖项,否则路由器在尝试加载路由时找不到控制器”是完美的我发现了什么的描述。不过,我认为这是一种解决方法,因为(正如我在帖子末尾提到的)该策略根本无法很好地扩展。

标签: javascript angularjs controller angular-ui-router


【解决方案1】:

经过数小时的研究,我想通了。这些是我需要做的事情:

  1. requireJS,让我可以在运行时轻松加载脚本。这最终是一项巨大的工作量,因为 一切 都需要动态加载,并且我需要为我们所有的指令和第三方控件设置依赖关系。你可以不用 requireJS,但它根本不值得麻烦。
  2. 更改状态调用以使用resolve() 提供控制器。这实际上是一个 hack,因为 resolve() 函数实际上是为了加载控制器的依赖项,但此时加载控制器的脚本就可以了。更简单的方法是使用angularAMD,它提供了一个简单的包装器,类似于下面的 sn-p。不幸的是,它没有让您有机会从参数动态构建控制器名称,所以我最终自己重写了 resolve。如果您的需求更简单,那么使用 angularAMD 是显而易见的选择。

这是正常指定视图时的样子:

$stateProvider.state("home", {
    url: "",
    views: {
    // Other views...
      "client": {
        templateUrl: "clientselection.html",
        controller: "ClientSelectController",
        controllerUrl: "Controllers/ClientSelect.controller"
      }
    }
  });

这与 angularAMD 相同。不同之处在于控制器现在是按需加载:

$stateProvider.state("home", {
    url: "",
    views: {
    // Other views...
      "client": angularAMD.route({
        templateUrl: "clientselection.html",
        controller: "ClientSelectController",
        controllerUrl: "Controllers/ClientSelect.controller"
      })
    }
  });

荣誉奖必须发给Dan Wahlin's terrific blog post。它不使用 UI 路由器,但概念是可转移的。不幸的是,这篇文章只给出了不完整的 sn-ps。其余的你需要从他上传到 Github 上的一个庞大的示例项目中挑选出来。我不明白为什么需要这样做,但这篇博文仍然值得一读。

【讨论】:

    【解决方案2】:

    对于 Angular 中的路由,您应该使用 routeProvider 它,然后您可以像这样使用它进行导航:

    var app = angular.module('moduleName', [
    'ngRoute',
    'angularModalService'
    ]);
    
    app.config(['$routeProvider',
    function($routeProvider) {
        $routeProvider.
            when('/home', {
            templateUrl: 'partials/home.html',
            controller: 'homeController'
        }).otherwise({
            redirectTo: '/home'
        });
    

    在这里您将 homeController 绑定到整个 home.html,但是如果您需要将控制器绑定到页面的较小部分,您可以像这样绑定它:

    <div class="row" ng-controller="homeController">
       //Further HTML
    </div>
    

    控制器将仅在此 div 中可用。

    对于包含部分,我通常在&lt;/body&gt; 标记上方定义所有脚本。我将它们分开,首先是 jQuery 和 AngularJS 之类的库,在它们下面首先是 app.js(带有 routeProvoder 的文件)、控制器,然后是服务/工厂,最后是指令

    【讨论】:

    • 这个问题是关于ui路由器的,正是因为我选择用它来代替ngRoute。谷歌可以给你所有的理由,但在我的情况下,它主要归结为需要有多个命名视图。
    【解决方案3】:

    为了回答您的问题,Angular 的设计方式是加载其所有 js 文件并将控制权交给 angular。在加载 Js 文件时,所有的控制器、服务等都在 ngApp 中注册。这就是为什么建议在 index.html 本身中给出所有脚本文件的原因。

    如果您认为在单个 index.html 中提供所有文件很笨拙,那么您可以将应用程序拆分为模块然后加载它们。这是让您的应用看起来不那么笨拙的更好方法。

    【讨论】: