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

32

我正在阅读 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

我基本上将partials/部分视图渲染到控制器操作中,就像这样:

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

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

  1. 我需要一种映射方式,让我可以访问 /partial/* 下的任何文件并获取相应的局部文件。
  2. 我希望重写特定局部路径的路由,这样我就可以使用控制器动作动态填充数据(罕见情况)。

有什么建议吗?

7个回答

31

当我尝试类似的东西时,我得出结论最好把它分成两部分:

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

我知道这听起来不是很好,并且回答了你如何做的问题,但尝试链接两个框架可能会出现问题,因为Angular中模板及其URL的映射方式,而且收益非常小,因为任何更改都需要大量的工作,因此消除了Play和AngularJS的主要优点,即快速开发。

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

你可以在这个Github仓库中看到一些示例代码(基于AngularJS的TODO教程)。 我提醒你,代码不是很好,但应该让你有一个想法,并作为奖励向您展示如何将Jasmine集成到Play中,以进行AngularJS单元测试。


2
我认为这可能是我需要采取的方法。 - Dominic Bou-Samra
这种方法在设置了“play.http.context”属性时不起作用。路由将缺少上下文前缀,因此会失败。有什么想法可以解决这个问题吗? - user2715478

4

2
你可能需要在这里进行披露,我的检查表明你是那个作者。这本身并不是一种罪行,它似乎是相关信息,只是我们不喜欢过度宣传。 - Kent Fredric
没问题,就像我说的那样,如果你公开说出类似“声明:我是该工具的作者,并且它是为回答这个问题而编写的”,人们会更信任你。 - Kent Fredric

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>

请确保在您的Angular应用程序的根作用域内使用它。


3

1
我不喜欢在一开始就使用requireJS和webjars。对我来说,这些应该是后期添加的(对我来说,这并不像Java中的Maven那样简单)。当应用程序足够大时,这种方法效果很好。但这并不改变+Pere Villega关于“快速开发”的说法。 - ses

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()

这个方法可以行得通。但是需要使用路由名称到控制器的映射表。虽然并没有太大的改进,但还是非常感谢。希望能对其他人有所帮助。 - Dominic Bou-Samra

1

这是关于此示例的教程 https://github.com/lashford/modern-web-template#master http://www.cakesolutions.net/teamblogs/2014/03/13/reactivemongo-play-angularjs - Bob

1

我真的认为这不是一个很好的想法,即使它来自一个值得尊重的思想。

我认为将每个事物保持默认(约定优于配置原则)是一个非常好的实践,这对我来说意味着我们可能更有兴趣将每个范例(Play和AngularJS)分开,因为其中一个或两个可能在不久的将来或远期发生变化,这将对代码维护产生影响。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接