Template aquisition in Can 4.x

I’m upgrading a large suite from can 2.2 to can 4.2. Hell of a lot of changes (:slight_smile:

Previously I could reference a template partial like so : {{>/templates/my.stache}} and the top level template would silently ajax in the partials, nothing to do, it Just Worked.

This functionality no longer seems to exist or have I missed it ??

If it is toast then how best to approach this problem ? I would like to componentize all the things but that’s just too big a job for the timescale allowed.

It’s not a trivial problem, there are over 50 such partials and the next upgrade project is even bigger …

Yes, that synchronous XHR lookup has been removed (dev tools would complain loudly about sync XHR).

Are you using StealJS?

If you are using steal, you can do:

<import from="path/to/template.stache" value:to="scope.vars.myTemplate"/>
{{>scope.vars.myTemplate}}

If you have a lot of this, we can help you create a code-mod that will automatically perform this transformation.

If you aren’t using stealjs, I can show you how to create a helper that will do the same thing. Something like:

{{loadAndRender("/templates/my.stache")}}

This should do it:

var templates = {}

stache.addHelper("loadAndRender", function(url, options){
  if(!templates[url]){
    var content = SYNC_XHR_TO(url)
    templates[url] = stache(content);
  }
  return templates[url](options.scope, options.nodeList);
})

Justin

Thanks for this.

I’ll go with your loadAndRender, change of getting agreement in the team re Steal etc is effectively zero …

What do I need to do ??

Re Synchromous XHR. Call me old fashioned but there are times when sync calls are perfectly acceptable … it’s like ‘don’t use eval’. There are times when you have to

Regards

Ron

From: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Synchronous_and_Asynchronous_Requests

var request = new XMLHttpRequest();
request.open('GET', '/bar/foo.txt', false);  // `false` makes the request synchronous
request.send(null);

if (request.status === 200) {
  console.log(request.responseText);
}

Justin

Thanks for your help with this, still not working correctly though, the below trivial code snippet shows the guts of what I’m doing

No errors are thrown but the rendering is off, see end of this message for what is rendered

This is the code with jQuery 3.xxx in the global scope via :

// we have can 4.2 npm-ed
import stache from 'can-stache';
// we need to use Map for legacy code reasons ...
import Map from 'can-map';

// synchromous template loader helper courtesy of Justin Meyer
const templates = {};
stache.addHelper('loadAndRender', (url, options) => {
    if (!templates[url]) {
        const content = $.ajax({ async: false, url: url });
        templates[url] = stache(content);
    }
    return templates[url](options.scope, options.nodeList);
});

const obj = {
    entry: 'this is a test for load and render',
    includedBy: 'loadAndRender'
};
const obs = new Map(obj);
const template = `
    <div class="entry">
        <p>this is the {{entry}}</p>
        {{loadAndRender('/templates/test/include.stache')}}
    </div>`;

const renderer = stache(template);
$('body').append(renderer(obs));

/*
/templates/test/include.stache :
<div class="included">
    <p>included by {{includedBy}}</p>
</div>
*/

This is what is actually rendered :

<div class="entry">
    <p>
	this is the
 	 this is a test for load and render
<p>
</div>

Re ; stache helpers :

I can’t find any documentation for this, am I missing something ? What are the ‘options’ in your loadAndRender snippet ??

Re : DefineMap :

Passing my original obj (as above) to DefineMap results in errors, is there any way to use this obj with DefineMap without completely reworking it ??

Regards

Ron

You might need {{{ }}}

Justin

No difference whatever. Something is definitely missing.

Is there any documentation re stache helpers ?

As a Quick and Dirty I have created a simple partial evaluation function to return a fully expanded template given a top-level entry point. For now I’ll work this up into the npm build tooling :

// —————————————————————————
const fixupTemplate = template => {
// expand all the partials in the template
// and recurse to produce a fully expanded template
const re = /{{[ \t]>[ \t](.+)[ \t]*?}}/g;
template = template.replace(re, (match, url) => {
// ajax synchronously for the partial
const partial = .ajax({ async: false, url: url }).responseText; if (partial) { //console.log('PARTIAL', partial); if (partial.match(re)) { return fixupTemplate(partial); } return partial; } else { alert(`ERROR : '{url}’ has no content`);
}
});
return template;
};

Ron

https://canjs.com/doc/can-stache.addHelper.html

It has a link to the options docs too.

Were you not able to find this? (I want to make sure this sort of stuff is discoverable). If not, why not?

Anyway you can format things more nicely, it’s hard to read the code. Thanks!

Code is just cat and pasted into (Mac) email …

Is there a fiddle/bin whatever that I can setup to show you ?

Needs jQuery and canes 4.2

Ron

If you come to the website, you can use the formatting options to make sure your code looks right. Yes, the JSBins are linked on the forums, canjs.com and gitter. Here is a starting one: https://jsbin.com/safigic/23/edit?html,js,output

Justin

Why is nothing ever straightforward …

Using JSbin etc you are pulling in can.all.js but this does no expose ‘stache’ which I am importing like so in my level environment :

import stache from 'can-stache’;

How can I get at stache from can.all.js ??

I’ll zip up all the things for you anyway

Life should not be this hard (:slight_smile:

Ron

can.stache … everything is exposed on can. The JSBin actually shows that:

Justin

It’s sorted, entirely my fault (:frowning:

In ajaxing for the template content I neglected to extract the responseText from the object returned by the Ajax

An elementary oversight and I’m sorry for wasting a few minutes of your time.

Ron +++

1 Like

Ron,
This seems to work: https://jsbin.com/lalomoj/edit?html,js,output

If your code is failing, it’s probably how the sync XHR is done. Is:

.ajax({ async: false, url: url }).responseText;

Actually giving the response text?