Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Strange behavior on Safari IOS #373

Closed
Furizaa opened this issue Nov 26, 2012 · 27 comments
Closed

Strange behavior on Safari IOS #373

Furizaa opened this issue Nov 26, 2012 · 27 comments

Comments

@Furizaa
Copy link

Furizaa commented Nov 26, 2012

I found this issue that is absolutely imposible to debug for me. It happens like every other time so it's not clearly repeatable. As soon as I hook my iPad into remote debugging the issue disappears completely.

It seems to happen after a certain point of template complexity and nesting level. This can cause that the name attribute in invokeAmbiguous is "undefined". This of course crashes at the next call setupHelpers. Any idea what circumstances have to occur to have an outcome like that? I've already checked that the context and the template is fine.

One of the possible templates where this can happen is:

<ul>
  {{#each filters}}
    <li>{{this}}</li>
  {{/each}}
</ul>

Where the context is

{
  filters: [
    "Transfer Inklusive"
  ]
}

There seems to be a Problem with {{this}} and it happens on the template call.

First we thought that splitting the heavy nested stuff in Partials would fix the problem. But after a few hours it started again. We do not precompile our templates in node.js.

As I said - it's almost impossible to reproduce and to debug because it only happens in IOS Safari when it's not hooked to a debugging device like every other reload. Could it possible be a race condition or some memory issues?

EDIT: Stack Trace:

'undefined' is not an object

[email protected]:1727
[email protected]?0:1735
[email protected]?0:1569
[email protected]?0:1190
[email protected]?0:1842
@handlebars.js?0:1850
@misteroneill
Copy link

I am encountering this as well, but it occurs in Safari 6 on OS X and only when the console is disabled (wondering if this means it's a Safari bug?). We aren't using {{this}} - but {{.}} and {{{.}}} are triggering it.

For me, the error is thrown in that quotedString function. Basically, it appears Handlebars is unable to track the scope of "this" in Safari 6.

@Furizaa
Copy link
Author

Furizaa commented Nov 27, 2012

I've found a rogue block helper in one of our templates that was unnecessary. Now everything seems to work for now. Wondering why it worked in the first place but as long as it works I'm happy with it.

The context was an object and this template is invoked approx. 200 times.

{{#this}} <- wrong
<do some one-time html rendering which didn't need to be looped />
{{/this}}

@misteroneill
Copy link

I have worked around this in our app by compiling templates in Node as part of our build, which is something I've been meaning to do anyway. Works fine that way!

@Furizaa Furizaa closed this as completed Nov 27, 2012
@windameister
Copy link

I meet exactly the same problem recently, wondering whether it is a bug in Handlebars ? or some mistake template I wrote? Or is there any way to avoid this?

@windameister
Copy link

@Furizaa my template is like

{{#each messages}}
<b>{{this}}</b>
{{/each}}

and the problem is the same as what you have described, I want to know how did you fix this? change the template usage? I don't want to change my build process to make the template be build in node, wish there is some easy way to work around this...

@Furizaa
Copy link
Author

Furizaa commented Jan 17, 2013

The stuff I wrote above is all I know about it. We had a severe error in out template that skyrocketed the complexity. For other projects we now build out templates with Node and never encountered the error again.

@misteroneill
Copy link

I am certain it's not a bug in Handlebars, but a bug in Safari 6. Compiling the templates in Node and saving them as .js files is the only way I found to work around it.

@windameister
Copy link

@misteroneill @Furizaa Thank you all the way, I will have a try to pre-compile the templates. Wish it would not be too difficult to change all my project's templates into pre-compiled ones, I have not considered this way before. : )

I have googled a lot on how to use pre-compiled handlebars templates, could not find any tutorial which shows how to put the pre-compiled templates work well with Require.js and Backbone.js, wondering whether there are some articles showing the best practices on doing this right?

Is https://github.com/SlexAxton/require-handlebars-plugin a good choice?
Is http://www.ericfeminella.com/blog/2012/07/15/managing-client-side-templates-with-requirejs/ a good way to handle all the templates dependency?

Best.

@misteroneill
Copy link

The Handlebars binary provided by the npm package will create an AMD module containing all your templates if you use the -a command line flag. You may also need the -h option to tell it where to find your Handlebars .js file. Our app uses a bash script to compile them at build time. Something like this:

handlebars path/to/templates -f js/compiled-templates.js -a -h js/lib

That's all there is to it. Dead simple. You may want to minify it using uglifyjs also, but that's optional. Just make sure you have Node and the handlebars package installed.

@windameister
Copy link

@misteroneill thanks again for the information on how to compile the templates.

I have got a big project now, with about 100 template files included by require.js, using require.js text plugin, in the way below:

define(["marionette", "text!templates/a.tpl"], function(Marionette, myATemplate) {
    var MyView = Marionette.ItemView.extend({
        template: Handlebars.compile(myATemplate),
    });
});

Until this problem, I haven't considered to manage and reference my template files in another way, but now,
if I change all my templates to compiled ones, I think I need to find a new way to manage them all, and reference them using a new method, no longer compiling them on the fly like this. I wasn't bothered by the number of files before, because I thought I could use r.js to build all the files, but now it seems I need to change the project's architecture a lot.....

I think there maybe some projects already solved this problem before, including the solution to use the un-compiled version in development and the compiled templates after build, all transparent to upper level developer. So when I say I want to find some best practice on this topic, I mean I want to find some articles talking how to manage the templates in a better way.

I've found some useful information here:
http://berzniz.com/post/24743062344/handling-handlebars-js-like-a-pro

Still wondering how other projects solved this problem.

I'm go on looking for some methods to manage all my template files in a better way, also any other projects that have already solved this problem or information would be great appreciated!
Thanks again for all your help! : )

@windameister
Copy link

Solved, use grunt http://gruntjs.com/ to build the whole project, invoke r.js and compile all templates in grunt is great. Thanks again.

@leonardehrenfried
Copy link

I managed to create a minimal test case for this problem: http://jsbin.com/ezidox/25

Only fails with Safari 6 and with the console closed.

Since it only starts failing after a few iterations, it looks like it could be due to a JS engine optimisation being too aggressive. It definitely looks like a bug in Safari 6.

@leonardehrenfried
Copy link

I have published a patched Handlebars 1.0.rc2 with the fix suggested in #440: https://s3-eu-west-1.amazonaws.com/le-public/handlebars-quoted-string-fix.js

This seems really dirty to me but it solves some of the problems associated with the Safari bug.

@GianlucaGuarini
Copy link

It seems that my fix is useful just to avoid Safari errors, but some templates don't get rendered. I think the problem is probably is generated by the context lost. Safari seems indeed to loose the reference to {{this}}

@psayre23
Copy link

I'm still having this problem. The simple bug fix renders empty strings where there should be content. Something a simple as:

['first', 'second', 'third']

<ul>
    {{#each this}}
    <li>{{this}}</li>
    {{/each}}
</ul>

Just renders as:

<ul>
    <li></li>
    <li></li>
    <li></li>
</ul>

How the heck do I get around that?

@leonardehrenfried
Copy link

Are you using version rc3 of handlebars?

On Tuesday, February 26, 2013, psayre23 wrote:

I'm still having this problem. The simple bug fix renders empty strings
where there should be content. Something a simple as:

['first', 'second', 'third']

    {{#each this}}
  • {{this}}
  • {{/each}}

Just renders as:

How the heck do I get around that?


Reply to this email directly or view it on GitHubhttps://github.com//issues/373#issuecomment-14084775
.

@psayre23
Copy link

Yes. I originally had rc1, but when I ran into this problem I upgraded to rc3. My templates still crash, but now with a different error. It's now an error coming from within the compiled code. I'll send a stack trace as soon as I can.

~ Paul

On Feb 25, 2013, at 11:07 PM, Leonard Ehrenfried [email protected] wrote:

Are you using version rc3 of handlebars?

On Tuesday, February 26, 2013, psayre23 wrote:

I'm still having this problem. The simple bug fix renders empty strings
where there should be content. Something a simple as:

['first', 'second', 'third']

    {{#each this}}
  • {{this}}
  • {{/each}}

Just renders as:

How the heck do I get around that?


Reply to this email directly or view it on GitHubhttps://github.com//issues/373#issuecomment-14084775
.


Reply to this email directly or view it on GitHub.

@psayre23
Copy link

Here's my stack:

'undefined' is not an object
program2 @http://192.168.10.63/js/lib/handlebars.js:2178
@http://192.168.10.63/js/lib/handlebars.js:114
program1 @http://192.168.10.63/js/lib/handlebars.js:2178
@http://192.168.10.63/js/lib/handlebars.js:146
anonymous @http://192.168.10.63/js/lib/handlebars.js:2143
@http://192.168.10.63/js/lib/handlebars.js:2111
@http://192.168.10.63/js/Tmpl.js:35
[native code]

The error happens randomly, sometimes not at all. And it only happens on iPad iOS 6.1.*.

This morning, I tried precompiling from Node. This works for the short term, but long term I can't force each developer on my team to recompile after every change during development. I'm on my 5th day of debugging, so let me know what else I can provide.

~ Paul

@kpdecker
Copy link
Collaborator

Can you link to the tagged version of that handlebars.js file so the line
numbers can be looked up?

On Tue, Feb 26, 2013 at 11:03 AM, psayre23 [email protected] wrote:

Here's my stack:

'undefined' is not an object
program2 @http://192.168.10.63/js/lib/handlebars.js:2178
@http://192.168.10.63/js/lib/handlebars.js:114
program1 http://192.168.10.63/js/lib/handlebars.js:114program1 @http://192.168.10.63/js/lib/handlebars.js:2178
@http://192.168.10.63/js/lib/handlebars.js:146
anonymous @http://192.168.10.63/js/lib/handlebars.js:2143
@http://192.168.10.63/js/lib/handlebars.js:2111
@http://192.168.10.63/js/Tmpl.js:35
[native code]

The error happens randomly, sometimes not at all. And it only happens on
iPad iOS 6.1.*.

This morning, I tried precompiling from Node. This works for the short
term, but long term I can't force each developer on my team to recompile
after every change during development. I'm on my 5th day of debugging, so
let me know what else I can provide.

~ Paul


Reply to this email directly or view it on GitHubhttps://github.com//issues/373#issuecomment-14126150
.

@leonardehrenfried
Copy link

I think you will get a better response from the handlebars developers
if you provide a reproducible test case in something like jsfiddle or
jsbin. (You can grab handlebars from cdnjs)

If the exception isn't thrown on every compile, then run the example in a
loop a few hundred times.

On Tuesday, February 26, 2013, psayre23 wrote:

Here's my stack:

'undefined' is not an object
program2 @http://192.168.10.63/js/lib/handlebars.js:2178
@http://192.168.10.63/js/lib/handlebars.js:114
program1 http://192.168.10.63/js/lib/handlebars.js:114program1 @http://192.168.10.63/js/lib/handlebars.js:2178
@http://192.168.10.63/js/lib/handlebars.js:146
anonymous @http://192.168.10.63/js/lib/handlebars.js:2143
@http://192.168.10.63/js/lib/handlebars.js:2111
@http://192.168.10.63/js/Tmpl.js:35
[native code]

The error happens randomly, sometimes not at all. And it only happens on
iPad iOS 6.1.*.

This morning, I tried precompiling from Node. This works for the short
term, but long term I can't force each developer on my team to recompile
after every change during development. I'm on my 5th day of debugging, so
let me know what else I can provide.

~ Paul


Reply to this email directly or view it on GitHubhttps://github.com//issues/373#issuecomment-14126150
.

@karlwestin
Copy link
Contributor

Just wanted to fill my stuff in here, since i saw it too and spent a couple of hours with it today (Chrome Canary 27.0.1453.2). Leaving this for the next poor person to encounter this

First of all this problem seems very hard to reproduce consistently, it comes and goes (i could see it on every other reload for about 30 min, then it was completely gone). So i think all we can do for the moment is to gather information about it here.

Here's how the node looked for me that cause the problem:

{ // Handlebars.Ast.MustacheNode
    "type": "mustache",
    "escaped": true,
    "hash": null,
    "id": { // handlebars.AST.IdNode
        "type": "ID",
        "original": "this",
        "parts": [],
        "string": "",
        "depth": 0,
        "isSimple": true
    },
    "params": [],
    "eligibleHelper": true,
    "isHelper": null
}

Notice something funky on the idNode: parts is an empty array but original (which is derived from parts) is "this". Anyway this parts must have length "1", because otherwise the AST.IdNode constructor shouldn't have tagged it as simple, but the content can't be "this", cause then it had been marked as isScoped, and not isSimple.

Problem is, since i'm not super-familiar with the handlebars source, at the time i had tracked it down to the Handlebars.AST.IdNode constructor, i stopped seeing the error for some reason.

If you see this bug again, i'd ask you to:

Handlebars.AST.IdNode = function(parts) {
  this.type = "ID";
  this.original = parts.join(".");

  // PLEASE TRY: set breakpoint here conditional, if this.original == "this"
  // what's the current value of parts?

  var dig = [], depth = 0;

  for(var i=0,l=parts.length; i<l; i++) {
    var part = parts[i];

    if (part === ".." || part === "." || part === "this") {
      if (dig.length > 0) { throw new Handlebars.Exception("Invalid path: " + this.original); }
      else if (part === "..") { depth++; }
      else { this.isScoped = true; }
    }
    else { dig.push(part); }
  }

  this.parts    = dig;
  this.string   = dig.join('.');
  this.depth    = depth;

  // an ID is simple if it only has one part, and that part is not
  // `..` or `this`.
  this.isSimple = parts.length === 1 && !this.isScoped && depth === 0;

  // PLEASE TRY: set breakpoint here conditional, if this.original == "this" && this.isSimple == true
  // what's the current value of parts?

  this.stringModeValue = this.string;
};

Thanks

@jani00
Copy link

jani00 commented Jun 19, 2013

I managed to get around this bug by using {{./this}} insted of {{this}}. Using version 1.0.0.

@sunilgowda
Copy link

I had a similar problem with identical symptoms. Upon opening the error console I would see a javascript 'type error' with the error disappearing when the developer console was open. None of the recommendations here and on SO (precompile, rename to compiled file to have .js extension) fixed it for me though.

After a few of frustrating debugging, I was able to work around it. One of my JSON properties was a number. Handlerbars.Utils.escapeExpressions would throw the type error in the string.toString() method when trying to render that property. Funnily enough, if I added a console.log(typeof string); line, the error would disappear. My fix/workaround was to just make it a string in the JSON.

@jasonmadigan
Copy link

Seem to be experiencing the exact same bug myself using Handlebars 2.0.0-alpha4 with Chrome 36. Canary and Chrome Beta (37) seem to be unaffected.

@lamarant
Copy link

I suddenly encountered this same bug in Chrome 36 with Handlebars 1.0.rc.1. Updating handlebars to version 1.3.0 seemed to do the trick.

@atondelier
Copy link

This issue is the only one we found on the internet about the issue we just solved in our case, so we put our conclusions here.

FYI, we encountered a very close rendering bug on Safari on iOS 9.2.1. We receive a JSON object with a string in it. This string is copied by models contructors and then copied again the this model constructor is called as a cloner. The same string is bound to multiple objects and Safari does an optimization. This string is then breaking the renderer after multiple usages (more than 7).

We detected the issue JavaScript side when reading the type of this string with typeof. In the buggy case, it said "object".

We succeed in fixing the rendering issue by forcing the engine to de-optimize this string. We are going to find the less consuming way of forcing de-optimization but what works for now is:

myObject.myString.split('').join('');

@shreymahendru
Copy link

Hey, @atondelier thanks for that comment. I encountered the same issue with Safari 11 (macOS). The string value in an object was being changed by safari into a garbage value everywhere and your solution fixed it. Would this be a Safari bug?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests