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.67k stars 339 forks source link

How to deal with JsRender error exceptions during server-side compiling or rendering? #292

Closed daslicht closed 8 years ago

daslicht commented 8 years ago

Hi, I have the following structure (forget the twig extension, its left from my nunjucks tests):

            var appData ={};
            res.render('layout', appData, function(err, html) {
               res.send(html);
            });
test

{{include tmpl='./_includes/footer.html'}} 
//if i remove the import, the template get rendered and shows 'test'

I get no errors , just a white page.

How to apply this on the server :

        $.views.settings.debugMode(true);

Is this correct :

jsrender.views.settings.debugMode(true)

Whats missing please?

BorisMoore commented 8 years ago

The error you have is a syntax error:

{{include tmpl='./_includes/footer.html'}} (Missing the closing slash)

should be

{{include tmpl='./_includes/footer.html' /}}

Wen running in Node, if JsRender throws, Node will catch the exception and pass it to your render callback, and you have to deal with it there.

res.render('layout', appData, function(err, html) {
    res.send(html);
}); callback:

For example you can have it render the error by writing:

res.render(..., function(err, html) {
    res.send(err ? jsrender.views.converters.html(err.message) : html); // HTML encode message
});

You can also run node in debug mode, and catch the errors with breakpoints. (Using node-debug ...)

Yes the setting for debugMode(true) is jsrender.views.settings.debugMode(true); - and that will work for rendering out any rendering errors - instead of throwing exceptions. But syntax errors in the template will still throw, since they occur during template compilation, not during rendering...

BorisMoore commented 8 years ago

I changed the title of this issue, to correspond to the scenario we are concerned with here.

daslicht commented 8 years ago

I have now added teh errorhandler to my Express app like this:

        app.use('development', function(){
            app.use(errorhandler());
        });

I will document my experience here for anyone in future..

––– I get Errors now !

{Error: ENOENT: no such file or directory, open './_includes/footer.html'}
"start": " tsc -w & export NODE_ENV=development nodemon & app.js"

console.log('env:',process.env.NODE_ENV); // env: development

I tried removing the dot of the path but then I get :

test _includes/footer.html

That is my views config:

app.set('views', __dirname + '/app'); 
console.log('js views debug mode: ', jsrender.views.settings.debugMode() ); // returns true
daslicht commented 8 years ago

Strange,

This properly renders the footer :

res.render('_includes/footer', appData, function(err, html) {
                res.send(err ? jsrender.views.converters.html(err.message) : html); 
            });

If I include the footer into the layout like this :

{{include tmpl='_includes/footer' /}}

I just get the following displayed:

_includes/footer
daslicht commented 8 years ago

I created a simple test project here to test the include :

here

But instead of rendering the included files, it just outputs the include filename ?

BorisMoore commented 8 years ago

"./..." paths are relative paths - they are relative to the calling script - in your case app.js. They are nothing to do with Express.

You need to write {{include tmpl='./templates/test.html' /}}

OTOH if you use Hapi or Express, you can define the templates directory location, and then write res.render('_includes/footer', ... where you are using an Express API - hence the path is relative to the templates folder you set up for Express, and you are omitting the .html extension, since you set that up too for express.

JsRender './...' paths work equally well whether you use Express, Hapi, or neither.

All that is shown in the samples here https://github.com/BorisMoore/jsrender-node-starter, and the docs here http://www.jsviews.com/#node/filetmpls.

Did you start from those samples? If so you should have been starting from the correct ./... relative paths...

daslicht commented 8 years ago

Thank you that works !

I studied the /jsrender-node-starter, but its wasn't clear to me that its relative to the calling script and I cant remember that I read it in the manual. It just says that it is relative but not from the calling script. But now its clear !

BorisMoore commented 8 years ago

Right - it didn't explain the calling script, but in fact that is standard to node, the paths are just used with standard semantics. When new to node it is sometimes confusing. But the examples in the docs show ./templates/... so suggest that the origin is higher up than the template folder (if one is assigned).

daslicht commented 8 years ago

I assumed (wrong) that after registering it as View Engine the imports behave as Express.

daslicht commented 8 years ago

Thank You Boris !

BorisMoore commented 8 years ago

Sure!

daslicht commented 8 years ago

Is there a way to get errors for syntax errors?

BorisMoore commented 8 years ago

As far as JsRender is concerned, if it detects a syntax error, it throws a corresponding error exception.

What happens to the exception is then up to you, and the environment you are running in. You can write to console or render to the browser, as you wish - by setting up the appropriate error handler to catch the error and do that.

If you mean on the server in Express, well again, it's up to you to catch the error, or set up error handling as you want, or use the other available middleware etc. http://expressjs.com/en/guide/error-handling.html.

If you have set debugMode(true) or similar, then if the error was during rendering, JsRender will not throw, but will include the error message in the output. Best keep debugMode to false, if you want to catch exceptions. Then you can use standard Express without additional middleware, and write, for example:

app.get('/', function(req, res) { // Express template
  res.render('layout-movies', appData, function(err, html) {
    if (err) {
      console.log("Error: " + err.message);
    }
    res.send(html)
  });
});
daslicht commented 8 years ago

Thank you and Happy Eastern :dancers: