Play Framework 2.1 - AngularJS路由 - 最佳解决方案?

时间:2013-04-18 14:19:29

标签: scala angularjs playframework

我正在通过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. 我希望覆盖到特定部分的路由,以便 CAN 使用控制器操作动态填充数据(罕见)。
  3. 有什么想法吗?

7 个答案:

答案 0 :(得分:32)

当我尝试类似的东西时,我得出的结论是,最好将其分为两部分:

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

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

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

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

答案 1 :(得分:4)

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

答案 2 :(得分:3)

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

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

答案 3 :(得分:3)

是的,可以创建客户端模板的服务器端元模板。这提供了一些独特的能力,因为这两种方法并不完全重叠。还有很多混淆的空间,所以一定要知道为什么要编写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>

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

答案 4 :(得分:1)

对于问题#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()

答案 5 :(得分:1)

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

我认为将每个思考都视为默认(约定优于配置原则)是一种非常好的做法,这对我来说意味着我们可能更有兴趣将每个范例(Play和AngularJS)分开,因为其中一个或两个可以进化近期或远期的代码维护费用。

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

答案 6 :(得分:1)

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

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

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