> For the complete documentation index, see [llms.txt](https://odilbeks-organization.gitbook.io/angular-ustoz-shogirt/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://odilbeks-organization.gitbook.io/angular-ustoz-shogirt/signals-va-modern-state-management.md).

# Signals va Modern State Management

### Mundarija

1. Signal nima?
2. Signal yaratish va ishlatish
3. Computed Signals
4. Effect
5. Serviceda Signal ishlatish
6. Signal vs Observable
7. Amaliy mashg’ulot

### Dars maqsadi

Angular 16+ da kiritilgan **Signals** - yangi va oddiy state management usulini o'rganish.

***

### 1. Signal nima?

**Signal** - bu o'zgaruvchan qiymat bo'lib, o'zgarganida Angular avtomatik yangilanadi.

**Oddiy qilib aytganda:** Ma'lumot o'zgarsa, sahifa avtomatik yangilanadi.

#### 1.1 Signal vs Observable

| Signal                 | Observable (RxJS)   |
| ---------------------- | ------------------- |
| ✅ Sodda                | ❌ Murakkab          |
| ✅ Sync (bir zumda)     | ⏳ Async (kutish)    |
| ✅ Subscribe kerak emas | ❌ Subscribe kerak   |
| ✅ Avtomatik cleanup    | ❌ Unsubscribe kerak |
| 🆕 Yangi (Angular 16+) | 📜 Eski (har doim)  |

{% hint style="info" %}
**Xulosa**

Signal'lar soddaroq va zamonaviy!
{% endhint %}

***

### 2. Signal yaratish va ishlatish

#### 2.1 Oddiy Signal

```tsx
import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-counter',
  template: `
    <div class="counter">
      <h2>Counter: {{ count() }}</h2>
      <button (click)="increment()">+</button>
      <button (click)="decrement()">-</button>
      <button (click)="reset()">Reset</button>
    </div>
  `
})
export class CounterComponent {
  // Signal yaratish
  count = signal(0);  // Boshlang'ich qiymat: 0

  // Signal qiymatini o'zgartirish
  increment() {
    this.count.set(this.count() + 1);
  }

  decrement() {
    this.count.set(this.count() - 1);
  }

  reset() {
    this.count.set(0);
  }
}

```

**Muhim:**

* `signal(0)` - Signal yaratish
* `count()` - Qiymatni o'qish (funksiya sifatida)
* `count.set(5)` - Qiymatni o'zgartirish

#### 2.2 update() metodi

```tsx
export class CounterComponent {
  count = signal(0);

  // set() o'rniga update() - hozirgi qiymat bilan ishlash
  increment() {
    this.count.update(value => value + 1);
  }

  decrement() {
    this.count.update(value => value - 1);
  }

  double() {
    this.count.update(value => value * 2);
  }
}

```

**update() afzalligi:** Hozirgi qiymatni ishlatib yangi qiymat yaratish oson.

***

### 3. Computed Signals (read-only signal)

**Computed** - boshqa signal'lardan kelib chiqadigan qiymat.

#### 3.1 Oddiy misol

```tsx
import { Component, signal, computed } from '@angular/core';

@Component({
  selector: 'app-calculator',
  template: `
    <div>
      <input type="number" [(ngModel)]="num1Value" (input)="num1.set(+$any($event.target).value)">
      <input type="number" [(ngModel)]="num2Value" (input)="num2.set(+$any($event.target).value)">

      <h3>Yig'indi: {{ sum() }}</h3>
      <h3>Ko'paytma: {{ product() }}</h3>
    </div>
  `
})
export class CalculatorComponent {
  num1 = signal(0);
  num2 = signal(0);

  num1Value = 0;
  num2Value = 0;

  // Computed signals - avtomatik yangilanadi
  sum = computed(() => this.num1() + this.num2());
  product = computed(() => this.num1() * this.num2());
}

```

**Qanday ishlaydi?**

1. `num1` yoki `num2` o'zgarsa
2. `sum` va `product` avtomatik qayta hisoblanadi
3. Template avtomatik yangilanadi

#### 3.2 Murakkab misol - Shopping Cart

```tsx
import { Component, signal, computed } from '@angular/core';

interface CartItem {
  id: number;
  name: string;
  price: number;
  quantity: number;
}

@Component({
  selector: 'app-cart',
  template: `
    <div class="cart">
      <h2>Savatcha</h2>

      @for (item of items(); track item.id) {
        <div class="cart-item">
          <span>{{ item.name }}</span>
          <span>{{ item.price }} so'm × {{ item.quantity }}</span>
          <span>{{ item.price * item.quantity }} so'm</span>
          <button (click)="removeItem(item.id)">O'chirish</button>
        </div>
      }

      <div class="cart-summary">
        <p>Jami mahsulotlar: {{ totalItems() }} ta</p>
        <p>Umumiy summa: {{ totalPrice() }} so'm</p>
      </div>
    </div>
  `
})
export class CartComponent {
  // Signal array
  items = signal<CartItem[]>([
    { id: 1, name: 'Kitob', price: 50000, quantity: 2 },
    { id: 2, name: 'Ruchka', price: 5000, quantity: 3 }
  ]);

  // Computed - jami mahsulotlar soni
  totalItems = computed(() => {
    return this.items().reduce((sum, item) => sum + item.quantity, 0);
  });

  // Computed - umumiy summa
  totalPrice = computed(() => {
    return this.items().reduce((sum, item) => sum + (item.price * item.quantity), 0);
  });

  removeItem(id: number) {
    this.items.update(items => items.filter(item => item.id !== id));
  }

  addItem(item: CartItem) {
    this.items.update(items => [...items, item]);
  }
}

```

**Computed afzalliklari:**

* Avtomatik yangilanadi
* Cache qilinadi (qaram bo’lgan signallar qiymatlari o’zgargandagina qayta hisoblaydi)
* Kod sodda va tushunarli

***

### 4. Effect - Signal o'zgarishiga javob

**Effect** - Signal o'zgarganida qo'shimcha amal bajarish.

#### Oddiy misol

```tsx
import { Component, signal, effect } from '@angular/core';

@Component({
  selector: 'app-theme',
  template: `
    <div>
      <button (click)="toggleTheme()">Theme: {{ theme() }}</button>
    </div>
  `
})
export class ThemeComponent {
  theme = signal<'light' | 'dark'>('light');

  constructor() {
    // Effect - theme o'zgarganda ishga tushadi
    effect(() => {
      const currentTheme = this.theme();
      console.log('Theme changed to:', currentTheme);

      // Body class o'zgartirish
      document.body.className = currentTheme;

      // LocalStorage ga saqlash
      localStorage.setItem('theme', currentTheme);
    });
  }

  toggleTheme() {
    this.theme.update(t => t === 'light' ? 'dark' : 'light');
  }
}

```

**Effect qachon ishlatiladi?**

* LocalStorage/SessionStorage
* Console.log (debugging)
* DOM manipulatsiya
* External API chaqirish

***

### 5. Serviceda Signal ishlatish

#### 5.1 Todo Service

```tsx
import { Injectable, signal, computed } from '@angular/core';

export interface Todo {
  id: number;
  text: string;
  completed: boolean;
}

@Injectable({
  providedIn: 'root'
})
export class TodoService {
  // Private signal
  private todos = signal<Todo[]>([]);

  // Public readonly (computed orqali)
  allTodos = this.todos.asReadonly();

  // Computed signals
  activeTodos = computed(() =>
    this.todos().filter(t => !t.completed)
  );

  completedTodos = computed(() =>
    this.todos().filter(t => t.completed)
  );

  totalCount = computed(() => this.todos().length);
  activeCount = computed(() => this.activeTodos().length);
  completedCount = computed(() => this.completedTodos().length);

  private nextId = 1;

  // Todo qo'shish
  addTodo(text: string) {
    const newTodo: Todo = {
      id: this.nextId++,
      text,
      completed: false
    };

    this.todos.update(todos => [...todos, newTodo]);
  }

  // Todo o'chirish
  deleteTodo(id: number) {
    this.todos.update(todos => todos.filter(t => t.id !== id));
  }

  // Toggle completed
  toggleTodo(id: number) {
    this.todos.update(todos =>
      todos.map(t => t.id === id ? { ...t, completed: !t.completed } : t)
    );
  }

  // Barcha completed'larni o'chirish
  clearCompleted() {
    this.todos.update(todos => todos.filter(t => !t.completed));
  }
}

```

#### 5.2 Todo Component

```tsx
import { Component, signal } from '@angular/core';
import { TodoService } from './services/todo.service';

@Component({
  selector: 'app-todos',
  template: `
    <div class="todo-app">
      <h2>Todo List</h2>

      <!-- Input -->
      <div class="input-section">
        <input
          type="text"
          [(ngModel)]="newTodoText"
          (keyup.enter)="addTodo()"
          placeholder="Yangi vazifa...">
        <button (click)="addTodo()">Qo'shish</button>
      </div>

      <!-- Filter -->
      <div class="filter-section">
        <button (click)="filter.set('all')" [class.active]="filter() === 'all'">
          Hammasi ({{ todoService.totalCount() }})
        </button>
        <button (click)="filter.set('active')" [class.active]="filter() === 'active'">
          Faol ({{ todoService.activeCount() }})
        </button>
        <button (click)="filter.set('completed')" [class.active]="filter() === 'completed'">
          Bajarilgan ({{ todoService.completedCount() }})
        </button>
      </div>

      <!-- Todo list -->
      <div class="todos-list">
        @for (todo of filteredTodos(); track todo.id) {
          <div class="todo-item" [class.completed]="todo.completed">
            <input
              type="checkbox"
              [checked]="todo.completed"
              (change)="todoService.toggleTodo(todo.id)">
            <span>{{ todo.text }}</span>
            <button (click)="todoService.deleteTodo(todo.id)">O'chirish</button>
          </div>
        } @empty {
          <p class="empty-state">Vazifalar yo'q</p>
        }
      </div>

      <!-- Actions -->
      @if (todoService.completedCount() > 0) {
        <button (click)="todoService.clearCompleted()" class="btn-clear">
          Bajarilganlarni tozalash
        </button>
      }
    </div>
  `
})
export class TodosComponent {
  newTodoText = '';
  filter = signal<'all' | 'active' | 'completed'>('all');

  constructor(public todoService: TodoService) {}

  // Computed - filtr bo'yicha todos
  filteredTodos = computed(() => {
    const currentFilter = this.filter();

    switch(currentFilter) {
      case 'active':
        return this.todoService.activeTodos();
      case 'completed':
        return this.todoService.completedTodos();
      default:
        return this.todoService.allTodos();
    }
  });

  addTodo() {
    if (this.newTodoText.trim()) {
      this.todoService.addTodo(this.newTodoText);
      this.newTodoText = '';
    }
  }
}

```

***

### 6. Signal vs Observable - Qachon nima ishlatish?

#### 6.1 Signal ishlatish kerak:

✅ **Local state** (component state)

```tsx
count = signal(0);
isOpen = signal(false);
selectedItem = signal<Item | null>(null);

```

✅ **Simple data flow** (oddiy ma'lumot oqimi)

```tsx
firstName = signal('');
lastName = signal('');
fullName = computed(() => `${this.firstName()} ${this.lastName()}`);

```

✅ **Sync operations** (bir zumda)

```tsx
increment() {
  this.count.update(c => c + 1);
}

```

#### 6.2 Observable ishlatish kerak:

✅ **HTTP requests**

```tsx
this.http.get('/api/data').subscribe();

```

✅ **Events** (click, input, timer)

```tsx
fromEvent(button, 'click').subscribe();
interval(1000).subscribe();

```

✅ **Complex async operations**

```tsx
searchInput.valueChanges.pipe(
  debounceTime(300),
  switchMap(query => this.search(query))
).subscribe();

```

#### 6.3 Ikkalasini birga ishlatish

```tsx
import { Component, signal } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-data'
})
export class DataComponent {
  // Signal - state
  data = signal<any[]>([]);
  loading = signal(false);
  error = signal('');

  constructor(private http: HttpClient) {}

  loadData() {
    this.loading.set(true);
    this.error.set('');

    // Observable - HTTP
    this.http.get<any[]>('/api/data').subscribe({
      next: (result) => {
        this.data.set(result);  // Signal yangilash
        this.loading.set(false);
      },
      error: (err) => {
        this.error.set('Xatolik yuz berdi');
        this.loading.set(false);
      }
    });
  }
}

```

***

### 7. Amaliy misol: Talabalar ro'yxati

**student.service.ts:**

```tsx
import { Injectable, signal, computed } from '@angular/core';

export interface Student {
  id: number;
  name: string;
  grade: number;
  subject: string;
  score: number;
}

@Injectable({
  providedIn: 'root'
})
export class StudentService {
  private students = signal<Student[]>([
    { id: 1, name: 'Ali Valiyev', grade: 10, subject: 'Matematika', score: 95 },
    { id: 2, name: 'Gulnora Karimova', grade: 10, subject: 'Fizika', score: 87 },
    { id: 3, name: 'Sardor Rahimov', grade: 11, subject: 'Matematika', score: 78 }
  ]);

  // Public readonly
  allStudents = this.students.asReadonly();

  // Search query
  searchQuery = signal('');
  selectedSubject = signal('all');
  selectedGrade = signal('all');

  // Computed - filtered students
  filteredStudents = computed(() => {
    let result = this.students();

    // Search bo'yicha
    const query = this.searchQuery().toLowerCase();
    if (query) {
      result = result.filter(s => s.name.toLowerCase().includes(query));
    }

    // Subject bo'yicha
    const subject = this.selectedSubject();
    if (subject !== 'all') {
      result = result.filter(s => s.subject === subject);
    }

    // Grade bo'yicha
    const grade = this.selectedGrade();
    if (grade !== 'all') {
      result = result.filter(s => s.grade === Number(grade));
    }

    return result;
  });

  // Statistics
  averageScore = computed(() => {
    const students = this.filteredStudents();
    if (students.length === 0) return 0;

    const total = students.reduce((sum, s) => sum + s.score, 0);
    return Math.round(total / students.length);
  });

  topStudent = computed(() => {
    const students = this.filteredStudents();
    if (students.length === 0) return null;

    return students.reduce((top, current) =>
      current.score > top.score ? current : top
    );
  });

  private nextId = 4;

  addStudent(student: Omit<Student, 'id'>) {
    const newStudent = { ...student, id: this.nextId++ };
    this.students.update(students => [...students, newStudent]);
  }

  deleteStudent(id: number) {
    this.students.update(students => students.filter(s => s.id !== id));
  }

  updateStudent(id: number, updates: Partial<Student>) {
    this.students.update(students =>
      students.map(s => s.id === id ? { ...s, ...updates } : s)
    );
  }
}

```

**students.component.ts:**

```tsx
import { Component } from '@angular/core';
import { StudentService } from './services/student.service';

@Component({
  selector: 'app-students',
  template: `
    <div class="students-container">
      <h2>Talabalar ro'yxati</h2>

      <!-- Filters -->
      <div class="filters">
        <input
          type="text"
          [ngModel]="studentService.searchQuery()"
          (ngModelChange)="studentService.searchQuery.set($event)"
          placeholder="Qidirish...">

        <select
          [ngModel]="studentService.selectedSubject()"
          (ngModelChange)="studentService.selectedSubject.set($event)">
          <option value="all">Barcha fanlar</option>
          <option value="Matematika">Matematika</option>
          <option value="Fizika">Fizika</option>
        </select>

        <select
          [ngModel]="studentService.selectedGrade()"
          (ngModelChange)="studentService.selectedGrade.set($event)">
          <option value="all">Barcha sinflar</option>
          <option value="10">10-sinf</option>
          <option value="11">11-sinf</option>
        </select>
      </div>

      <!-- Statistics -->
      <div class="statistics">
        <div class="stat-card">
          <h3>Jami: {{ studentService.filteredStudents().length }}</h3>
        </div>
        <div class="stat-card">
          <h3>O'rtacha ball: {{ studentService.averageScore() }}</h3>
        </div>
        @if (studentService.topStudent(); as top) {
          <div class="stat-card">
            <h3>Eng yaxshi: {{ top.name }}</h3>
            <p>{{ top.score }} ball</p>
          </div>
        }
      </div>

      <!-- Students list -->
      <div class="students-list">
        @for (student of studentService.filteredStudents(); track student.id) {
          <div class="student-card">
            <h3>{{ student.name }}</h3>
            <p>{{ student.grade }}-sinf | {{ student.subject }}</p>
            <p class="score">Ball: {{ student.score }}</p>
            <button (click)="deleteStudent(student.id)">O'chirish</button>
          </div>
        } @empty {
          <p class="empty-state">Talabalar topilmadi</p>
        }
      </div>
    </div>
  `
})
export class StudentsComponent {
  constructor(public studentService: StudentService) {}

  deleteStudent(id: number) {
    if (confirm('Talabani o\\'chirmoqchimisiz?')) {
      this.studentService.deleteStudent(id);
    }
  }
}

```

***

### 8. Signal Best Practices

#### ✅ Yaxshi:

**1. Readonly signals**

```tsx
@Injectable()
export class DataService {
  private data = signal<Data[]>([]);
  readonly publicData = this.data.asReadonly();
}

```

**2. Computed for derived state**

```tsx
count = signal(0);
doubleCount = computed(() => this.count() * 2);

```

**3. Update for transformations**

```tsx
// ✅ Yaxshi
count.update(c => c + 1);

// ❌ Yomon
count.set(count() + 1);

```

**4. Effect for side effects only**

```tsx
effect(() => {
  // ✅ Side effects
  localStorage.setItem('data', JSON.stringify(this.data()));
  console.log('Data changed:', this.data());
});

```

#### ❌ Yomon:

**1. Effect ichida signal o'zgartirish**

```tsx
// ❌ Yomon - infinite loop!
effect(() => {
  this.count.set(this.count() + 1);
});

```

**2. Computed ichida async**

```tsx
// ❌ Yomon - computed faqat sync
data = computed(() => {
  this.http.get('/api/data').subscribe(); // ❌
});

```

***

### 9. Xulosa

#### ✅ Signal asoslari:

**Yaratish va o'qish:**

```tsx
count = signal(0);        // Yaratish
value = count();          // O'qish

```

**O'zgartirish:**

```tsx
count.set(5);             // To'g'ridan-to'g'ri
count.update(c => c + 1); // Hozirgi qiymat bilan

```

**Computed:**

```tsx
double = computed(() => count() * 2);

```

**Effect:**

```tsx
effect(() => {
  console.log('Count:', count());
});

```

#### ✅ Afzalliklari:

1. **Sodda** - Observable'dan oson
2. **Avtomatik** - Subscribe/unsubscribe yo'q
3. **Tez** - Performance yaxshi
4. **Zamonaviy** - Angular'ning kelajagi

#### ✅ Signal vs Observable:

* **Signal** → Local state, sync
* **Observable** → HTTP, events, async

***

### 10. Nazorat Savollari

1. **Signal nima va nima uchun kerak?**
2. **Signal qanday yaratiladi va o'qiladi?**
3. **set() va update() farqi nima?**
4. **Computed signal nima?**
5. **Effect qachon ishlatiladi?**
6. **Signal va Observable farqi?**
7. **asReadonly() nima uchun ishlatiladi?**
8. **Signal ichida async operatsiya qilish mumkinmi?**
9. **Effect ichida signal o'zgartirish mumkinmi?**
10. **Signal qachon ishlatish kerak?**

***

### 11. Test Savollari

**1. Signal yaratish:**

* A) `signal(0)`
* B) `new Signal(0)`
* C) `Signal.create(0)`
* D) `createSignal(0)`

