HTML模板填写在服务器端和更新的客户端

时间:2012-08-07 02:57:17

标签: playframework knockout.js angularjs dust.js

我有一个包含动态内容的网页。假设这是一个产品页面。当用户直接转到example.com/product/123时,我想在服务器上呈现我的产品模板并将html发送到浏览器。但是,当用户稍后点击指向/product/555的链接时,我想使用JavaScript在客户端更新模板。

我想使用类似Knockout.js或Angularjs的东西,但我不知道如何在服务器上预先填充这些模板和一些初始数据,并且仍然在客户端上有一个正常运行的模板。即如果我的Angular模板是这样的:

<ul>
    <li ng-repeat="feature in features">
      {{feature.title}}
      <p>{{feature.description}}</p>
    </li>
</ul>

当用户直接访问URL时,我需要一些仍然可用作Angular模板的东西,但是用当前产品的html填充。显然这不起作用:

<ul>
    <li ng-repeat="feature in features">Hello
      <p>This feature was rendered server-side</p>
    </li>
    <li>Asdf <p>These are stuck here now since angular won't replace them when
       it updates.... </p></li>
</ul>

似乎我唯一的选择是将服务器呈现的html与单独的匹配模板一起发送到浏览器......?

在这种情况下,我想避免两次编写每个模板。这意味着我需要切换到我的服务器语言的JavaScript(我不会高兴)或选择一个编译为Java和JavaScript的模板语言,然后找到一种方法将其破解到Play框架(这是什么我正在使用。)

有人有任何建议吗?

4 个答案:

答案 0 :(得分:7)

如果您真的希望在Angular激活之前将区域中存储初始值 - 您可以使用ng-bind属性而不是{{bound strings}},来自您的示例:

<ul>
    <li ng-repeat="feature in features">
        <div ng-bind="feature.title">Hello</div>
        <p ng-bind="feature.description">This feature was rendered server-side but can be updated once angular activates</p>
    </li>
</ul>

我不确定这会派上用场,但你也希望将初始数据集作为文档中脚本标记的一部分包含在内,这样当角色DOES激活时它就不会消失带有空值的显示信息。

修改(根据评论者的要求)

或者,您可以在列表顶部进行ng-repeat,将其配置为根据“功能”列表本身填写。在ng-repeat元素之后,使用ng-hide =“features”设置具有ng-hide属性的非ng-repeat元素,如果Angular加载,原始服务器提供的列表中的所有元素都隐藏自己,并且角度列表跳跃存在。对Angular没有任何hacky修改,也没有摆弄直接ng-bind属性。

作为旁注,您可能仍希望发送一段能够读取该数据的初始服务器元素的脚本,以便在角度同步之前将其提供给角度,如果您想要避免闪烁,其中角度清除数据等待来自服务器的相同数据的请求。

答案 1 :(得分:2)

我只使用了Knockout,而不是Angular,但我使用的一种看似非常常见的方法是将数据的初始状态呈现为页面标记为JSON,并且在DOM上准备使用它来构建初始Javascript查看模型,然后应用Knockout绑定来构建UI。因此,即使对于已存在于服务器上的产品(例如您的产品),UI也会构建客户端。这意味着可以为初始UI创建和添加客户端内容时调用相同的模板,例如具有自己的视图模型和模板的子产品。这是你的选择吗?

编辑:如果我误解了您的要求,我正在讨论的方法在此问题中详细说明:KnockoutJS duplicating data overhead

答案 2 :(得分:1)

AngularJS中的一个选项可能是使用一个指令将服务器上呈现的值复制到模型中,并让后续操作通过JavaScript检索数据。

我在ASP.NET WebForms应用程序中使用了here中描述的方法,在第一次请求时通过服务器中的隐藏值预先填充我的模型。根据讨论,这可以从Angular方式中解脱出来,但它是可能的。

这是html的例子:

<input type="hidden" ng-model="modelToCopyTo" copy-to-model value='"this was set server side"' />

JavaScript的:

var directiveModule = angular.module('customDirectives', []);

directiveModule.directive('copyToModel', function ($parse) {
    return function (scope, element, attrs) {
        $parse(attrs.ngModel).assign(scope, JSON.parse(attrs.value));
    }
});

答案 3 :(得分:0)

我不知道这样做有什么好的技术,但这是我刚刚在我正在构建的rails应用程序中解决的问题。

首先使用ng-init初始化带有种子数据的模板。

<ul ng-init="features = <%= features.to_json %>">
    <li ng-repeat="feature in features">
      {{feature.title}}
      <p>{{feature.description}}</p>
    </li>
</ul>

然后两次渲染种子数据。一旦从Angular引导您的应用程序,从服务器再次。当应用程序被引导时,Angular将隐藏初始种子数据,只留下愤怒的模板。

在引导之前使用ng-cloak隐藏角度模板非常重要。

<ul ng-hide="true">
  <% features.each do |feature| %>
    <li>
      <%= feature.title %>
      <p><%= feature.description =></p>
    </li>
  <% end %>
</ul>
<ul ng-hide="false" ng-cloak ng-init="features = <%= features.to_json %>">
    <li ng-repeat="feature in features">
      {{feature.title}}
      <p>{{feature.description}}</p>
    </li>
</ul>

它不能与大模板一起扩展,你复制了标记,但至少在Angular引导你的app时你不会得到闪烁。

理想情况下,我希望能够在服务器上重复使用与客户端相同的模板。我想到了胡子之类的东西。显然,诀窍是实现angular的指令和流量控制。不是一件容易的事。