> 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/linkedsignal-pipes-custom-directive-interceptor.md).

# linkedSignal, Pipes, Custom Directive, Interceptor

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

***

### 1. linkedSignal() - Signal Sync

#### 1.1 linkedSignal nima?

**linkedSignal** - bu boshqa signal o'zgarganida avtomatik yangilanadigan signal (Angular 19+).

**computed vs linkedSignal:**

```tsx
// computed() - faqat o'qish
fullName = computed(() => `${firstName()} ${lastName()}`);

// linkedSignal() - o'qish va yozish
fullName = linkedSignal(() => `${firstName()} ${lastName()}`);
fullName.set('Yangi ism');  // ✅ Mumkin
```

#### 1.2 Oddiy misol

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

@Component({
  selector: 'app-user-profile',
  template: `
    <div class="profile">
      <input [(ngModel)]="firstNameValue" (input)="firstName.set(firstNameValue)">
      <input [(ngModel)]="lastNameValue" (input)="lastName.set(lastNameValue)">

      <h3>Full Name: {{ fullName() }}</h3>

      <button (click)="setFullName()">Set "Ali Valiyev"</button>
    </div>
  `
})
export class UserProfileComponent {
  firstName = signal('');
  lastName = signal('');

  firstNameValue = '';
  lastNameValue = '';

  // linkedSignal - avtomatik yangilanadi va o'zgartirish mumkin
  fullName = linkedSignal(() => {
    return `${this.firstName()} ${this.lastName()}`.trim();
  });

  setFullName() {
    // linkedSignal ni o'zgartirish mumkin
    this.fullName.set('Ali Valiyev');
  }
}
```

#### 1.3 Real misol: Currency Converter

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

@Component({
  selector: 'app-currency-converter',
  template: `
    <div class="converter">
      <h2>Valyuta konvertor</h2>

      <div class="input-group">
        <label>USD:</label>
        <input
          type="number"
          [value]="usd()"
          (input)="usd.set(+$any($event.target).value)">
      </div>

      <div class="input-group">
        <label>UZS:</label>
        <input
          type="number"
          [value]="uzs()"
          (input)="uzs.set(+$any($event.target).value)">
      </div>

      <p>Kurs: 1 USD = {{ rate() }} UZS</p>
    </div>
  `
})
export class CurrencyConverterComponent {
  rate = signal(12500);  // 1 USD = 12500 UZS
  usd = signal(0);

  // linkedSignal - USD o'zgarsa UZS avtomatik yangilanadi
  // va UZS o'zgartirilsa ham ishlaydi
  uzs = linkedSignal(() => this.usd() * this.rate());

  // Agar uzs.set() chaqirilsa, usd ni yangilash
  constructor() {
    effect(() => {
      const currentUzs = this.uzs();
      const currentUsd = this.usd();

      // Agar UZS qo'lda o'zgartirilgan bo'lsa
      if (currentUzs !== currentUsd * this.rate()) {
        this.usd.set(currentUzs / this.rate());
      }
    });
  }
}
```

#### 1.4 linkedSignal vs computed

| Feature     | computed() | linkedSignal() |
| ----------- | ---------- | -------------- |
| Read        | ✅          | ✅              |
| Write       | ❌          | ✅              |
| Auto update | ✅          | ✅              |
| set()       | ❌          | ✅              |
| update()    | ❌          | ✅              |

***

### 2. Pipes (Quvurlar)

#### 2.1 Pipe nima?

**Pipe** - ma'lumotni template'da formatlash uchun.

**Sintaksis:** `{{ value | pipeName }}`

#### 2.2 Built-in Pipes

#### DatePipe

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

