Mesh iteration.

Description

Iteration in Guise Mesh is essential. We should generally follow Thymeleaf iteration, but the approach of other frameworks should be investigated as well.

The final implementation uses the following attributes:

  • mx:each - The MEXL expression indicating the thing being iterated.

  • mx:item-var - Indicates the name of the iteration item variable; if not set, defaults to it.

  • mx:index-var - Indicates the name of the iteration index variable; if not set, defaults to i.

  • mx:iter-var - Indicates the name of the iterator, with additional convenience methods such as isFirst(); if not set, defaults to iter.

The following types of iteration source objects are supported:

This implementation supports the following source types: •Object[] (of any non-primitive type)

  • int[]

  • long[]

  • double[]

  • Iterable

  • Iterator

  • Enumeration

  • Stream

  • Map (returns iteration of Map.Entry)

  • Object (returns iteration of single object)

Environment

None

Activity

Show:
Garret Wilson
March 27, 2021, 3:26 PM

There's still one issue to consider: what do we do if an iteration item evaluates to null? Currently this will result in an error when we try to set the value in the context, because Mesh context variables don't support null at the moment (by design). Some use cases may want to show some placeholder element for the missing item, showing some text such as "unavailable". Other use cases may want to skip the missing elements altogether. This requires more consideration; for the moment we'll leave in the restriction against null iteration elements.

Garret Wilson
January 2, 2021, 6:04 PM

There's a tiny issue in the initial implementation we need to fix: if the mx:each expression yields null, then we neglect to remove the original element. The bug is that we consider an empty expression result as equivalent to there not being an mx:each attribute at all.

Garret Wilson
December 30, 2020, 2:05 PM

Let me summarize the requirements:

  • The iterable thing being iterated over.

  • A variable for the iteration item.

  • A variable for the iteration index.

  • Other iteration status information, such as whether the item is the first/last.

  • A way to access the parent iteration (as you can do with AgularJS $parent, although that apparently works for any scope).

Putting that together, my current thinking is:

  • mx:each="list" indicates the thing being iterated.

  • mx:item-var="it" indicates the name of the iteration item variable; if not set, defaults to it.

  • mx:index-var="i" indicates the name of the iteration index variable; if not set, defaults to i.

  • mx:iter-var="iter" indicates the name of the iteration status variable (to be defined); if not set, defaults to iter.

Garret Wilson
December 29, 2020, 6:32 PM
Edited

The attribute form is obviously more concise than the element form, because it doesn't require an extra element just to reproduce e.g. a <li> element.

If we leave the variable declaration in the expression, that means Guise Mesh will have to perform an extra layer of (very light) parsing in order to get the variable name, and then delegate to the expression engine to get the data source. Another approach would be to create a separate attribute just for the variable definition, for example <div mx:each="list" mx:var="item"> or <div mx:each="item" mx:in="list">. If we later decide to allow text templating, this approach would obviously differ from that used in the script, e.g. JEXL for(item : list) or Liquid {% for item in list %}.

Another consideration is whether we require some expression delimiters for variables such as mx:in="^{list}" rather than just mx:in="list". Thymeleaf and JSTL require that the data source variable be delimited, but note that e.g. AngularJS and Vue.js do not. JSTL requires the delimiters to indicates that the entire string is an expression. Thymeleaf already considers the string to be an expression, but without the delimiters would consider list to be a "literal token". To me requiring the delimiters seems to verbose; plus there's no reason to add delimiters until we need them. If the string is an expression by default, my initial inclination is to just recognize the data source variable with no need for delimiters, e.g. mx:in="list" or mx:each="item : list".

Garret Wilson
December 29, 2020, 6:19 PM
Edited

Here are some some templating approaches to iteration:

  • Thymeleaf: th:each="item : ${list}"

  • JEXL: for(item : list)

  • JSTL: <c:forEach var="item" items="${list}"> or <c:forEach var="num" begin="0" end="8" step="2">

  • AngularJS: ng-repeat="item in list"> or ng-repeat="(key, value) in map">

  • Angular: *ngFor="let item of list; index as i"

  • Vue.js: v-for="item in list"

  • Handlebars: {{#each list}} (this is item variable)

Fixed

Assignee

Garret Wilson

Reporter

Garret Wilson

Labels

None

Epic Link

Components

Fix versions

Priority

Critical