**Javob: A**

***

**2. Signal qiymatini o'qish:**

* A) `count.value`
* B) `count()`
* C) `count.get()`
* D) `count.read()`

**Javob: B**

***

**3. Signal o'zgartirish:**

* A) `count.set(5)`
* B) `count = 5`
* C) `count.value = 5`
* D) `count.change(5)`

**Javob: A**

***

**4. update() metodi:**

* A) `count.update(c => c + 1)`
* B) `count.update(+1)`
* C) `count.increment()`
* D) `count.add(1)`

**Javob: A**

***

**5. Computed signal:**

* A) `computed(() => count() * 2)`
* B) `signal.computed(() => count() * 2)`
* C) `count.computed(() => * 2)`
* D) `new Computed(() => count() * 2)`

**Javob: A**

***

**6. Effect:**

* A) `effect(() => {})`
* B) `onEffect(() => {})`
* C) `signal.effect(() => {})`
* D) `watchEffect(() => {})`

**Javob: A**

***

**7. Readonly signal:**

* A) `signal.asReadonly()`
* B) `signal.readonly()`
* C) `readonly(signal)`

**Javob: A**

***

**8. Signal subscribe kerakmi?**

* A) Ha
* B) Yo'q
* C) Ba'zan
* D) Faqat effect'da