@Component({
  selector: 'app-date-demo',
  template: `
    <div>
      <p>To'liq: {{ today | date:'full' }}</p>
      <p>Qisqa: {{ today | date:'short' }}</p>
      <p>Custom: {{ today | date:'dd/MM/yyyy' }}</p>
      <p>Vaqt: {{ today | date:'HH:mm:ss' }}</p>
    </div>
  `
})
export class DateDemoComponent {
  today = new Date();
}
```

**Natija:**

```
To'liq: Tuesday, February 18, 2026 at 2:30:45 PM GMT+5
Qisqa: 2/18/26, 2:30 PM
Custom: 18/02/2026
Vaqt: 14:30:45
```

#### UpperCase / LowerCase / TitleCase

```html
<p>{{ 'salom dunyo' | uppercase }}</p>    <!-- SALOM DUNYO -->
<p>{{ 'SALOM DUNYO' | lowercase }}</p>    <!-- salom dunyo -->
<p>{{ 'salom dunyo' | titlecase }}</p>    <!-- Salom Dunyo -->
```

#### DecimalPipe (NumberPipe)

```html
<p>{{ 1234.5678 | number }}</p>           <!-- 1,234.568 -->
<p>{{ 1234.5678 | number:'1.0-0' }}</p>   <!-- 1,235 -->
<p>{{ 1234.5678 | number:'1.2-2' }}</p>   <!-- 1,234.57 -->
```

**Format:** `'minIntegerDigits.minFractionDigits-maxFractionDigits'`

#### CurrencyPipe

```html
<p>{{ 50000 | currency:'USD' }}</p>        <!-- $50,000.00 -->
<p>{{ 50000 | currency:'UZS':'symbol':'1.0-0' }}</p>  <!-- UZS 50,000 -->
```

#### PercentPipe

```html
<p>{{ 0.85 | percent }}</p>                <!-- 85% -->
<p>{{ 0.8567 | percent:'1.2-2' }}</p>      <!-- 85.67% -->
```

#### JsonPipe (Debug uchun)

```html
<pre>{{ user | json }}</pre>
<!--
{
  "name": "Ali",
  "age": 25
}
-->
```

#### SlicePipe

```tsx
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
```

```html
<p>{{ numbers | slice:0:5 }}</p>          <!-- [1,2,3,4,5] -->
<p>{{ numbers | slice:5 }}</p>            <!-- [6,7,8,9,10] -->
```

***

#### 2.3 Custom Pipe yaratish

#### 2.3.1 Oddiy pipe

```bash
ng generate pipe pipes/reverse
```

**reverse.pipe.ts:**

```tsx
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'reverse',
  standalone: true
})
export class ReversePipe implements PipeTransform {
  transform(value: string): string {
    return value.split('').reverse().join('');
  }
}
```

**Ishlatish:**

```html
<p>{{ 'Angular' | reverse }}</p>  <!-- ralugnA -->
```

#### 2.3.2 Parametrli pipe

**truncate.pipe.ts:**

```tsx
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'truncate',
  standalone: true
})
export class TruncatePipe implements PipeTransform {
  transform(value: string, limit: number = 10, trail: string = '...'): string {
    if (value.length <= limit) {
      return value;
    }
    return value.substring(0, limit) + trail;
  }
}
```

**Ishlatish:**

```html
<p>{{ 'Bu juda uzun matn' | truncate:5 }}</p>        <!-- Bu ju... -->
<p>{{ 'Bu juda uzun matn' | truncate:8:'→' }}</p>    <!-- Bu juda →-->
```

#### 2.3.3 Foydali custom pipes

**uzs-currency.pipe.ts:**

```tsx
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'uzsCurrency',
  standalone: true
})
export class UzsCurrencyPipe implements PipeTransform {
  transform(value: number): string {
    return value.toLocaleString('uz-UZ') + ' so\\'m';
  }
}
```

```html
<p>{{ 50000 | uzsCurrency }}</p>  <!-- 50,000 so'm -->
```

**time-ago.pipe.ts:**

```tsx
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'timeAgo',
  standalone: true
})
export class TimeAgoPipe implements PipeTransform {
  transform(value: Date): string {
    const now = new Date();
    const diff = now.getTime() - value.getTime();

    const seconds = Math.floor(diff / 1000);
    const minutes = Math.floor(seconds / 60);
    const hours = Math.floor(minutes / 60);
    const days = Math.floor(hours / 24);

    if (days > 0) return `${days} kun oldin`;
    if (hours > 0) return `${hours} soat oldin`;
    if (minutes > 0) return `${minutes} daqiqa oldin`;
    return 'Hozirgina';
  }
}
```

```html
<p>{{ postDate | timeAgo }}</p>  <!-- 2 soat oldin -->
```

**phone-format.pipe.ts:**

```tsx
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'phoneFormat',
  standalone: true
})
export class PhoneFormatPipe implements PipeTransform {
  transform(value: string): string {
    // +998901234567 → +998 (90) 123-45-67
    if (value.length !== 13) return value;

    return `${value.slice(0, 4)} (${value.slice(4, 6)}) ${value.slice(6, 9)}-${value.slice(9, 11)}-${value.slice(11)}`;
  }
}
```

```html
<p>{{ '+998901234567' | phoneFormat }}</p>
<!-- +998 (90) 123-45-67 -->
```

***

### 3. Custom Directive

#### 3.1 Directive nima?

**Directive** - HTML elementning xatti-harakatini o'zgartirish.

**3 xil directive’lar mavjud:**

1. **Component**
2. **Structural** - \*ngIf, \*ngFor (DOM’ni o'zgartiradi)
3. **Attribute** - ngClass, ngStyle (element ko’rinishini o'zgartiradi)

#### 3.2 Attribute Directive yaratish

#### 3.2.1 Highlight Directive

```bash
ng generate directive directives/highlight
```

**highlight.directive.ts:**

```tsx
import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({
  selector: '[appHighlight]',
  standalone: true
})
export class HighlightDirective {
  @Input() highlightColor: string = 'yellow';

