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.
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:
<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:
<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...?
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.)
Does anyone have any advice?
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:
<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.
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.
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.