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

时间:2021-07-15 16:20:58

I have a webpage with dynamic content. Let's say it's a product page. When the user goes directly to example.com/product/123 I want to render my product template on the server and send html to the browser. However, when the user later clicks a link to /product/555 I'd like to use JavaScript to update the template on the client-side.

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

I'd like to use something like Knockout.js or Angularjs, but I don't see how I can pre-populate those templates with some initial data on the server and still have a functioning template on the client. i.e. If my Angular template is this:

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

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

When the user goes directly to the URL, I need something that still works as an Angular template, but is filled in with the html for the current product. Obviously this doesn't work:

当用户直接访问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>

It seems like my only option is to send the server-rendered html to the browser along with a separate matching template...?

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

In that case, I'd like to avoid writing every template twice. Which means I need to either switch to JavaScript for my server language (which I would not be happy about) or choose a template language that compiles to both Java and JavaScript and then find a way to hack it into the Play Framework (which is what I'm currently using.)

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

Does anyone have any advice?

有人有建议吗?

4 个解决方案

#1


7  

If you would really like to have an initial value stored in an area before Angular activates- you can use the ng-bind attribute rather than {{bound strings}}, from your example:

如果你真的想在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>

I'm not sure where this would come in handy, but you'll also want to include the initial data-set as part of a script tag in the document, so that when angular DOES activate it doesn't wipe out the displayed information with nulls.

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

Edit: (As requested by commenters)

编辑:(根据评论者的要求)

Alternatively, you could make an ng-repeat at the top of the list, have it configured to fill out based on the 'features' list itself. Following that ng-repeat element, have non-ng-repeat elements which have an ng-hide attribute with the setting ng-hide="features", if Angular loads, all the elements from the original server-provided list hide themselves, and the angular list jumps into existence. No hacky modifications to Angular, and no fiddling with the direct ng-bind attribute.

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

As a side note, you might still want to send a piece of script capable of reading that initial server-element for its data to feed it into angular before angular synchronizes if you want to avoid a blink where angular clears the data while waiting for a request for the same data from the server.

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

#2


2  

I've only used Knockout, not Angular, but a seemingly very common approach which I use is to have the initial state of your data rendered into the page markup as JSON, and on DOM ready use that to build your initial Javascript view model, then apply the Knockout bindings to build the UI. So the UI gets built client side even for an item such as your product which already exists on the server. This means the very same templates can be invoked both for the initial UI creation and when you add something client side, like a sub-product with its own view model and template. Is this an option for you?

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

Edit: in case I misunderstood your requirements, the approach I'm talking about is detailed more in this question: KnockoutJS duplicating data overhead

编辑:如果我误解了你的要求,我正在讨论的方法在这个问题中详细说明:KnockoutJS重复数据开销

#3


1  

One option in AngularJS might be to use a directive that copies values rendered on the server into the model and have subsequent actions retrieve data via JavaScript.

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

I've used the method described here in an ASP.NET WebForms application to pre-populate my model via hidden values from the server on the first request. Per the discussion this breaks from the Angular way but it is possible.

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

Here is example of the html:

这是html的例子:

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

JavaScript:

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));
    }
});

#4


0  

I don't know of a nice techiqunue for doing this, but this is something I've settled on for the time being in a rails application I'm building.

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

You start by initialising your template with seed data using ng-init.

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

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

Then you render the seed data twice. Once from the server and again once Angular has bootstrapped your application. When the application is bootstrapped, Angular will hide the initial seed data leaving only the angularized template.

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

It's important that you use ng-cloak to hide the angular template before it's bootstrapped.

在引导之前使用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>

It doesn't scale with large templates, you're duplicating markup, but at least you won't get that flickering while Angular is bootstrapping your app.

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

Ideally I'd like to be able to re-use the same template on the server as on the client. Something like mustache comes to mind. Obviously the trick would be implementing angular's directives and flow control. Not an easy job.

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

#1


7  

If you would really like to have an initial value stored in an area before Angular activates- you can use the ng-bind attribute rather than {{bound strings}}, from your example:

如果你真的想在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>

I'm not sure where this would come in handy, but you'll also want to include the initial data-set as part of a script tag in the document, so that when angular DOES activate it doesn't wipe out the displayed information with nulls.

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

Edit: (As requested by commenters)

编辑:(根据评论者的要求)

Alternatively, you could make an ng-repeat at the top of the list, have it configured to fill out based on the 'features' list itself. Following that ng-repeat element, have non-ng-repeat elements which have an ng-hide attribute with the setting ng-hide="features", if Angular loads, all the elements from the original server-provided list hide themselves, and the angular list jumps into existence. No hacky modifications to Angular, and no fiddling with the direct ng-bind attribute.

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

As a side note, you might still want to send a piece of script capable of reading that initial server-element for its data to feed it into angular before angular synchronizes if you want to avoid a blink where angular clears the data while waiting for a request for the same data from the server.

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

#2


2  

I've only used Knockout, not Angular, but a seemingly very common approach which I use is to have the initial state of your data rendered into the page markup as JSON, and on DOM ready use that to build your initial Javascript view model, then apply the Knockout bindings to build the UI. So the UI gets built client side even for an item such as your product which already exists on the server. This means the very same templates can be invoked both for the initial UI creation and when you add something client side, like a sub-product with its own view model and template. Is this an option for you?

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

Edit: in case I misunderstood your requirements, the approach I'm talking about is detailed more in this question: KnockoutJS duplicating data overhead

编辑:如果我误解了你的要求,我正在讨论的方法在这个问题中详细说明:KnockoutJS重复数据开销

#3


1  

One option in AngularJS might be to use a directive that copies values rendered on the server into the model and have subsequent actions retrieve data via JavaScript.

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

I've used the method described here in an ASP.NET WebForms application to pre-populate my model via hidden values from the server on the first request. Per the discussion this breaks from the Angular way but it is possible.

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

Here is example of the html:

这是html的例子:

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

JavaScript:

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));
    }
});

#4


0  

I don't know of a nice techiqunue for doing this, but this is something I've settled on for the time being in a rails application I'm building.

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

You start by initialising your template with seed data using ng-init.

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

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

Then you render the seed data twice. Once from the server and again once Angular has bootstrapped your application. When the application is bootstrapped, Angular will hide the initial seed data leaving only the angularized template.

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

It's important that you use ng-cloak to hide the angular template before it's bootstrapped.

在引导之前使用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>

It doesn't scale with large templates, you're duplicating markup, but at least you won't get that flickering while Angular is bootstrapping your app.

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

Ideally I'd like to be able to re-use the same template on the server as on the client. Something like mustache comes to mind. Obviously the trick would be implementing angular's directives and flow control. Not an easy job.

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