  constructor(private el: ElementRef) {}

  @HostListener('mouseenter') onMouseEnter() {
    this.highlight(this.highlightColor);
  }

  @HostListener('mouseleave') onMouseLeave() {
    this.highlight('');
  }

  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color;
  }
}
```

**Ishlatish:**

```html
<p appHighlight>Hover qiling - sariq bo'ladi</p>
<p appHighlight highlightColor="lightblue">Hover qiling - ko'k bo'ladi</p>
```

#### 3.2.2 Tooltip Directive

**tooltip.directive.ts:**

```tsx
import { Directive, ElementRef, Input, HostListener, Renderer2 } from '@angular/core';

@Directive({
  selector: '[appTooltip]',
  standalone: true
})
export class TooltipDirective {
  @Input() appTooltip: string = '';
  private tooltipElement?: HTMLElement;

  constructor(
    private el: ElementRef,
    private renderer: Renderer2
  ) {}

  @HostListener('mouseenter') onMouseEnter() {
    if (!this.tooltipElement) {
      this.showTooltip();
    }
  }

  @HostListener('mouseleave') onMouseLeave() {
    if (this.tooltipElement) {
      this.hideTooltip();
    }
  }

  private showTooltip() {
    // Tooltip yaratish
    this.tooltipElement = this.renderer.createElement('span');
    this.renderer.appendChild(
      this.tooltipElement,
      this.renderer.createText(this.appTooltip)
    );

    // Style qo'shish
    this.renderer.setStyle(this.tooltipElement, 'position', 'absolute');
    this.renderer.setStyle(this.tooltipElement, 'background', '#333');
    this.renderer.setStyle(this.tooltipElement, 'color', 'white');
    this.renderer.setStyle(this.tooltipElement, 'padding', '5px 10px');
    this.renderer.setStyle(this.tooltipElement, 'borderRadius', '4px');
    this.renderer.setStyle(this.tooltipElement, 'fontSize', '14px');
    this.renderer.setStyle(this.tooltipElement, 'zIndex', '1000');

    // DOM ga qo'shish
    this.renderer.appendChild(document.body, this.tooltipElement);

    // Pozitsiya
    const hostPos = this.el.nativeElement.getBoundingClientRect();
    const tooltipPos = this.tooltipElement.getBoundingClientRect();

    const top = hostPos.top - tooltipPos.height - 10;
    const left = hostPos.left + (hostPos.width - tooltipPos.width) / 2;

    this.renderer.setStyle(this.tooltipElement, 'top', `${top}px`);
    this.renderer.setStyle(this.tooltipElement, 'left', `${left}px`);
  }

  private hideTooltip() {
    if (this.tooltipElement) {
      this.renderer.removeChild(document.body, this.tooltipElement);
      this.tooltipElement = undefined;
    }
  }
}
```

**Ishlatish:**

```html
<button appTooltip="Bu tugma">Hover qiling</button>
```

#### 3.2.3 CopyToClipboard Directive

**copy-clipboard.directive.ts:**

```tsx
import { Directive, HostListener, Input } from '@angular/core';

@Directive({
  selector: '[appCopyClipboard]',
  standalone: true
})
export class CopyClipboardDirective {
  @Input() appCopyClipboard: string = '';

  @HostListener('click') onClick() {
    this.copyToClipboard(this.appCopyClipboard);
  }

  private copyToClipboard(text: string) {
    navigator.clipboard.writeText(text).then(() => {
      alert('Nusxalandi!');
    }).catch(err => {
      console.error('Nusxalashda xatolik:', err);
    });
  }
}
```

**Ishlatish:**

```html
<button appCopyClipboard="+998901234567">
  Telefon raqamni nusxalash
</button>
```

#### 3.2.4 AutoFocus Directive

**auto-focus.directive.ts:**

```tsx
import { Directive, ElementRef, OnInit } from '@angular/core';

