BorisMoore / jsviews

Interactive data-driven views, MVVM and MVP, built on top of JsRender templates
http://www.jsviews.com/#jsviews
MIT License
856 stars 130 forks source link

better error message #438

Closed johan-ohrn closed 4 years ago

johan-ohrn commented 4 years ago

This fiddle

html:

<div id="ph"></div>

js:

$.templates("{{:~unknown()}}").link("#ph");

gives the following unhelpful error message:

Uncaught TypeError: view.ctxPrm(...) is not a function
...

I would rather the error message mention the name of the context parameter, in this case "unknown".

It would also be helpful in tracking down the error if the exception would write out which template and tag hierarchy that threw the exception.

BorisMoore commented 4 years ago

Thanks for calling this out, Johan. I agree the error message is not ideal, and that the additional information would be helpful, but unfortunately it looks like a fix is difficult to achieve. If it were possible at all it would imply significant changes and additions to the the template parsing code, with corresponding cost in perf, code size, and potentially stability of current code.

So my current take is that this is a 'won't fix`.

In fact the parsed code is view.ctxPrm("unknown")() and the browser throws an error. For Chrome it is view.ctxPrm(...) is not a function. For Safari it is undefined is not a function... It would be nice if the browser gave the message view.ctxPrm("unknown") is not a function. But it doesn't, and we have no control over that...

johan-ohrn commented 4 years ago

What I'm discovering is that I come back to a month old code or more and change something, perhaps a web api or something in a viewmodel which result in a template all of a sudden being linked against a new data structure. Bang I'm getting this kind of error. At best of times it's easy to find out which template expression is the culprit but it happens all to often that I have to remove a lot of template code and by process of elimination locate the template expression that is causing the error.

Perhaps a best effort would be enough. I'm noticing that the function buildCode(ast, tmpl, isLinkExpr) is passed tmpl which contains the expression tmpl.markup. At the end of the function it builds up the dynamic javascript in the code var such as this:

// unnamed
var v,ret=""
+((v=view.ctxPrm("unknown")())!=null?v:"");
return ret;

What if instead the dynamic javascript was generated something like this instead?

// unnamed
try {
  var v,ret=""
  +((v=view.ctxPrm("unknown")())!=null?v:"");
  return ret;
} catch (err) {
  err.message = "template: '" + tmplName + "', markup: '" + tmpl.markup + "' threw the following " + err.message;
  throw err;
}

Instead of hijacking the error it might be better to throw a custom error. I seem to recall there being a template parser error..

This shouldn't incur any performance penalty, code size should be small and it would at least print out the template expression I should be looking out for.

BorisMoore commented 4 years ago

Ah - well if you want to include a try catch, I believe there is a perf cost, but you can do it with existing features. Just do the following, as an opt-in choice:

$.views.settings.debugMode(myOnErrorHandler);

function myOnErrorHandler(err, fallback, view) {
   throw "template: '" + view.tmpl.tmplName + "', markup: '" + view.tmpl.markup + "' threw the following " + err.message;
}

(It is opt-in because of the trade-off perf/debugging information)

See https://www.jsviews.com/#onerror@debugmode-function.

You can also set onError on the tag, to pass in anything you want via the fallback parameter of your handler. It can be a string, an object or a function, such as the template object:

{{:~unknown() onError=#tmpl}}
function myOnErrorHandler(err, template, view) {
   throw "template: '" + template.tmplName + "', markup: '" + template.markup + "' threw the following " + err.message;
}
johan-ohrn commented 4 years ago

That first option does what I need. Thanks.

BorisMoore commented 4 years ago

Closing, since the requested feature is already available through existing feature: https://www.jsviews.com/#onerror@debugmode-function.