List Rendering

We can use the `for()` method to render a list of items based on a reactive array. Lists in requery-js support both keyed and non-keyed rendering.

Written By Joel

Last updated About 1 month ago


Basic Usage

For simple lists of primitive values, you can use the non-keyed approach:

HTML

<my-component>
  <div rq="item">
   <span rq="title"></span>
  </div>
</my-component>

JavaScript

defineComponent("my-component", {
  store: {
    todos: ["Learn requery", "Build app"]
  },
  setup(component, props, store) {
    component.query("item").for(
      () => store.todos,
      (el, item) => {
        el.query("title").text(() => item);
      }
    );
  }
})

Object List

Lists often need to contain more than just a single value. Here's how to work with objects in a list:

HTML

<my-component>
  <div rq="item">
    <span rq="status"></span>
    <span rq="title"></span>
  </div>
</my-component>

JavaScript

defineComponent("my-component", {
  store: {
    todos: [
      {
        text: "Learn requery",
        completed: true
      },
      {
        text: "Build app",
        completed: false
      }
    ]
  },
  setup(component, props, store) {
    component.query("item").for(
      () => store.todos,
      (el, todo) => {
        el.query("title").text(() => todo.text);
        el.query("status").text(() => 
          todo.completed ? "✅" : "❌"
        );
      }
    );
  }
})

Keyed Lists

When rendering lists without passing a key function, requery-js uses an "in-place" update strategy. This means elements are updated in their current position, regardless of how the data has moved. While efficient, this approach isn't suitable when:

  • List items contain form inputs

  • You're animating list changes

  • You’re using 3rd party libraries in list items

To reorder the underlying DOM nodes when updating a list you can provide a key function.

Important

The key function should return primitive values (strings or numbers). Don't use objects as keys

defineComponent("my-component", {
  store: {
    todos: [
      {
        id: 1,
        text: "Learn requery",
        completed: false
      },
      {
        id: 2,
        text: "Build app",
        completed: true
      }
    ]
  },
  setup(component, props, store) {
    component.query("item").for(
      () => store.todos,
      (todo) => todo.id, // Key function tells requery-js how to track items
      (el, todo) => {
        el.query("title").text(() => todo.text);
        el.query("status").text(() =>
          todo.completed ? "✅" : "❌"
        );
      }
    );
  }
})

Non-keyed vs. Keyed Lists

It's recommended to use a key function with for() whenever possible, unless:

  • Your list contains only simple text or numbers

  • You specifically want in-place updates for performance

Array Change Detection

Mutation Methods

requery-js automatically detects the following array changes:

  • push() / pop()

  • shift() / unshift()

  • splice()

  • sort() / reverse()

Replacing an Array

Array updates can be done through direct mutations or by creating new arrays. Methods like push() and splice() modify the original array, while methods like filter() and slice() create new arrays. When using methods that return new arrays, you need to reassign the result:

store.todos = store.todos.filter((item) => item.completed)

Displaying Filtered/Sorted Results

When displaying filtered or sorted results, return the transformed array in your getter function:

HTML

<my-component>
  <button rq="toggle-completed">Hide Completed</button>
  <button rq="toggle-sort">Sort by Name</button>

  <div rq="todo">
    <span rq="status"></span>
    <span rq="title"></span>
  </div>
</my-component>

JavaScript

defineComponent('my-component', {
  store: {
    todos: [
      { id: 1, text: 'Learn requery', completed: false },
      { id: 2, text: 'Build app', completed: true },
    ],
    hideCompleted: false,
    sortByName: false,
  },
  setup(component, props, store) {
    component.query('todo').for(
      () => {
        // Start with the original array
        let result = store.todos;

        // Apply filter if needed
        if (store.hideCompleted) {
          result = result.filter((todo) => !todo.completed);
        }

        // Apply sort if needed
        if (store.sortByName) {
          result = [...result].sort((a, b) => a.text.localeCompare(b.text));
        }

        return result;
      },
      (todo) => todo.id,
      (el, todo) => {
        el.query('title').text(() => todo.text);
        el.query('status').text(() => (todo.completed ? '✅' : '❌'));
      }
    );

    // Toggle buttons
    component.query('toggle-completed').on('click', () => {
      store.hideCompleted = !store.hideCompleted;
    });

    component.query('toggle-sort').on('click', () => {
      store.sortByName = !store.sortByName;
    });
  },
});

Be careful with reverse() and sort() in your getter functions! These methods mutate the original array. Create a copy using the spread operator before applying these methods:

return [...todos].reverse()

Could not load article.

Could not load article.