HTML template filled in server-side and updated client-side

后端 未结 4 751
攒了一身酷
攒了一身酷 2020-12-13 21:32

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 templ

相关标签:
4条回答
  • 2020-12-13 21:59

    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.

    You start by initialising your template with seed data using 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.

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

    <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.

    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.

    0 讨论(0)
  • 2020-12-13 22:01

    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.

    0 讨论(0)
  • 2020-12-13 22:16

    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?

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

    0 讨论(0)
  • 2020-12-13 22:20

    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.

    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.

    Here is example of the 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));
        }
    });
    
    0 讨论(0)
提交回复
热议问题