Wow! This is a pretty tricky situation.
First off, you have to understand that calling l.asOptionsC() actually has an attr() call because this.forEach calls can.each which has this little nugget of code which uses .attr:
- if (elements.attr) {
- elements.attr('length');
- }
Now let's look at why calling this function inside the returned function or outside make a difference.
In both cases the compiled template looks like this:
___v1ew.push(can.view.txt(1, 'p', 1, this, function() {
return foo(modelList)
}));
can.view.txt calls the wrapping function and checks if any attr calls are made. If there are attr calls, it does some live binding stuff, otherwise, it just calls the function to generate HTML.
When the call to l.asOptionsC() is outside the returned function, can.view.txt finds an attr call (length) and starts to setup live binding, which gets us into trouble.
When the call to l.asOptionsC() is inside the returned function, can.view.txt only finds a return statement and sees no attr calls, so it just does normal EJS stuff (no live binding).
This means live binding will not work in EJS helpers defined like this. I also tried it with a regular EJS helper that just modifies an attr and this also does not work.
Here's a fiddle with some examples.