BorisMoore / jsrender

A lightweight, powerful and highly extensible templating engine. In the browser or on Node.js, with or without jQuery.
http://www.jsviews.com
MIT License
2.68k stars 340 forks source link

Problem rendering a client template within a server template (need to use different delimiters on client and server) #279

Closed daslicht closed 8 years ago

daslicht commented 8 years ago

Hi, I have the following code:

<div id="container"> </div>

<script id="peopleTmpl" type="text/x-jsrender">
    <ul>
        {{for people}}
            <li> Name: {{:name}} </li>
        {{/for}}
    </ul>
</script>

<script>
    var data = {
        people: [
            {name: "Jim"}, 
            {name: "Pedro"}
        ] 
    };
    $(document).ready(function(){
        var tmpl = $.templates("#peopleTmpl");
        var html = tmpl.render(data);
        console.log(html);
        $("#container").html(html);
    });

</script>

However this renders to:

html:

<ul>

</ul>

What am I missing, please?

Interesting is also the HTML in the browser, why is there the template empty ?:

BorisMoore commented 8 years ago

Clearly the problem is that template in the browser showing only the <ul>. How are you creating the browser HTML? Are you rendering the page on the server? Is livereload playing a role, rerendering the HTML? It doesn't look like a JsRender issue...

Did you start by following one of the patterns on the https://github.com/BorisMoore/jsrender-node-starter for server rendering etc.?

daslicht commented 8 years ago

Yeah my solution is based on those patterns :

https://github.com/daslicht/hello-hapi-jsviews

BorisMoore commented 8 years ago

OK - well the problem is you are trying to use JsRender both for rendering on the server and in the browser.

See http://www.jsviews.com/#jsr-node-quickstart http://www.jsviews.com/#node/filetmpls and http://www.jsviews.com/#node/server-browser

about different ways of rendering file-based templates in the browser.

You are not using those patterns for rendering the peopleTmpl in the browser, but instead you are rendering it directly in the server rendered content

<h2>Hello: "{{:hello}}"</h2>

...
<script id="peopleTmpl" type="text/x-jsrender">
    <ul>
        {{for people}}
            <li> Name: {{:name}} </li>
        {{/for}}
...

All of those tags ({{:hello}} and {{for ...}} are in your server-rendered template, so are being rendered there on the server. On the server, people is undefined, so the {{for people}} renders no content.

If you want to include a client template in the server-rendered template, you have either to use {{clientTemplate}} or browserify, etc. as in the jsr-node-quickstart, or you can choose to use different template delimiters on the server and the client. E.g call jsrender.views.settings.delimiters("<%", "%>"); on the server and then use:

<%include tmpl='./templates/_includes/helloworld.html'/%>

and

<h2>Hello: "<%:hello%>"</h2>

...
<script id="peopleTmpl" type="text/x-jsrender">
    <ul>
        {{for people}}
            <li> Name: {{:name}} </li>
        {{/for}}
...

Now the {{for }} tags will not be recognized as server jsrender tags, and will get rendered directly to the browser, where they will get recognize as tags by the client jsrender engine.

I changed the title of this issue, to help others who may have the same difficulty...

daslicht commented 8 years ago

Thank you for your reply !

I got it now by using the _{{clientTemplate "./templates/includes/clientTemplate.html" /}}

https://github.com/daslicht/hello-hapi-jsviews/blob/master/templates/layout.html#L7

The Last Paragraph here is what I like to create:

Single Page Apps with initial rendering on server http://www.jsviews.com/#node/server-browser

What is the simplest approach to do this? Sharing a router would be needed as well, hm.

BorisMoore commented 8 years ago

Well the jsrender-node-starter (with its samples and readme), along with the node.js topics on jsviews.com (http://www.jsviews.com/#jsrnode), is all about showing how to do Single Page Apps with initial rendering on server - with alternative ways do to that. I can't do other than suggest choosing between the approaches described there.

daslicht commented 8 years ago

Ok looks like I oversaw the routing part, I will read it again.

BorisMoore commented 8 years ago

On the starter I address initial server rendering using the same template as subsequent client-side SPA rendering - e.g.

<tbody id="movieList" data-link="{include tmpl='./templates/movie-list.html'}">
    {{include tmpl='./templates/movie-list.html'/}}
</tbody>

but I don't do shared server-side/client-side routing. The initial rendering is on the 'home page' of the SPA only.

But you should be able to use the URL hash to switch 'pages' client-side - like I do on the www.jsviews.com site - and then have the server also render a different page based on the hash - complete with initial rendering of included templates.

daslicht commented 8 years ago

Sounds interesting , which router do you use ? That should also work with push state no? www.jsviews.com is not rendered server-side? If I disable JS I just see the navigation. How does search engines index your www.jsviews.com ?

BorisMoore commented 8 years ago

I don't use any external libraries other than jsviews and jquery. The code is all here: https://github.com/BorisMoore/jsviews.com. I'm not actually using push state. No it is not rendered server-side. Just index.html for the initial page.

Search engines seem now to be able to search SPA's - with javascript on, without depending on server rendered content. Not sure how they do it! But manifestly it works.

daslicht commented 8 years ago

yeah its mysterious, even the hash routes seam to get indexed :) At least by google

BorisMoore commented 8 years ago

Closing