Skip to content

x:for Directive

The x:for directive is used to render lists by looping over arrays.

Basic Usage

Syntax

html
<template x:for="item in array">
  <!-- Content to be rendered for each item -->
</template>

Usage with Index

typescript
@Widget({
  selector: '.indexed-list',
  template: `
    <template x:for="item, index in items">
      <div>
        <span x:text="index"></span>: <span x:text="item.name"></span>
      </div>
    </template>
  `
})
export class IndexedListWidget {
  @xproperty()
  items: Array<{name: string}> = [
    { name: 'Item 1' },
    { name: 'Item 2' },
    { name: 'Item 3' }
  ];
}

Nested Properties

typescript
@Widget({
  selector: '.nested-list',
  template: `
    <template x:for="user in users">
      <div class="user-card">
        <h3 x:text="user.profile.name"></h3>
        <p x:text="user.profile.email"></p>
        <span x:text="user.status"></span>
      </div>
    </template>
  `
})
export class NestedListWidget {
  @xproperty()
  users = [
    {
      profile: { name: 'Ahmet', email: 'ahmet@example.com' },
      status: 'active'
    },
    {
      profile: { name: 'Mehmet', email: 'mehmet@example.com' },
      status: 'inactive'
    }
  ];
}

Event Binding

typescript
@Widget({
  selector: '.event-list',
  template: `
    <template x:for="item, index in items">
      <div class="item">
        <span x:text="item.name"></span>
        <button x:on:click="editItem(index)">Edit</button>
        <button x:on:click="deleteItem(index)">Delete</button>
      </div>
    </template>
  `
})
export class EventListWidget {
  @xproperty()
  items: Array<{name: string}> = [
    { name: 'Item 1' },
    { name: 'Item 2' }
  ];
  
  editItem(index: number): void {
    console.log('Editing:', this.items[index]);
  }
  
  deleteItem(index: number): void {
    this.items.splice(index, 1);
  }
}

Conditional Rendering

typescript
@Widget({
  selector: '.conditional-list',
  template: `
    <template x:for="item in items">
      <div x:class:active="item.isActive">
        <span x:text="item.name"></span>
        <span x:if="item.isNew">NEW</span>
        <span x:if="item.discount > 0" x:text="item.discount + '%'"></span>
      </div>
    </template>
  `
})
export class ConditionalListWidget {
  @xproperty()
  items = [
    { name: 'Product 1', isActive: true, isNew: true, discount: 20 },
    { name: 'Product 2', isActive: false, isNew: false, discount: 0 }
  ];
}

Dynamic List Management

typescript
@Widget({
  selector: '.dynamic-list',
  template: `
    <div>
      <input x:model="newItem" placeholder="New item">
      <button x:on:click="addItem">Add</button>
      
      <template x:for="item, index in items">
        <div class="item">
          <span x:text="item"></span>
          <button x:on:click="removeItem(index)">Delete</button>
        </div>
      </template>
      
      <p x:if="items.length === 0">List is empty</p>
    </div>
  `
})
export class DynamicListWidget {
  @xproperty()
  items: string[] = [];
  
  @xproperty()
  newItem: string = '';
  
  addItem(): void {
    if (this.newItem.trim()) {
      this.items.push(this.newItem.trim());
      this.newItem = '';
    }
  }
  
  removeItem(index: number): void {
    this.items.splice(index, 1);
  }
}

Nested Loops

typescript
@Widget({
  selector: '.nested-loop',
  template: `
    <template x:for="category in categories">
      <div class="category">
        <h3 x:text="category.name"></h3>
        <template x:for="product in category.products">
          <div class="product">
            <span x:text="product.name"></span>
            <span x:text="product.price"></span>
          </div>
        </template>
      </div>
    </template>
  `
})
export class NestedLoopWidget {
  @xproperty()
  categories = [
    {
      name: 'Electronics',
      products: [
        { name: 'Laptop', price: 15000 },
        { name: 'Phone', price: 8000 }
      ]
    },
    {
      name: 'Clothing',
      products: [
        { name: 'T-Shirt', price: 150 },
        { name: 'Pants', price: 300 }
      ]
    }
  ];
}

Computed Arrays

typescript
@Widget({
  selector: '.filtered-list',
  template: `
    <div>
      <input x:model="searchQuery" placeholder="Search...">
      
      <template x:for="item in filteredItems">
        <div>
          <span x:text="item.name"></span>
        </div>
      </template>
      
      <p x:if="filteredItems.length === 0">No results found</p>
    </div>
  `
})
export class FilteredListWidget {
  @xproperty()
  items = [
    { name: 'Apple' },
    { name: 'Pear' },
    { name: 'Banana' }
  ];
  
  @xproperty()
  searchQuery: string = '';
  
  @xcomputed({ dependencies: ['items', 'searchQuery'] })
  get filteredItems() {
    if (!this.searchQuery) {
      return this.items;
    }
    
    const query = this.searchQuery.toLowerCase();
    return this.items.filter(item => 
      item.name.toLowerCase().includes(query)
    );
  }
}

Performance Tips

Large Lists

typescript
@Widget({
  selector: '.large-list',
  template: `
    <template x:for="item in visibleItems">
      <div x:text="item.name"></div>
    </template>
  `
})
export class LargeListWidget {
  @xproperty()
  allItems: any[] = []; // Thousands of items
  
  @xproperty()
  pageSize: number = 20;
  
  @xproperty()
  currentPage: number = 1;
  
  @xcomputed({ dependencies: ['allItems', 'currentPage', 'pageSize'] })
  get visibleItems() {
    const start = (this.currentPage - 1) * this.pageSize;
    const end = start + this.pageSize;
    return this.allItems.slice(start, end);
  }
}

Summary

  • List rendering: Loop over arrays
  • Index support: item, index in array syntax
  • Nested loops: Nested loops
  • Event binding: Event handlers for each item
  • Conditional: Conditional rendering with x:if, x:class
  • Computed arrays: Filtering and sorting
  • Dynamic: List updates with push, splice
  • Performance: Optimization with computed properties