فهرست سرفصل‌های Angular
خانه (Home) معرفی (Intro) شروع سریع (Get Started) اولین اپ (First App) قالب ها (Templates) جای گذاری (Interpolation) متغیرهای ارجاعی (Reference Variables) ناوبری امن از نال (?.) (Null-Safe Navigation (?.)) دستورهای ساختاری (Structural Directives) ngTemplateOutlet (ngTemplateOutlet) عبارات و $event (Statements and $event) نام مستعار (Alias) پایپ ها (|) (Pipes (|)) بایندینگ ویژگی (Attribute Binding) TrackBy (TrackBy) کامپوننت ها (Components) دیتا بایندینگ (Data Binding) دایرکتیوها (Directives) رویدادها (Events) رندر شرطی (Conditional) لیست ها (Lists) فرم ها (Forms) روتر (Router) سرویس ها و تزریق وابستگی (Services & DI) کلاینت HTTP (HTTP Client) پایپ ها (Pipes) قلاب های چرخه حیات (Lifecycle Hooks) استایل دهی (Styling) راه اندازی برنامه (App Bootstrap) کنترل جریان (Control Flow) سیگنال ها (Signals) تشخیص تغییر (Change Detection) کامپوننت های پویا (Dynamic Comp) تزریق وابستگی پیشرفته (Advanced DI) روتر پیشرفته (Router Advanced) اینترسپتورهای HTTP (HTTP Interceptors) فرم های پیشرفته (Forms Advanced) مدیریت وضعیت (State Management) انیمیشن ها (Animations) تست (Testing) امنیت (Security) SSR و هیدریشن (SSR & Hydration) کامپایلر (Compiler) تمرین ها (Exercises) آزمون (Quiz) سرور (Server)
نتیجه‌ای برای جستجو یافت نشد.
Angular

Angular — لیست ها (Lists)

آخرین بروزرسانی: 1404/08/21

لیست ها (Lists)

می خواهیم «لیست ها در انگولار» را مثل بازی فهرست اسکن ها بفهمیم. «لیست (List)» یعنی چند آیتم پشت سرهم. با حلقه رندر می گیریم. با «سیگنال (Signal)» وضعیت را نگه می داریم. با «شناسه پایدار (Stable Identity)» جابه جایی ها را بدون چشمک DOM مدیریت می کنیم. سپس با «محاسبه شده (computed)» فیلتر و مرتب سازی می سازیم.

تعریف سریع

حلقه @for آیتم ها را تکرار می کند. شاخص i را با let i = $index می گیریم. سیگنال، آرایه را نگه می دارد. با set() و update() همیشه یک آرایه تازه می سازیم. «شناسه پایدار» مثل id کمک می کند DOM دوباره کاری نکند. برای نماهای مشتق از computed() کمک بگیرید.

نکته: برای کنترل جریان، Control Flow و برای شرطی ها Conditional Rendering را هم ببینید.

لیست ها در انگولار: شروع سریع

الگوی پایه ساده است. یک سیگنال از آرایه می سازیم. سپس با @for رندر می کنیم. اگر لیست خالی شد، با @empty پیام جایگزین نشان می دهیم.

لیست پایه با @for و @empty

import { bootstrapApplication } from '@angular/platform-browser';
import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <h3>Lists</h3>
    <ul>
      @for (item of items(); let i = $index; track item) {
        <li>{{ i + 1 }}. {{ item }}</li>
      } @empty {
        <li>No items</li>
      }
    </ul>
    <button (click)="add()">Add Item</button>
    <button (click)="clear()">Clear</button>
    <button (click)="reset()">Reset</button>
  `
})
export class App {
  items = signal(['Angular', 'React', 'Vue']);

  add() {
    this.items.update(arr => [...arr, 'Svelte']);
  }

  clear() {
    this.items.set([]);
  }

  reset() {
    this.items.set(['Angular', 'React', 'Vue']);
  }
}

bootstrapApplication(App);

مشاهده در ادیتور

نکته: هرگز آرایه را درجا دست کاری نکن. همیشه یک کپی تازه بساز.

لیست با track و شناسه پایدار

وقتی آیتم ها جابه جا می شوند، track کمک می کند DOM الکی نسازد. از it.id یا کلید یکتا استفاده کن.

import { bootstrapApplication } from '@angular/platform-browser';
import { Component, signal } from '@angular/core';

type Item = { id: number; name: string };

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <h3>Lists with track</h3>
    <ul>
      @for (it of items(); let i = $index; track it.id) {
        <li>{{ i + 1 }}. {{ it.name }} (id: {{ it.id }})</li>
      }
    </ul>
    <button (click)="renameFirst()">Rename first</button>
    <button (click)="shuffle()">Shuffle</button>
    <button (click)="add()">Add item</button>
  `
})
export class App {
  items = signal<Item[]>([
    { id: 1, name: 'Angular' },
    { id: 2, name: 'React' },
    { id: 3, name: 'Vue' }
  ]);

