Skip to content

Commit

Permalink
Update <for> to use tag params and add migration (#1238)
Browse files Browse the repository at this point in the history
  • Loading branch information
DylanPiercey committed Feb 4, 2019
1 parent f5a2b56 commit b2b1bd6
Show file tree
Hide file tree
Showing 37 changed files with 1,016 additions and 607 deletions.
4 changes: 2 additions & 2 deletions docs/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ class {
<div>
<h1 key="header">Hello</h1>
<ul>
<for(color in ['red', 'green', 'blue'])>
<for|color| of=['red', 'green', 'blue']>
<li key="colors[]">${color}</li>
</for>
</ul>
Expand Down Expand Up @@ -382,7 +382,7 @@ The `key` attribute can be used to pair HTML elements or UI components that are

```marko
<ul>
<for(user in input.users)>
<for|user| of=input.users>
<li key=user.id>${user.name}</li>
</for>
</ul>
Expand Down
143 changes: 44 additions & 99 deletions docs/core-tags.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,28 +38,24 @@ And support complex expressions:

### `<for>`

The `<for>` tag allows iterating over an array of items:
The `<for>` tag allows you to map an iterable, object properties or a range of numbers into a template. Like some of the other core tags, `<for>` relies on [tag parameters](./syntax.md#tag-body-parameters) to provide data about the current iteration to its body.

```marko
<ul>
<for(color in colors)>
<li>${color}</li>
</for>
</ul>
```
#### Iterating over a list

It may also be applied as an attribute:
Much like the [`for...of`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of) loop in javascript, providing an `of` attribute will iterate over the provided array/iterable. The current item, index, and the input list will be provided as [tag parameters](./syntax.md#tag-body-parameters).

```marko
<ul>
<li for(color in colors)>${color}</li>
<for|color, index| of=colors>
<li>${index}: ${color}</li>
</for>
</ul>
```

With either of the above templates, and the following value for `colors`:
With the following value for `colors`:

```js
var colors = ["red", "green", "blue"];
const colors = ["red", "green", "blue"];
```

The output HTML would be the following:
Expand All @@ -72,123 +68,72 @@ The output HTML would be the following:
</ul>
```

#### Loop Status Variable
> **Pro Tip**: The `<for>` tag with an `of` attribute can iterate over any iterable just like the JavaScript [`for...of`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of) loop. In order to iterate over other "array like" objects (ones that just have a `length` property for example) you can use [`Array.from`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from).
>
> ```marko
> <for|letter, index| of=Array.from({ 0: "a", 1: "b", length: 2 })>
> ${index + 1}: ${letter}
> </for>
> ```
#### Iterating over an objects properties

The `for` directive also supports a loop status variable in case you need to know the current loop index. For example:
Much like the [`for...in`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in) loop in javascript, providing an `in` attribute will iterate over the provided objects properties. The current property name and its value will be provided as [tag parameters](./syntax.md#tag-body-parameters).

```marko
<ul>
<li for(color in colors | status-var=loop)>
${color}
${loop.getIndex()+1}) of ${loop.getLength()}
<if(loop.isFirst())> - FIRST</if>
<if(loop.isLast())> - LAST</if>
</li>
<for|name, enabled| in=settings>
<li>${name}: ${enabled ? "on" : "off"}</li>
</for>
</ul>
```

##### Loop Status Methods

###### `getLength()`

Returns the length of the array
With the following value for `settings`:

###### `getIndex()`

Returns the current loop index

###### `isFirst()`

Returns `true` if the current index is the first index, otherwise `false`

###### `isLast()`

Returns `true` if the current index is the last index, otherwise `false`

#### Loop Separator

Used for separating values in a loop by characters. The first element will not
be prefixed and the last element will not be suffixed with the `separator`:

```marko
<div>
<!-- Output: red, green, blue -->
<span for(color in colors | separator=", ") style="color: ${color}">
${color}
</span>
</div>
```js
const settings = {
"Dark Mode": false,
Fullscreen: true
};
```

#### Range Looping

A range can be provided in the following format; `<var-name> from <from> to <to>[ step <step>]`.

The `from`, `to` and `step` values must be numerical expressions. If not specified, step defaults to 1.
The output HTML would be the following:

```marko
```html
<ul>
<li for(i from 0 to 10)>
${i}
</li>
<li>Dark Mode: off</li>
<li>Fullscreen: on</li>
</ul>
```

#### Iterating between a range of numbers

The final variant allows you to iterate between two numbers. You must provide a `from` and `to` attribute, along side an optional `step` attribute. If not specified, `step` defaults to 1. The current number in the range will be provided as [tag parameters](./syntax.md#tag-body-parameters).

```marko
<ul>
<li for(i from 0 to 10 step 2)>
${i}
</li>
<for|i| from=0 to=10>
<li>${i}</li>
</for>
</ul>
```

```marko
<ul>
<li for(i from 0 to myArray.length-1)>
${myArray[i]}
</li>
<for|i| from=0 to=10 step=2>
<li>${i}</li>
</for>
</ul>
```

#### Property Looping

```marko
<ul>
<li for(name, value in settings)>
<b>${name}</b>: ${value}
</li>
<for|i| from=0 to=(myArray.length - 1)>
<li>${myArray[i]}</li>
</for>
</ul>
```

#### Native JavaScript for-loop

```marko
<for(var i=1; i<=3; i++)>
${i}
</for>
```

#### Custom Iterator

```marko
static function reverseIterator(arrayList, callback) {
for(var i=arrayList.length-1; i>=0; i--){
callback(arrayList[i]);
}
}
<div for(item in ['a', 'b', 'c'] | iterator=reverseIterator)>
${item}
</div>
```

Output:

```html
<div>c</div>
<div>b</div>
<div>a</div>
```

### `<while>`

Any element can be repeated until a condition is met by using the `while` directive. The directive can be applied as an element or as an attribute.
Expand Down
12 changes: 6 additions & 6 deletions docs/marko-vs-react.md
Original file line number Diff line number Diff line change
Expand Up @@ -397,8 +397,8 @@ JSX is syntactic sugar on top of JavaScript, but it requires expressions, so
simple things like an `if/else/for` statement don’t work on their own within a JSX element. As
a result, you must either use a ternary expression, an immediately invoked
function expression, function call expression, or the experimental `do {}` expression
(stage 0 at the time of writing). This is not an issue for Marko, and directives
such as `if()` and `for()` can be used anywhere as shown below:
(stage 0 at the time of writing). This is not an issue for Marko, and tags
such as `if()` and `for` can be used anywhere as shown below:

#### React JSX

Expand Down Expand Up @@ -474,7 +474,7 @@ function renderColors(colors) {

```marko
<ul>
<for(color in colors)>
<for|color| of=colors>
<li.color style={ backgroundColor: color }>
${color}
</li>
Expand Down Expand Up @@ -508,7 +508,7 @@ favor of indentation. Here’s how the Marko syntax options compare:

```marko
<ul>
<for(color in colors)>
<for|color| of=colors>
<li>${color}</li>
</for>
</ul>
Expand All @@ -518,15 +518,15 @@ favor of indentation. Here’s how the Marko syntax options compare:

```marko
ul
for(color in colors)
for|color| of=colors
li -- ${color}
```

#### Marko mixed syntax

```marko
ul
for(color in colors)
for|color| of=colors
<li>${color}</li>
```

Expand Down
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"warp10": "^2.0.1"
},
"devDependencies": {
"@marko/migrate": "^4.1.0",
"@marko/migrate": "^4.1.1",
"babel-cli": "^6.24.1",
"babel-core": "^6.24.1",
"babel-plugin-minprops": "^2.0.1",
Expand Down
69 changes: 32 additions & 37 deletions src/compiler/Builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,47 +203,42 @@ class Builder {
return new Expression({ value });
}

forEach(varName, inExpression, body) {
if (arguments.length === 1) {
var def = arguments[0];
return new ForEach(def);
} else {
varName = makeNode(varName);
inExpression = makeNode(inExpression);
return new ForEach({ varName, in: inExpression, body });
}
forEach(params, ofExpression, body) {
return new ForEach(
arguments.length === 1
? params
: {
params: makeNode(params),
of: makeNode(ofExpression),
body
}
);
}

forEachProp(nameVarName, valueVarName, inExpression, body) {
if (arguments.length === 1) {
var def = arguments[0];
return new ForEachProp(def);
} else {
nameVarName = makeNode(nameVarName);
valueVarName = makeNode(valueVarName);
inExpression = makeNode(inExpression);
return new ForEachProp({
nameVarName,
valueVarName,
in: inExpression,
body
});
}
forEachProp(params, inExpression, body) {
return new ForEachProp(
arguments.length === 1
? params
: {
params: makeNode(params),
in: makeNode(inExpression),
body
}
);
}

forRange(varName, from, to, step, body) {
if (arguments.length === 1) {
var def = arguments[0];
return new ForRange(def);
} else {
varName = makeNode(varName);
from = makeNode(from);
to = makeNode(to);
step = makeNode(step);
body = makeNode(body);

return new ForRange({ varName, from, to, step, body });
}
forRange(params, from, to, step, body) {
return new ForRange(
arguments.length === 1
? params
: {
params: makeNode(params),
from: makeNode(from),
to: makeNode(to),
step: makeNode(step),
body
}
);
}

forStatement(init, test, update, body) {
Expand Down
6 changes: 0 additions & 6 deletions src/compiler/CompileContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,6 @@ const helpers = {
escapeStyle: "xc",
forEach: "f",
forEachProp: { module: "marko/runtime/helper-forEachProperty" },
forEachPropStatusVar: {
module: "marko/runtime/helper-forEachPropStatusVar"
},
forEachWithStatusVar: {
module: "marko/runtime/helper-forEachWithStatusVar"
},
forRange: { module: "marko/runtime/helper-forRange" },
getWidgetFromOut: {
module: "marko/components/legacy/helper-getWidgetFromOut"
Expand Down
Loading

0 comments on commit b2b1bd6

Please sign in to comment.