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

[Question] Using with block to step up in context not working after upgrade from 1.0 to 1.11.5 #505

Open
breenyoung opened this issue Feb 24, 2022 · 3 comments
Labels

Comments

@breenyoung
Copy link

Hi guys, was previous using Handlebars.Net 1.0 on project and recently upgraded to 1.11.5 and have noticed one of my previously working templates no longer works. It involves being in a each loop and having to step up into the parent context for a short period using a with block. Is this no longer possible without specifying exactly what you're looking for on the root level.

{{#each data}}

   {{#with ..}}
      Manipulate things in the root context..
   {{/with}}

{{/each}}

I've tried swapping out .. with @root and same problem, gives me an error:
'Value cannot be null, Parameter: source'

@pigiax
Copy link

pigiax commented Feb 25, 2022

Seems to be the same on 2.1.0

@breenyoung
Copy link
Author

breenyoung commented Feb 25, 2022

After looking thru the source code for 1.11.5 and an older 1.x version it looks like the 'with' helper changed. It used to unconditionally call 'options.Template(output, arguments[0]);' in which passing '..' would work.

Now in 1.11.5 and onwards it seems to call 'IsTruthyOrNonEmpty' on the argument to support the possibility of an Inverse return, which in turn calls 'IsFalsyOrEmpty' which during it's execution attempts to see if the arg is an IEnumerable and if it is call .Any() on it.

The stacktrace is stemming from there on the attempt to call .Any on '..'

Old 'with' helper:

[Description("with")]
        public static void With(TextWriter output, HelperOptions options, dynamic context, params object[] arguments)
        {
            if (arguments.Length != 1)
            {
                throw new HandlebarsException("{{with}} helper must have exactly one argument"); 
            }
            options.Template(output, arguments[0]);
        }

New 'with' helper

[Description("with")]
        public static void With(TextWriter output, HelperOptions options, dynamic context, params object[] arguments)
        {
            if (arguments.Length != 1)
            {
                throw new HandlebarsException("{{with}} helper must have exactly one argument");
            }

            if (HandlebarsUtils.IsTruthyOrNonEmpty(arguments[0]))
            {
                options.Template(output, arguments[0]);
            }
            else
            {
                options.Inverse(output, context);
            }
        }

Further down the stack, IsTruthyOrNonEmpty calls IsFalsyOrNonEmpty and the .Any() check is attempted

        public static bool IsFalsyOrEmpty(object value)
        {
            if(IsFalsy(value))
            {
                return true;
            }
            else if (value is IEnumerable && ((IEnumerable)value).OfType<object>().Any() == false) // Blows up here
            {
                return true;
            }
            return false;
        }

@oformaniuk
Copy link
Member

@breenyoung , @pigiax , test below works fine in 2.1.0. The test is based on @breenyoung template. Am I missing something? If so, can you please provide a test that fails?

As for 1.11.5 version, it is not maintained. If you're not able to upgrade to the latest, we can work together to publish a fix to 1.11+ package in case the problem is critical.

[Fact]
public void ObjectEnumeratorWithReferenceToParent()
{
    var handlebars = Handlebars.Create();   
    var source = "{{#each enumerateMe}}{{#with ..}}{{inRoot}} {{/with}}{{/each}}";
    var template = handlebars.Compile(source);
    var data = new
    {
        inRoot = 42,
        enumerateMe = new
        {
            foo = "1",
            bar = "2"
        }
    };
    var result = template(data);
    Assert.Equal("42 42 ", result);
}

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

No branches or pull requests

3 participants