feat(for): Allow iterating over dictionary key and value

Closes #94
This commit is contained in:
Kyle Fuller
2017-04-19 00:03:47 +01:00
parent a8d680b30e
commit 89256b96f4
5 changed files with 61 additions and 6 deletions

View File

@@ -1 +1 @@
3.0.1 3.1

View File

@@ -17,6 +17,14 @@
{% endif %} {% endif %}
``` ```
- `for` block now allows you to iterate over array of tuples or dictionaries.
```html+django
{% for key, value in thing %}
<li>{{ key }}: {{ value }}</li>
{% endfor %}
```
### Bug Fixes ### Bug Fixes
- You can now use literal filter arguments which contain quotes. - You can now use literal filter arguments which contain quotes.

View File

@@ -53,13 +53,41 @@ class ForNode : NodeType {
self.where = `where` self.where = `where`
} }
func push<Result>(value: Any, context: Context, closure: () throws -> (Result)) rethrows -> Result {
if loopVariables.isEmpty {
return try context.push() {
return try closure()
}
}
if let value = value as? (Any, Any) {
let first = loopVariables[0]
if loopVariables.count == 2 {
let second = loopVariables[1]
return try context.push(dictionary: [first: value.0, second: value.1]) {
return try closure()
}
}
return try context.push(dictionary: [first: value.0]) {
return try closure()
}
}
return try context.push(dictionary: [loopVariables.first!: value]) {
return try closure()
}
}
func render(_ context: Context) throws -> String { func render(_ context: Context) throws -> String {
let resolved = try resolvable.resolve(context) let resolved = try resolvable.resolve(context)
var values: [Any] var values: [Any]
if let dictionary = resolved as? [String: Any], !dictionary.isEmpty { if let dictionary = resolved as? [String: Any], !dictionary.isEmpty {
values = Array(dictionary.keys) values = dictionary.map { ($0.key, $0.value) }
} else if let array = resolved as? [Any] { } else if let array = resolved as? [Any] {
values = array values = array
} else { } else {
@@ -68,7 +96,7 @@ class ForNode : NodeType {
if let `where` = self.where { if let `where` = self.where {
values = try values.filter({ item -> Bool in values = try values.filter({ item -> Bool in
return try context.push(dictionary: [loopVariables.first!: item]) { () -> Bool in return try push(value: item, context: context) {
try `where`.evaluate(context: context) try `where`.evaluate(context: context)
} }
}) })
@@ -84,8 +112,10 @@ class ForNode : NodeType {
"counter": index + 1, "counter": index + 1,
] ]
return try context.push(dictionary: [loopVariables.first!: item, "forloop": forContext]) { return try context.push(dictionary: ["forloop": forContext]) {
try renderNodes(nodes, context) return try push(value: item, context: context) {
try renderNodes(nodes, context)
}
} }
}.joined(separator: "") }.joined(separator: "")
} }

View File

@@ -111,6 +111,13 @@ func testForNode() {
let node = ForNode(resolvable: Variable("dict"), loopVariables: ["key"], nodes: nodes, emptyNodes: emptyNodes, where: nil) let node = ForNode(resolvable: Variable("dict"), loopVariables: ["key"], nodes: nodes, emptyNodes: emptyNodes, where: nil)
try expect(try node.render(context)) == "onetwo" try expect(try node.render(context)) == "onetwo"
} }
$0.it("renders supports iterating over dictionary") {
let nodes: [NodeType] = [VariableNode(variable: "key"), VariableNode(variable: "value")]
let emptyNodes: [NodeType] = [TextNode(text: "empty")]
let node = ForNode(resolvable: Variable("dict"), loopVariables: ["key", "value"], nodes: nodes, emptyNodes: emptyNodes, where: nil)
try expect(try node.render(context)) == "oneItwoII"
}
} }
} }

View File

@@ -19,6 +19,16 @@ A for loop allows you to iterate over an array found by variable lookup.
{% endfor %} {% endfor %}
</ul> </ul>
The ``for`` tag can iterate over dictionaries.
.. code-block:: html+django
<ul>
{% for key, value in dict %}
<li>{{ key }}: {{ value }}</li>
{% endfor %}
</ul>
The ``for`` tag can contain optional ``where`` expression to filter out The ``for`` tag can contain optional ``where`` expression to filter out
elements on which this expression evaluates to false. elements on which this expression evaluates to false.