**Javob: B**

***

**9. Computed cache qiladimi?**

* A) Ha
* B) Yo'q
* C) Ba'zan
* D) Faqat effect'da

**Javob: A**

***

**10. Signal unsubscribe kerakmi?**

* A) Ha
* B) Yo'q
* C) Faqat effect'da
* D) Faqat computed'da

**Javob: B**

***

**11. Computed async bo'lishi mumkinmi?**

* A) Ha
* B) Yo'q
* C) Promise bilan
* D) Observable bilan

**Javob: B**

***

**12. Signal vs Observable:**

* A) Signal sodda
* B) Signal sync
* C) Signal subscribe yo'q
* D) Hammasi to'g'ri

**Javob: D**

> [QuizBot](https://t.me/QuizBot?start=nhFeWcAF)

***

### 12. Amaliy Topshiriqlar

#### Topshiriq 1: Counter App

Signal ishlatib:

* Increment/Decrement/Reset
* Computed: double, triple
* Effect: localStorage ga saqlash

***

#### Topshiriq 2: Todo List

* Signal array todos
* Computed: active/completed count
* Filter: all/active/completed
* LocalStorage integration

***

#### Topshiriq 3: Shopping Cart

* Signal products array
* Computed: total price, item count
* Add/Remove/Update quantity
* Discount calculation

***

#### Topshiriq 4: Theme Switcher

* Signal theme: 'light' | 'dark'
* Effect: body class change
* LocalStorage persistence
* Smooth transitions

***

#### Topshiriq 5: Search & Filter

* Signal search query
* Signal filters (category, price)
* Computed: filtered results
* Effect: URL params update

***

{% hint style="info" %}
Kamida 3 ta taskni qilish kerak bo’ladi!
{% endhint %}

***

> **Darsdan namuna (takrorlash uchun):**

{% embed url="<https://youtu.be/qOim1Hc0Shc>" %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://odilbeks-organization.gitbook.io/angular-ustoz-shogirt/signals-va-modern-state-management.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