  nextId = 4;

  renameFirst() {
    this.items.update(arr => arr.map((it, i) => {
      if (i === 0) {
        return { ...it, name: it.name + ' *' };
      }
      return it;
    }));
  }

  shuffle() {
    this.items.update(arr => {
      const copy = [...arr];
      for (let i = copy.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [copy[i], copy[j]] = [copy[j], copy[i]];
      }
      return copy;
    });
  }

  add() {
    this.items.update(arr => {
      return [...arr, { id: this.nextId++, name: 'New ' + Date.now() }];
    });
  }
}

bootstrapApplication(App);

مشاهده در ادیتور

هشدار: از شاخص آرایه به عنوان شناسه استفاده نکن. با جابه جایی، ناسازگاری می گیری.

فیلتر و مرتب سازی با computed()

یک نما از داده ها بساز. منبع را دست نزن. سپس بر اساس جستجو و کلید مرتب سازی خروجی بده.

import { bootstrapApplication } from '@angular/platform-browser';
import { Component, signal, computed } from '@angular/core';
import { CommonModule } from '@angular/common';

type Product = { name: string; price: number };

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule],
  template: `
    <h3>Filter & Sort</h3>
    <div style="display:flex;gap:8px;margin-bottom:8px;">
      <label>Search: <input #q (input)="query.set(q.value)" placeholder="Type to filter..." /></label>
      <button (click)="setSort('name')">Sort by Name</button>
      <button (click)="setSort('price')">Sort by Price</button>
      <button (click)="toggleDir()">{{ sortDir() === 1 ? 'Asc' : 'Desc' }}</button>
    </div>

    <table style="width:100%;border-collapse:collapse;">
      <thead>
        <tr><th style="border:1px solid #ddd;padding:8px;background:#f7f7f7;">Name</th><th style="border:1px solid #ddd;padding:8px;background:#f7f7f7;width:140px;">Price</th></tr>
      <\/thead>
      <tbody>
        @for (p of view(); track p.name) {
          <tr>
            <td style="border:1px solid #ddd;padding:8px;">{{ p.name }}</td>
            <td style="border:1px solid #ddd;padding:8px;">{{ p.price | currency:'USD' }}</td>
          </tr>
        }
      <\/tbody>
    <\/table>
  `
})
export class App {
  items = signal<Product[]>([
    { name: 'Angular', price: 0 },
    { name: 'React', price: 0 },
    { name: 'Vue', price: 0 },
    { name: 'Svelte', price: 0 },
    { name: 'Solid', price: 0 },
    { name: 'Lit', price: 0 }
  ]);

  query = signal('');
  sortKey = signal<'name' | 'price'>('name');
  sortDir = signal<1 | -1>(1);

  view = computed(() => {
    const q = this.query().toLowerCase();
    const dir = this.sortDir();
    const key = this.sortKey();
    return this.items()
      .filter(it => it.name.toLowerCase().includes(q))
      .sort((a, b) => {
        const av: any = (a as any)[key];
        const bv: any = (b as any)[key];
        if (av < bv) {
          return -1 * dir;
        }
        if (av > bv) {
          return 1 * dir;
        }
        return 0;
      });
  });

  setSort(key: 'name' | 'price') {
    if (this.sortKey() === key) {
      this.toggleDir();
      return;
    }
    this.sortKey.set(key);
  }

  toggleDir() {
    this.sortDir.set(this.sortDir() === 1 ? -1 : 1);
  }
}

bootstrapApplication(App);

مشاهده در ادیتور

نکته: کار سنگین را در قالب انجام نده. با computed از قبل آماده کن.

جمع بندی سریع

  • @for برای تکرار و @empty برای حالت خالی.
  • سیگنال ها را فقط با set یا update تغییر بده.
  • برای جابه جایی ها، از track با کلید یکتا استفاده کن.
  • نمای فیلتر و سورت را با computed بساز.
  • هرگز شاخص آرایه را کلید نکن.