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

Add for(integerVal) counting loops #709

Merged
merged 3 commits into from
Dec 23, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions docs/helpers/for-of.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,32 @@
```
@codepen

If a positive integer value is passed, it will loop that number of times.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably clean up the signature docs, specifically around this part:

EXPRESSION {KeyLookup Expression|Call Expression}: An expression that typically returns a list like data structure. If the value of the EXPRESSION is an observable list (for example: can-define/list/list), the resulting HTML is updated when the list changes. When a change in the list happens, only the minimum amount of DOM element changes occur. The list itself can also change, and a difference will be performed, which also will perform a minimal set of updates.

I think we should make it more clear what happens if the expression evaluates to:

  • An array-like structure (linking to canReflect.isListLike would be good here)
  • An object-like (key-value) data structure
  • A Number

You can access the current index with `scope.index`:

```js
import {stache} from "can";

var view = stache(`
<ul>
{{# for(ittr of this.integerValue) }}
<li>{{scope.index}}: {{ ittr }}</li>
{{/ for }}
</ul>
`);

var data = {
integerValue: 3
};

var frag = view(data);
console.log(frag.firstElementChild.innerHTML)
//-> <li>0: 0</li><li>1: 1</li><li>2: 2</li>

document.body.appendChild(frag);
```
@codepen

If the `EXPRESSION` is falsy or an empty object or list, the `INVERSE` section will be rendered:

```js
Expand Down
28 changes: 28 additions & 0 deletions helpers/-for-of-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,31 @@ QUnit.test("for(value of object) works if value is keyed with a dot (issue#698)"

assert.equal( frag.firstChild.className, "[first-FIRST][second.key.has.dot-SECOND]");
});

QUnit.test("for(integerValue) works", function (assert) {
var template = stache("<div>{{#for(integerValue)}}[{{scope.index}}]{{/for}}</div>");
var frag = template({
integerValue: 3
});
assert.equal( frag.firstChild.innerHTML, "[0][1][2]" );
});

QUnit.test("for(ittr of intVal) works", function (assert) {
var template = stache("<div class='{{#for(ittr of object.intVal)}}[{{scope.index}}-{{ittr}}]{{/for}}'></div>");
var object = {
intVal: 6
};
var frag = template({
object: object
});

assert.equal( frag.firstChild.className, "[0-0][1-1][2-2][3-3][4-4][5-5]" );
});

QUnit.test("for(integerValue) uses else block for negatives", function (assert) {
var template = stache("<div>{{#for(integerValue)}}[{{scope.index}}]{{else}}val is negative{{/for}}</div>");
var frag = template({
integerValue: -3
});
assert.equal( frag.firstChild.innerHTML, "val is negative" );
});
20 changes: 20 additions & 0 deletions helpers/-for-of.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,23 @@ var bindAndRead = function (value) {
}
};

function forOfInteger(integer, variableName, options) {
var result = [];
for (var i = 0; i < integer; i++) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems there's some indentation inconsistency

var variableScope = {};
if(variableName !== undefined){
variableScope[variableName] = i;
}
result.push(
options.fn( options.scope
.add({ index: i }, { special: true })
.addLetContext(variableScope) )
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we should create a variable?

{{# for(ittr of this.integerValue) }}
  <li>{{scope.index}}: {{ ittr }}</li>
{{/ for }}

I sorta think access should be only through scope.index like: {{# for( this.integerValue ) }} {{scope.index}} {{/for}}.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On second thought, I think it's ok to make {{# for(ittr of this.integerValue) }} work. But I wouldn't show this as the main way of using it.

);
}

return options.stringOnly ? result.join('') : result;
}

function forOfObject(object, variableName, options){
var result = [];
canReflect.each(object, function(val, key){
Expand Down Expand Up @@ -68,6 +85,9 @@ var forHelper = function(helperOptions) {
options = args.pop(),
resolved = bindAndRead(items);

if(resolved && resolved === Math.floor(resolved)) {
return forOfInteger(resolved, variableName, helperOptions);
}
if(resolved && !canReflect.isListLike(resolved)) {
return forOfObject(resolved,variableName, helperOptions);
}
Expand Down