@Directive({
  selector: '[appAutoFocus]',
  standalone: true
})
export class AutoFocusDirective implements OnInit {
  constructor(private el: ElementRef) {}

  ngOnInit() {
    setTimeout(() => {
      this.el.nativeElement.focus();
    }, 0);
  }
}
```

**Ishlatish:**

```html
<input type="text" appAutoFocus placeholder="Avtomatik focus">
```

***

### 4. HTTP Interceptor

#### 4.1 Interceptor nima?

**Interceptor** - HTTP so'rovlarni chiqib ketishida yoki kirib kelishida ushlab olib unga o’zgartirish kiritish yoki kuzatib turish(log) uchun ishlatiladi.

**Ishlatilishi:**

* Token qo'shish (Authentication)
* Loading state
* Error handling
* Logging

#### 4.2 Auth Interceptor (Token qo'shish)

```bash
ng generate interceptor interceptors/auth
```

**auth.interceptor.ts:**

```tsx
import { HttpInterceptorFn } from '@angular/common/http';

export const authInterceptor: HttpInterceptorFn = (req, next) => {
  // Token olish (localStorage dan)
  const token = localStorage.getItem('auth_token');

  // Agar token bo'lsa, header ga qo'shish
  if (token) {
    const clonedRequest = req.clone({
      setHeaders: {
        Authorization: `Bearer ${token}`
      }
    });
    return next(clonedRequest);
  }

  // Token yo'q bo'lsa, oddiy so'rov
  return next(req);
};
```

**app.config.ts (standalone):**

```tsx
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { authInterceptor } from './interceptors/auth.interceptor';

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(
      withInterceptors([authInterceptor])
    )
  ]
};
```

#### 4.3 Loading Interceptor

**loading.interceptor.ts:**

```tsx
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { finalize } from 'rxjs/operators';
import { LoadingService } from '../services/loading.service';

export const loadingInterceptor: HttpInterceptorFn = (req, next) => {
  const loadingService = inject(LoadingService);

  // Loading start
  loadingService.show();

  return next(req).pipe(
    finalize(() => {
      // Loading stop (success yoki error)
      loadingService.hide();
    })
  );
};
```

**loading.service.ts:**

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

@Injectable({
  providedIn: 'root'
})
export class LoadingService {
  isLoading = signal(false);

  show() {
    this.isLoading.set(true);
  }

  hide() {
    this.isLoading.set(false);
  }
}
```

**app.component.ts:**

```html
@if (loadingService.isLoading()) {
  <div class="loading-overlay">
    <div class="spinner"></div>
  </div>
}
```

#### 4.4 Error Interceptor

**error.interceptor.ts:**

```tsx
import { HttpInterceptorFn, HttpErrorResponse } from '@angular/common/http';
import { catchError, throwError } from 'rxjs';
import { inject } from '@angular/core';
import { Router } from '@angular/router';

export const errorInterceptor: HttpInterceptorFn = (req, next) => {
  const router = inject(Router);

  return next(req).pipe(
    catchError((error: HttpErrorResponse) => {
      let errorMessage = '';

      if (error.error instanceof ErrorEvent) {
        // Client-side error
        errorMessage = `Error: ${error.error.message}`;
      } else {
        // Server-side error
        switch (error.status) {
          case 401:
            errorMessage = 'Unauthorized. Iltimos, tizimga kiring.';
            router.navigate(['/login']);
            break;
          case 403:
            errorMessage = 'Forbidden. Ruxsat yo\\'q.';
            break;
          case 404:
            errorMessage = 'Ma\\'lumot topilmadi.';
            break;
          case 500:
            errorMessage = 'Server xatosi. Keyinroq urinib ko\\'ring.';
            break;
          default:
            errorMessage = `Error: ${error.message}`;
        }
      }

      // Alert yoki Toastr
      alert(errorMessage);

      return throwError(() => error);
    })
  );
};
```

#### 4.5 Logging Interceptor

**logging.interceptor.ts:**

```tsx
import { HttpInterceptorFn } from '@angular/common/http';
import { tap } from 'rxjs/operators';

export const loggingInterceptor: HttpInterceptorFn = (req, next) => {
  const startTime = Date.now();

  console.log('🚀 Request:', req.method, req.url);

  return next(req).pipe(
    tap({
      next: (event) => {
        const elapsed = Date.now() - startTime;
        console.log('✅ Response:', req.url, `(${elapsed}ms)`);
      },
      error: (error) => {
        const elapsed = Date.now() - startTime;
        console.error('❌ Error:', req.url, `(${elapsed}ms)`, error);
      }
    })
  );
};
```

