【问题标题】:Play Framework 2.1 - AngularJS routing - best solution?Play Framework 2.1 - AngularJS 路由 - 最佳解决方案?
【发布时间】:2013-04-11 17:18:02
【问题描述】:

我正在学习 AngularJS 教程。 Angular 使用它自己的 JS 路由机制来允许单页应用程序。 Angular 的示例路由文件如下所示:

angular.module('phonecat', []).
  config(['$routeProvider', function($routeProvider) {
  $routeProvider.
      when('/phones', {templateUrl: '/partials/phone-list',   controller: PhoneListCtrl}).
      when('/phones/:phoneId', {templateUrl: 'partials/phone-detail', controller: PhoneDetailCtrl}).
      otherwise({redirectTo: '/phones'});
}]);

我正在尝试找一个好地方来存储我的部分内容(Angular 特定的 HTML 文件)。理想情况下,我希望能够从 Play 中对它们进行模板化(即将它们作为 *.scala.html 文件)。我可以使用 Play 路由文件来完成此操作,如下所示:

GET     /partials/phone_index       controllers.Application.phone_index

我基本上偏向于这样的控制器动作:

def phone_index = Action {
  Ok(views.html.partials.phone_index())
}

我正在寻找的解决方案是两种理想的结合:

  1. 我会有某种映射,让我可以访问 /partial/* 下的任何文件并取回部分文件。
  2. 我希望覆盖到特定部分的路由,因此我可以使用控制器操作来动态填充数据(很少见)。

有什么想法吗?

【问题讨论】:

    标签: scala angularjs playframework


    【解决方案1】:

    当我尝试类似的事情时,我得出的结论是最好将其分成两部分:

    • 将 Play 用作您通过 Ajax 调用与之交互的后端
    • 将 Angular 模板存储在 Play public 文件夹(类似于 /public/angular/)中,并使用默认的 AngularJs 方式映射模板

    我知道这听起来不太好,也确实没有回答您关于如何做到这一点的问题,但是由于模板及其网址在 Angular 中的映射方式以及好处,尝试链接这两个框架可能会出现问题将非常小,因为任何更改都意味着大量工作,从而消除了 Play 和 Angular 的主要优势,即快速开发。

    这还允许您更好地分离关注点,如果您的项目增长,这可能很重要,因为您可以将 AngularJS 代码作为连接到后端的独立应用程序拿走,它会正常工作。

    您可以在Github repository 中看到我所说的一些示例代码(基于 AngularJS 的 TODO 教程)。我警告你,代码不太好,但应该给你一个想法,并且作为奖励向你展示如何将 Jasmine 集成到 Play 中,用于 AngularJS 单元测试。

    【讨论】:

    • 我认为这可能是我需要采用的方法。
    • 好吧,如果设置了“play.http.context”属性,这种方法就不起作用。路由将错过上下文前缀,因此会失败。有什么想法可以解决这个问题吗?
    【解决方案2】:

    最终的种子 (https://github.com/angyjoe/eventual) 是构建 Play + AngularJS 应用程序的另一种方式。该代码是一个甜心并且有据可查。

    【讨论】:

    • 你可能想在这里公开,我的检查表明你是那个的作者。这本身不是犯罪,它似乎是相关信息,只是我们对广告不友好。
    • 没关系,就像我说的那样,如果你敞开心扉说Disclosure: I am author of said tool, and it was written in response to this question之类的话,人们会更信任你。
    【解决方案3】:

    这不会直接回答您的问题,但我发现这是构建 Play + Angular 应用程序的最佳方式:

    https://github.com/typesafehub/angular-seed-play

    【讨论】:

    • 这里我不喜欢的是一开始就使用requireJS和webjars。对我来说,添加之后是有意义的。 (对我来说,它不像java中的maven那么简单)。当应用程序足够大时,这很有效。这并没有改变 +Pere Villega 所说的“快速发展”
    【解决方案4】:

    是的,可以创建客户端模板的服务器端元模板。这提供了一些独特的能力,因为这两种方法并不完全重叠。还有很多混淆的空间,所以请确保您知道为什么要编写 Play 块而不是 Angular 指令。

    您是否应该这样做仍然是一个悬而未决的问题;这实际上取决于您是否真的需要访问模板中的服务器信息。我认为有必要和适当的一个例子是在您的视图中实施访问控制。

    现在回答你的问题。通过内联部分而不是尝试为它们提供按需加载的路线来解决问题。见http://docs.angularjs.org/api/ng.directive:script

    模板如下所示:

    @(id: Long)(implicit request: RequestWithUser[AnyContent])
    
    @import helper._
    
    <!doctype html>
    <html lang="en" ng-app="phonecat">
    <head>
      <meta charset="utf-8">
      <title>Google Phone Gallery</title>
      <link rel="stylesheet" href="css/app.css">
      <link rel="stylesheet" href="css/bootstrap.css">
      <script src="lib/angular/angular.js"></script>
      <script src="js/app.js"></script>
      <script src="js/controllers.js"></script>
      <script src="js/filters.js"></script>
      <script src="js/services.js"></script>
      <script src="lib/angular/angular-resource.js"></script>
    </head>
    <body>
      <div ng-view></div>
    
      @ngTemplate("phone-list.html") {
        <div class="container-fluid">
          <div class="row-fluid">
            <div class="span12">Hello @request.user.name</div>
          </div>
    
          <div class="row-fluid">
            <div class="span2">
              <!--Sidebar content-->
    
              Search: <input ng-model="query">
              Sort by:
              <select ng-model="orderProp">
                <option value="name">Alphabetical</option>
                <option value="age">Newest</option>
              </select>
    
            </div>
            <div class="span10">
              <!--Body content-->
    
              <ul class="phones">
                <li ng-repeat="phone in phones | filter:query | orderBy:orderProp" class="thumbnail">
                  <a href="#/phones/{{phone.id}}" class="thumb"><img ng-src="{{phone.imageUrl}}"></a>
                  <a href="#/phones/{{phone.id}}">{{phone.name}}</a>
                  <p>{{phone.snippet}}</p>
                </li>
              </ul>
    
            </div>
          </div>
        </div>
      }
    
      @ngTemplate("phone-detail.html") {
        <img ng-src="{{mainImageUrl}}" class="phone">
    
        <h1>{{phone.name}}</h1>
    
        <p>{{phone.description}}</p>
    
        <ul class="phone-thumbs">
          <li ng-repeat="img in phone.images">
            <img ng-src="{{img}}" ng-click="setImage(img)">
          </li>
        </ul>
    
        <ul class="specs">
          <li>
            <span>Availability and Networks</span>
            <dl>
              <dt>Availability</dt>
              <dd ng-repeat="availability in phone.availability">{{availability}}</dd>
            </dl>
          </li>
        </ul>
      }
    </body>
    </html>
    

    应用程序:

    'use strict';
    
    /* App Module */
    
    angular.module('phonecat', ['phonecatFilters', 'phonecatServices']).
      config(['$routeProvider', function($routeProvider) {
      $routeProvider.
          when('/phones', {templateUrl: 'phone-list.html',   controller: PhoneListCtrl}).
          when('/phones/:phoneId', {templateUrl: 'phone-detail.html', controller: PhoneDetailCtrl}).
          otherwise({redirectTo: '/phones'});
    }]);
    

    只需包含这个助手:

    @**
     * @ngTemplate
     * Generate an AngularJS inlined template.
     *
     * Note: Do not include scripts in your @template HTML. This will break the template.
     *
     * @param name
     * @param template
     *@
    @(name: String)(template: Html)
    
    <script type="text/ng-template" id="@name">@template</script>
    

    并确保在 Angular 应用的根范围内使用它。

    【讨论】:

      【解决方案5】:

      对于问题 #1,您可以介绍这样的路线:

      /partials/:view    controllers.Application.showView(view:String)
      

      然后在您的控制器中,您需要将视图名称映射到实际视图:

      Map("phone_index" -> views.html.partials.phone_index())
      

      您可能希望延迟渲染模板或要求存在请求,那么您可能应该执行以下操作:

      val routes = Map(
        "phone_index" -> { implicit r:RequestHeader => 
           views.html.partials.phone_index()) 
        }
      

      您的操作将如下所示:

      def showView(view:String) = 
        Action { implicit r =>
          routes(view)
        }
      

      如果您想要特定路由的特定控制器方法(问题 #2),您只需在动态路由上方添加一个路由:

      /partials/specific    controllers.Application.specific()
      

      【讨论】:

      • 这行得通。但它需要使用路由名称映射到控制器。这不是一个更好的数量。不过还是谢谢你。希望它会帮助别人。
      【解决方案6】:

      我真的认为这不是一个好主意,即使它确实来自一个恭敬的心。

      我认为将每个想法都保留为默认设置(约定优于配置原则)是一种非常好的做法,这意味着对我来说,我们可能更感兴趣将每个范式(Play 和 AngularJS)分开,因为其中一个或两个都可以演变近期或远期的代码维护成本。

      第二个非常重要的一点是可测试性,如果您将两种技术混合使用,您最终会得到一个混合,从而在应用程序的两侧提供真正良好的测试覆盖率。 干杯

      【讨论】:

        【解决方案7】:

        这可能无法准确回答问题,但您可以尝试关注这个项目,因为它似乎是构建 Play/scala/Angular 应用程序的好例子:

        https://github.com/lashford/modern-web-template#master

        http://typesafe.com/activator/template/modern-web-template

        猜你喜欢
        • 1970-01-01
        • 2022-11-11
        • 1970-01-01
        • 2020-04-12
        • 1970-01-01
        • 1970-01-01
        • 2023-03-13
        • 2014-09-18
        • 1970-01-01
        相关资源
        最近更新 更多