Control flow

Control flow is the order in which the DOM nodes and components are rendered. This order can be manipulated with conditional directives and loops.

Conditional directives

Use the :if attribute to conditionally render a block. The block will only be rendered if the given expression returns a truthy value.

<figcaption :if="caption">{ caption }</figcaption>

:else

Use :else to indicate an “else block” for :if

<div>
  <h1 :if="cool">I'm cool!</h1>
  <h1 :else>I'm ordinary</h1>

  <button @click="cool = !cool">Toggle</button>
</div>

A :else element must immediately follow a :if or a :else-if element - otherwise it will not be recognized.

:else-if

The :else-if serves as an “else if block” for :if. It can be chained multiple times:

<b :if="type == 'A'">A</b>

<b :else-if="type == 'B'">B</b>

<b :else-if="type == 'C'">C </b>

<b :else>Not A/B/C</b>

Similar to :else, a :else-if element must immediately follow a :if or a :else-if block.

Loops

Nue uses :for attribute to render over arrays, objects, and components. Nue has a somewhat simpler syntax for loops than Vue. For example, you don’t need to maintain a loop state with a special “key” variable.

Array loops

Arrays are looped with syntax like item in items, where items is the data array and item is the element being iterated:

<ul>
  <li :for="item in items">
    { item.lang } = { item.text }
  </li>

  <script>
    items = [
      { lang: 'en', text: 'Hello' },
      { lang: 'es', text: 'Hola' },
      { lang: 'it', text: 'Ciao' },
      { lang: 'fi', text: 'Moi' }
    ]
  </script>
</ul>

Inside the loop, template expressions have access to the item being looped, as well as all parent properties. In addition, :for supports an optional second alias for the index of the looped item:

<li :for="(item, index) in items">
  { index }: { item.text }
</li>

You can use destructuring for the item variable similar to destructuring function arguments:

<li :for="{ lang, text } in items">
  { lang } = { text }
</li>

Destructuring and the index variable can be used together:

<li :for="({ text }, index) in items">
  { text } { index }
</li>

Loops can be nested and each :for scope has access to all parent scopes.

<li :for="item in items">
  <p :for="child in item.children">
    { item.text } { child.text }
  </p>
</li>

You can also use of as the delimiter instead of in so that it is closer to JavaScript’s syntax for iterators:

<div :for="item of items"></div>

Object loops

You can loop through Object values using the standard Object.entries() method:

<ul>
  <li :for="[lang, text] in Object.entries(items)">
    { lang } = { text }
  </li>
  <script>
    items = {
      en: 'Hello',
      es: 'Hola',
      it: 'Ciao',
      fi: 'Moi'
    }
  </script>
</ul>

You can provide an alias for the index variable as the third argument:

<ul>
  <li :for="[lang, text, index] in Object.entries(items)">
    { index } / { lang } = { text }
  </li>
</ul>

Conditional loops

When they exist on the same node, :if has a higher priority than :for. That means the :if is executed first.

<li :for="todo in todos" :if="todos">
  {{ todo.name }}
</li>

Use the standard hidden property to conditionally hide elements inside a loop::

<li :for="todo in todos" :hidden="!todo.is_complete">
  {{ todo.name }}
</li>

Component loops

Components can also be looped:

<my-component :for="item in items"/>

You can pass the iterated data to the component with attributes:

<my-component
  :for="(item, index) in items"
  :item="item"
  :index="index"
/>

Or you can use :bind attribute to pass all the data at once:

<my-component :for="item in items" :bind="item"/>

The bind attribute makes the item properties accessible directly on the component. So instead of { item.title } you can write { title } inside the component.

Array updates

Nue can detect when a reactive array’s mutation methods are called and trigger necessary updates. These array methods are:

Replacing the array

Mutation methods mutate the original array they are called on. The non-mutating methods like filter(), concat() and slice() always return a new array in which case you should replace the old array with the new one and Nue will render accordingly:

search() {
  this.items = this.items.filter(item => item.text.match(/Foo/))
}

Animating new entries

Nue lets you define an oninsert callback function that is called every time a new item is added to any of the array properties in the component. This gives you the possibility to add a CSS transition effect (among other things) for the newly added dom nodes.

Example animation

Click on the “Add user” button below to see the animation in action

Source code

Here’s the full source code for the above demo:

<article @name="animation-demo" class="user-list">

  <button @click="addUser" :disabled="users[5]">
    Add user
  </button>

  <section class="user-list" translate="no">
    <media-object :for="user in users" :bind="user"/>
  </section>

  <script>

    // fill list with the first three available items
    constructor({ items }) {
      this.users = items.slice(0, 3)
    }

    // insert a new user from the available items
    addUser() {
      const { items, users } = this
      const user = items[users.length]
      if (user) users.push(user)
    }

    // add a CSS transition class to a newly added dom nodes
    oninsert(node) {
      setTimeout(() => node.classList.add('fade-in'), 1)
    }
  </script>

</article>