#### 4.6 Multiple Interceptors

**app.config.ts:**

```tsx
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { loggingInterceptor } from './interceptors/logging.interceptor';
import { authInterceptor } from './interceptors/auth.interceptor';
import { loadingInterceptor } from './interceptors/loading.interceptor';
import { errorInterceptor } from './interceptors/error.interceptor';

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(
      withInterceptors([
        loggingInterceptor,    // 1. Logging
        authInterceptor,       // 2. Token
        loadingInterceptor,    // 3. Loading
        errorInterceptor       // 4. Error handling
      ])
    )
  ]
};
```

**Tartib muhim!** Yuqoridan pastga ishlanadi.

***

### 5. Xulosa

#### ✅ linkedSignal:

* Signal sync
* O'qish va yozish
* computed() ga qaraganda moslashuvchan

#### ✅ Pipes:

* Built-in: date, number, currency
* Custom: truncate, timeAgo, phoneFormat
* Parametrli pipes

#### ✅ Directives:

* Attribute directives
* HostListener
* ElementRef, Renderer2

#### ✅ Interceptors:

* Auth token
* Loading state
* Error handling
* Logging

***

### 6. Nazorat Savollari

1. **linkedSignal va computed() farqi nima?**
2. **Pipe qanday yaratiladi?**
3. **Custom directive uchun qaysi decorator kerak?**
4. **HostListener nima?**
5. **Interceptor nima uchun kerak?**
6. **Pipe parametr qanday qabul qiladi?**
7. **ElementRef nima?**
8. **Interceptor ketma-ketligi muhimmi?**
9. **Renderer2 nima uchun ishlatiladi?**
10. **Pipe'da async operatsiya qilish mumkinmi?**

***

### 7. Test Savollari (10 ta)

**1. linkedSignal:**

* A) Faqat o'qish
* B) Faqat yozish
* C) O'qish va yozish
* D) Faqat computed

**Javob: C**

***

**2. Pipe sintaksisi:**

* A) `{{ value | pipeName }}`
* B) `{{ value -> pipeName }}`
* C) `{{ pipeName(value) }}`
* D) `{{ value : pipeName }}`

**Javob: A**

***

**3. Custom pipe:**

* A) `@Pipe() class`
* B) `implements PipeTransform`
* C) `transform()` metod
* D) Hammasi kerak

**Javob: D**

***

**4. Directive decorator:**

* A) `@Component`
* B) `@Directive`
* C) `@Injectable`
* D) `@Pipe`

**Javob: B**

***

**5. HostListener:**

* A) Element hodisalarini tinglash
* B) Faqat click
* C) Faqat input
* D) Service uchun

**Javob: A**

***

**6. Pipe parametr:**

* A) `{{ value | pipe:param }}`
* B) `{{ value | pipe(param) }}`
* C) `{{ value | pipe[param] }}`
* D) `{{ value | pipe{param} }}`

**Javob: A**

***

**7. ElementRef:**

* A) Native DOM elementi
* B) Component reference
* C) Template reference
* D) Service reference

**Javob: A**

***

**8. Renderer2:**

* A) DOM manipulatsiya
* B) Safe DOM access
* C) A va B
* D) Faqat debug

**Javob: C**

***

**9. Interceptorda errorni ushlash:**

* A) `catchError`
* B) `try-catch`
* C) `handleError`

**Javob: A**

***

**10. Directive selector:**

* A) `[appName]`
* B) `.appName`
* C) `#appName`
* D) `appName`

**Javob: A**

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

***

### 9. Amaliy Topshiriqlar

#### Topshiriq 1: Custom Pipes Set

Yarating:

* `capitalize` - Birinchi harf katta
* `plural` - Ko'plik shakli (1 kitob, 2 kitoblar)
* `fileSize` - Byte to MB/GB

***

#### Topshiriq 2: Click Outside Directive

* Element tashqarisida click
* Close dropdown/modal
* HostListener

***

#### Topshiriq 3: Debounce Directive

* Input debounce (300ms)
* Search optimization
* Custom delay parameter

***

#### Topshiriq 4: Cache Interceptor

* GET so'rovlarni cache qilish
* Cache timeout (5 min)
* Force refresh option

***

#### Topshiriq 5: Retry Interceptor

* Failed request retry
* Max 3 attempts
* Exponential backoff

***

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

{% embed url="<https://youtu.be/N-2SqS8INzY>" %}
