jonschlinkert / templates

System for creating and managing view collections, rendering, engines, routes and more. See the "dev" branch for most recent updates.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Rendering issue: a param from a previous render call gets injected

korya opened this issue · comments

commented

I have a basic setup: a layout and 2 views built on top of the layout. When I render the first view everything is OK, but when I render second view unless helper behaves in a weird way: it uses context param value of title from a previous call to render.

'use strict';

var templates = require('templates');
var app = templates();

/**
 * Defin the engine to use
 */

app.engine('txt', require('engine-handlebars'));
app.option('engine', 'txt');

/**
 * Create view collections
 */

app.create('pages');
app.create('layouts', {viewType: 'layout'});
app.create('headings', {viewType: 'partial'});

/**
 * Add some views
 */

// layout
app.layout('base', {
  data: {},
  content: '<div>{{#unless title}}{{ title }}{{/unless}}<span>{% body %}</span>{{ title }}</div>',
});
app.option('layout', 'base');

// pages
app.page('one', {
  data: {title: 'title-A'},
  content: 'ONE',
});
app.page('two', {
  data: {},
  content: 'TWO',
});

/**
 * Render views
 */

app.render('one', {}, function(err, res) {
  if (err) return console.log(err.stack);
  console.log(res.content);
});
app.render('two', {}, function(err, res) {
  if (err) return console.log(err.stack);
  console.log(res.content);
});

Expected output:

<div><span>ONE</span>title-A</div>
<div><span>TWO</span></div>

Actual output:

<div><span>ONE</span>title-A</div>
<div>title-A<span>TWO</span></div>

.render is async, do this instead:

app.render('one', {}, function(err, res) {
  if (err) return console.log(err.stack);
  console.log(res.content);

  app.render('two', {}, function(err, res) {
    if (err) return console.log(err.stack);
    console.log(res.content);
  });
});
commented

Thanks.

One more clarification. As far as I can see, the "bad" package having a race condition is handlebars-engine. Is it right? and can it be easily fixed? or is this "global-state" problem inherent and it comes from Handlebars module?

commented

I have provided a minimal example of the problem occurring in my setup, and indeed your solution solves the problem in that example. However my real setup is a bit more complicated. I am running two instances of Templates in the same process, and that causes the problem. Below is an updated example demonstrating the problem:

'use strict';

const { promisify } = require('util');
const Templates = require('templates');

const getTemplateApp = () => {
  const app = new Templates();

  app.engine('txt', require('engine-handlebars'));
  app.option('engine', 'txt');

  app.create('pages');
  app.create('layouts', {viewType: 'layout'});
  app.create('headings', {viewType: 'partial'});

  app.layout('base', {
    data: {},
    content: '<div>{{#unless hideTitle}}{{ title }}{{/unless}}<span>({% body %})</span>{{ hideTitle }}</div>',
  });
  app.option('layout', 'base');

  return app;
};

const app1 = getTemplateApp();
const app2 = getTemplateApp();

app1.page('zero', {
  data: {},
  content: 'ZERO',
});

app2.page('one', {
  data: {title: 'title-A', hideTitle: true},
  content: 'ONE',
});
app2.page('two', {
  data: {},
  content: 'TWO',
});

/**
 * Render views
 */

app1.render('zero', {}, function(err, res) {
  if (err) return console.log(err.stack);
  console.log(res.content);

  app2.render('one', {}, function(err, res) {
    if (err) return console.log(err.stack);
    console.log(res.content);

    app2.render('two', {}, function(err, res) {
      if (err) return console.log(err.stack);
      console.log(res.content);
    });
  });
});

Expected output:

<div><span>(ZERO)</span></div>
<div><span>(ONE)</span>true</div>
<div><span>(TWO)</span></div>

Actual output:

<div><span>(ZERO)</span></div>
<div><span>(ONE)</span>true</div>
<div>title-A<span>(TWO)</span></div>
commented

Is it a bug? or is it a desired feature and templates was not designed to be used like that? Is there any workaround?

commented

Can you please just let me know if there is a quick workaround for the issue?

commented

The solution I found is to patch the engine package to support multiple instances per process. I've done by exporting a create() function similar to what Handlebars package is doing. The actual commit is available at PlanitarInc/engine-handlebars@d9e621f .

@korya thanks for posting back here. I had looked into this and forgot to respond earlier.

I'm glad you found a fix. I haven't tried it yet, but your fix makes me think that using Object.assign in the code above would work too:

app.engine('txt', Object.assign({}, require('engine-handlebars')));

When I get a chance, I'll try that out. Otherwise, we might want to look into your changes.

Closing based on age, and this was a support request more than a bug report. Also, we'll be handling engines differently in the pending 2.0 release.