Derinlemesine yazılım eğitimleri için kanalımı takip edebilirsiniz...

Angular 16 – Signals Yapılanmasını Detaylı İnceleyelim

angular signals
Merhaba,

Bu içeriğimizde Angular 16 ile hayatımıza giren, geliştiriciler açısından oldukça iş kolaylaştırıcı ve bir o kadar da uygulama açısından da performans arttırıp maliyet kırıcı bir özellik olan, teknik olarak bir değişkenin verisel açıdan değişikliğe uğradığı anı yakalayabilmemiz için yeni bir yol sunan ve böylece kodu daha reaktif bir gale getirmemizi sağlayan Signal’ları tam teferruatlı inceliyor olacağız. O halde buyurun başlayalım.

Signals Nedir?

Signals; bir değişkenin, değerinin değişmesini takip eden ve değişiklik meydana geldiği taktirde reaktif bir tutum sergileyerek bunu haberdar eden yeni bir özelliktir. Bunu daha iyi anlayabilmek için aşağıdaki örnek üzerinden ilerleyelim;

    let x = 5;
    let y = 15;
    let z = x + y;
    console.log(z); // 20
    x = 10;
    console.log(z); // 20
    Yandaki çalışmaya göz atarsanız eğer z değişkeni x ile y değişkenlerinin toplamından oluşmakta ve 4. satırda bu toplam sonucu console’a yazdırılmaktadır. Buraya kadar her şey normalken, sizce 5. satırda x değişkenin değerinin değiştirilmesi 6. satırdaki z değişkeninin değerinde fark yaratacak mıdır? Tabi ki de hayır, değil mi? Çünkü z değişkeninin değeri, 3. satırdan itibaren x ve y’den bağımsız olarak varlık gösterecek ve süreçte x’de yahut y’de olabilecek herhangi bir değişiklik z’yi etkilemeyecektir!

Halbuki sizler, yazılan kodun x veya y üzerinde olacak değişikliklere karşı tepki vermesini istiyorsanız ya da bir başka değişle x veya y değerleri değiştiği taktirde z toplam sonucunun yeniden hesaplanmasını istiyorsanız, reaktif bir tutum sergilemeniz gerekmektedir. Bunun için de aşağıdaki gibi bir getter property’den istifade edebilirsiniz.

  get total() {
    return this.x + this.y;
  }
    Evet, bu şekilde reaktif davranış söz konusu olmuş olabilir amma velakin bu örneği property ile değil de, direkt olarak değişken üzerinde bir değişiklik olduğu taktirde uygulamak isteseydik işte o taktirde Signal’lardan aşağıdaki gibi istifade edebiliriz.
    let x = signal(5);
    let y = 15;
    let z = computed(() => x() + y);
    console.log(z()); // 20
    x.set(10)
    console.log(z()); // 25
    Görüldüğü üzere; signal fonksiyonu ile değer değişikliği takip edilecek olan x değişkeni tanımlanmakta, computed fonksiyonu ile ise bu değişkenin değeri değiştiği taktirde z’nin değeri yeniden hesaplanmaktadır. Değer değişiminde ise 5. satırda olduğu gibi set fonksiyonu kullanılmaktadır. Tabi ki de burada bahsi geçen fonksiyonları ve bu fonksiyonların varlık sebeplerini içeriğimizin devamındaki satırlarda başlık başlık inceliyor olacağız. Şimdilik reaktif bir tutumun Signals özelliğiyle gelen basit fonksiyonlarla nasıl kolayca sağlanabildiğini görmeniz açısından bu şekilde basit bir örneklendirmeyle hızlı girizgâh eylemiş bulunuyoruz.

Bu noktada anlayacağınız; Signal’lar, Angular çalışmalarında değişkenlerin değerlerini (değişken seviyesinde) dinamik olarak takip etmemizi ve olası değişiklik durumlarında türlü nitelikleriyle aksiyonlar almamızı ve bu aksiyonlar eşliğinde hedeflediğimiz çalışmalarımızı kolaylıkla yapmamızı sağlayan özelliktir.

Signals, değişkenlerdeki verisel değişikliklere(change detection) karşın oldukça iyi kontrol sağlamakta, uygulamanın performansını arttırmakta ve change detection açısından etkili bir kullanıcı deneyimi sunmaktadır.

Signal’lar Zone.JS’e ihtiyaç duymaksızın çalışırlar!
Evet, Signal’lar Angular’da ki Zone.JS’e ihtiyaç duymaksızın çalışma sergileyen yapılanmalardır. Zone.JS, Angular’ın asenkron işlemleri yönetirken, etkin bir şekilde çalışmasını ve event’leri işlemesini sağlayan bir çalışma modelidir. Bu model, Angular uygulamasındaki component’lerin ve diğer parçaların etkileşimini düzenlemekte ve performansı optimize edebilmektedir. Yani anlayacağınız Zone.JS; Angular uygulamasındaki herhangi bir değişikliği takip eden ve değişiklik yapılan DOM elemanlarının hemen hemen herhangi bir yerinde/noktasında çalışan component’lerin bu değişikliğe tepki vermesini sağlayan bir kütüphanedir.

Signal’ler Nasıl Tanımlanır ve Kullanılır?

Bir signal’ı tanımlayabilmek için @angular/core path’inde bulunan signal fonksiyonundan istifade ediyoruz.

import { signal } from '@angular/core';
.
.
.
let x = signal(5);

Görüldüğü üzere yukarıda ‘5’ değerine sahip bir signal tanımlanmıştır. Bu signal’ın türüne göz attığımızda aşağıdaki gibi ‘WritableSignal’ olduğunu göreceğiz;Angular 16 - Signals Yapılanmasını Detaylı İnceleyelimYani hem yazılabilir hem de okunabilir bir signal tanımlamış oluyoruz. Bunu daha da netleştirebilmek için ilgili değişkenin türünü aşağıdaki gibi de belirtebiliriz.

let x: WritableSignal<number> = signal(5);

Tabi bu tanımlama sürecinde isterseniz oluşturulacak signal’ın türünü de generic olarak belirtebilirsiniz.

let x: WritableSignal<string> = signal<string>("merhaba");

Tanımlanmış bir signal’ı kullanmak ya da bir başka deyişle signal’ın değerini okumak istiyorsanız eğer ihtiyaç noktasında x() gibi fonksiyonvari bir şekilde çağırmanız yeterlidir. Burada parantezlerin özellikle boş olmasına özen göstermeniz gerektiğine dikkatinizi çekerim.

Signal Fonksiyonları

Şimdi signal’ler üzerinde işlevsel davranışlar sergilemizi sağlayan önemli fonksiyonları inceleyelim.

  • set Fonksiyonu
    Bir signal’e ilk değerinin dışında farklı değer atamamızı yahut var olan değeri değiştirmemizi sağlayan fonksiyondur.

        let x: WritableSignal<number> = signal<number>(3);
        console.log(x());
        x.set(5);
        console.log(x());
    

    set fonksiyonunun detayında bilinmesi gereken kritik şudur ki; bir signal’da aşağıdaki gibi set fonksiyonuyla art arda değer ataması gerçekleştirilirse, template güncellemesi süreçteki tüm set işlemleri için değil, sadece sonuncusu için gerçekleştirilecektir.

    @Component({
      selector: 'app-root',
      standalone: true,
      template: `
      <button (click)="onSet()">set</button> <br>
      {{x()}}
      `
    })
    export class AppComponent {
      x: WritableSignal<number> = signal<number>(3);
      onSet() {
        this.x.set(5);
        this.x.set(10);
        this.x.set(15);
        this.x.set(20);
        this.x.set(25);
      }
    }
    

    Haliyle yukarıdaki örneği incelersek eğer butona tıklanıldığı taktirde x signal’ının değeri önce 5, sonra sırasıyla 10, 15, 20 ve son olarakta 25 olarak değiştirilecektir. Amma velakin template ki 6. satıra ise bu değişiklik tüm kademeler için yansıtılmayacak, sadece 25 değerinin atandığı sonuncu set işlemi için yansıtılacaktır.

  • update Fonksiyonu
    Signal’ın değerini, mevcut olan değerle birlikte güncellememizi sağlayan fonksiyondur.

        let x: WritableSignal<number> = signal<number>(3);
        console.log(x()); //3
        x.update(data => data * 5);
        console.log(x()); //15
    
  • mutate Fonksiyonu
    Signal, yapısal olarak içerisinde object türünden değer/nesne barındırabilecek bir özelliktir. Dolayısıyla aşağıdaki gibi signal’da ki değer bir objeyse, ilgili nesnenin içerisindeki alanlara karşın mutate fonksiyonu ile gerekli değişiklikler uygulanabilmekte ve böylece signal içerisindeki nesnenin değerleri mutasyona uğratılabilmektedir.

        let person: WritableSignal<Person> = signal<Person>({
          name: 'Hilmi',
          surname: 'Celayir',
          age: 45
        });
        console.log(person());
        person.mutate(obj => obj.name = "Şuayip");
        console.log(person());
    

    Angular 16 - Signals Yapılanmasını Detaylı İnceleyelim

  • computed Fonksiyonu – computed Signal
    Computed fonksiyonu, değerini içerisine verilen signal(ler)in değer değişiminden alan ve sonuç olarak geriye ‘Signal’ türünden bir değer dönen fonksiyondur.

    @Component({
      selector: 'app-root',
      standalone: true,
      template: `
        <button (click)="onComputed()">computed</button> <br>
        {{x()}}
      `
    })
    export class AppComponent {
      x: WritableSignal<number> = signal<number>(1);
      y: Signal<string> = computed(() => `x değişkeninin değeri ${this.x()} olarak değişti.`);
      onComputed() {
        this.x.update(data => data + 1);
        console.log(this.y());
      }
    }
    

    Yukarıdaki kod bloğunu incelerseniz eğer biri signal(x) biri de computed(y) olmak üzere iki değişken mevcuttur. Butona her tıklandığı vakit x signal’inin değeri update edilmekte ve console’a ‘y’ computed signal’inin değeri yazılmaktadır.Angular 16 - Signals Yapılanmasını Detaylı İnceleyelimŞimdi computed fonksiyonundaki maksada gelirsek eğer, içerisinde kullanılan signal(lar)’dan en az birinde olacak her değer değişimine karşı tepki vermekte ve yeniden tetiklenmektedir. Böylece computed sayesinde, birden fazla signal’ın değer değişimi takip edilebilmekte ve bu değişimlere karşın sentez yeni değerler üretilebilmektedir. Ayrıca computed fonksiyonu değer üreteceği için read-only bir davranış sergileyecektir ve bu da ‘Signal’ türüyle ifade edilmektedir.

    Computed fonksiyonu içerisinde kullanılan signal’ler dependencies/bağımlılıklar olarak nitelendirilmektedirler.

    Ayrıca computed fonksiyonunun dependency’leri dinamik bir davranış sergilemektedir. Bunun ne demek olduğunu anlayabilmek için computed‘ın çalışmasını biraz daha derinlerde gözlemlemek gerekmektedir. Bunun için aşağıdaki kod bloğunu inceleyiniz.

    @Component({
      selector: 'app-root',
      standalone: true,
      template: `
        <button (click)="onComputed()">computed</button> <br>
        {{x()}}
      `
    })
    export class AppComponent {
      check: WritableSignal<boolean> = signal<boolean>(false);
    
      x: WritableSignal<number> = signal<number>(1);
      y: Signal<string> = computed(() => {
        if (this.check())
          return `x değişkeninin değeri ${this.x()} olarak değişti.`;
        return "";
      });
      onComputed() {
        this.x.update(data => data + 1);
        console.log(this.y());
      }
    }
    

    Yukarıdaki çalışmada dikkat ederseniz computed fonksiyonu içerisinde ‘check’ signal’i üzerinden bir kontrol gerçekleştirilmektedir. Bu kontrol true olduğu sürece x signal’inin değeri computed fonksiyonunun bağımlılığı olarak değerlendirilecektir. Yok eğer false olursa x signal’i bu computed fonksiyonu tarafından bir bağımlılık olarak değerlendirilmeyecektir.

  • effect Fonksiyonu – effect Side

    effect fonksiyonu yalnızca constructor seviyesinde kullanılabilmektedir.

    Bir signal’ın değeri değiştiğinde bazı kodları çalıştırmak isteyebiliriz. Bunun için effect fonksiyonunu kullanabiliriz.

    @Component({
      selector: 'app-root',
      standalone: true,
      template: `
        <button (click)="onSet()">set</button> <br>
        {{x()}}
      `
    })
    export class AppComponent {
      constructor() {
        effect(() => console.log(`x değişkeninin mevcut değeri : ${this.x()}`))
      }
    
      x: WritableSignal<number> = signal<number>(1);
    
      onSet() {
        this.x.update(data => data + 1);
      }
    }
    

    Görüldüğü üzere 11. satırda tanımlanmış olan effect fonksiyonu ile x signal’ında olabilecek verisel değişikliklere karşın ekstradan işlemler yürütebilmekteyiz.

    Effect fonksiyonu her daim değişiklik algılama sürecini(change detection) asenkron bir şekilde yürütür.

    Uygulama süreçlerinde effect fonksiyonunun işlevine pek ihtiyaç duyulacak olmasa da, nadiren de olsa aşağıdaki gibi kritik durumlarda biçilmiş kaftan görevi görebilir;

    • Verileri değişiklik durumlarında loglarken
    • Veriler, değişiklik neticelerinde local storage gibi ortamlara senkronize edilirken
    • Özel DOM davranışları eklenirken

     
    Ayrıca bir effect oluşturulduğunda, onun yaratıldığı component, directive yahut pipe destroy edildiğinde otomatik effect’te yok edilir. Bunun dışında manuel bir şekilde oluşturulmuş bir effect’i destroy etmek istiyorsanız aşağıdaki gibi effect fonksiyonunun döndürdüğü ‘EffectRef’ türünden olan nesneyi bir referansla karşılayarak, ‘destroy’ fonksiyonunu iradenizle çağırabilirsiniz.

        const _effect: EffectRef = effect(() => console.log(`x değişkeninin mevcut değeri : ${this.x()}`))
        _effect.destroy();
    

    Peki hoca, bir effect içerisinde herhangi bir signal’ın değerini değiştirebilir miyiz? Ki değiştirdiğimizi düşündüğümüzde sonsuz bir döngü ihtimali var gibi…

    Güzel soru… Evet, effect gibi bir fonksiyonu gördüğünde insanın aklına direkt bu tarz bir soru geliyor diyebiliriz. Şimdi bu soru nezdinde effect içerisinde bir signal’in değerini değiştirmeye çalışarak gözlemde bulunalım.

        effect(() => {
          console.log(`x değişkeninin mevcut değeri : ${this.x()}`);
          this.x.update(data => data + 1);
        })
    

    Bu işlem neticesinde aşağıdaki gibi bir hatayla karşılaşıyoruz:
    Angular 16 - Signals Yapılanmasını Detaylı İnceleyelimHatanın metinsel hali;

    ERROR Error: NG0600: Writing to signals is not allowed in a `computed` or an `effect` by default. Use `allowSignalWrites` in the `CreateEffectOptions` to enable this inside effects.

    Yani anlayacağınız effect ve hatta computed içerisinde herhangi bir signal’in değerini değiştiremiyoruz. Ama bu davranışı gerçekleştirebilmek için allowSignalWrites özelliği ile müdahalede bulunabiliyoruz.

        effect(() => {
          console.log(`x değişkeninin mevcut değeri : ${this.x()}`);
          this.x.update(data => data + 1);
        }, { allowSignalWrites: true })
    

    allowSignalWrites özelliği ile effect içerisinde signal değerinin değiştirilmesine izin verilmesi pekte tavsiye edilen bir yaklaşım değildir!

    effect ile ilgili inceleyeceğimiz bir başka konu ise cleanup function‘dır. Nedir bu cleanup function diye sorarsanız eğer şöyle izah edelim… Effect’ler, ister istemez bazı durumlarda uzun süren operasyonları başlatabilir ve bu operasyonları üst üste bindirebilirler. Şöyle ki;

        effect(() => {
          console.log(this.x());
    
          const timer = setTimeout(() => {
            console.log("Processing...");
          }, 2000);
        })
    

    Angular 16 - Signals Yapılanmasını Detaylı İnceleyelimYukarıdaki kod bloğunu incelerseniz x signal’ında olacak her verisel değişiklikte bu effect tetiklenecek ve her tetiklenmeye karşılık 2 saniyelik bir process başlatacaktır. Ve bu process’ler arkaplanda effect’ler için işlemeye devam edecektir. Bunun için yandaki görseli inceleyebilirsiniz. Effect’in içerisindeki process’ler işlemleri bittikçe nihayete ermekte ve console’a çıktı vermektedirler.

    Bizler bu tarz effect‘in seri bir şekilde tetiklendiği durumlarda, effect içerisindeki process bitmeden başka bir tetiklenme neticesinde başlayan process’i işlemektense, yeni bir process başladığı taktirde bir önceki effect‘in process’ini durdurmak ve böylece en sonuncu effect‘e göre işlem yapmak istiyorsak cleanup function‘ı kullanabiliriz.

    cleanup function‘ı, bir sonraki effect başlamadan önce yahut ilgili effect destroy edildiğinde çağrılan bir callback function‘dır ve aşağıdaki gibi kullanılır.

        effect(onCleanup => {
          console.log(this.x());
    
          const timer = setTimeout(() => {
            console.log("Processing...");
          }, 2000);
    
          onCleanup(() => {
            clearTimeout(timer);
          })
        })
    

    Angular 16 - Signals Yapılanmasını Detaylı İnceleyelimGörüldüğü üzere; her effect içerisindeki process, işlemi bitmeksizin bir sonraki effect tarafından yeniden başlatılma ihtimaline nazaran 8 ile 10. satır aralığında ‘onCleanup’ fonksiyonu tarafından iptal edilmekte ve process süreci bir sonraki effect‘e bırakılmaktadır. Haliyle bu şekilde çalışmanın neticesinde yandaki görseldeki davranışla karşılaşılmaktadır.

    Anlayacağınız, callback function bizlere, yeni bir effect fire olduğu taktirde eski effect‘leri destroy eden bir ortam sağlamakta ve böylece olası maliyetleri mümkün mertebe düşürebileceğimiz senaryolarda daha performanslı bir yaklaşım sergilememize imkan tanımaktadır.
     
     

    Cleanup function, yeni effect fire olduğunda eski effect’leri destroy eder!

    Ve son olarak bir signal’ın değeri değiştiğinde herhangi bir effect‘in tetiklenmesini istemiyorsak nasıl bir davranış sergilememiz gerektiğini inceleyelim. Bunun için untracked fonksiyonunu kullanabiliriz. untracked fonksiyonunda, effect tarafından bağımlılık olarak değerlendirilmemesi gereken bazı harici kodlar çalıştırılabilir.

    import { Component, WritableSignal, effect, signal, untracked } from '@angular/core';
    
    @Component({
      selector: 'app-root',
      standalone: true,
      template: `
        <button (click)="onSet()">set</button> <br>
        {{x()}}
      `
    })
    export class AppComponent {
      constructor() {
        effect(() => {
          console.log(`x değişkeninin mevcut değeri : ${this.x()}`);
    
          untracked(() => {
            this.y.update(data => data - 1);
            console.log(`y değişkeninin mevcut değeri : ${this.y()}`);
          });
        })
      }
      x: WritableSignal<number> = signal<number>(1);
      y: WritableSignal<number> = signal<number>(999);
    
      onSet() {
        this.x.update(data => data + 1);
      }
    }
    

    Yukarıdaki kod bloğunu incelerseniz eğer x signal’ının değerine göre tepki verecek bir effect olduğunu göreceksiniz. Bu effect içerisindeki untracked fonksiyonu ile yürütülen işlemler effect‘in bağımlılık göstermeyeceği çalışmaları ifade etmektedir. Haliyle y signal’ının değerinin değişmesi ve console’a yazdırılması ancak x signal’ının değeri değiştiğinde effect‘in tetiklenmesiyle gerçekleşecektir.

Böylece signal’ler üzerinde işlevsel davranışlar sergilememizi sağlayan fonksiyonları tam teferruatlı incelemiş olduk. Şimdi ise observable ve signal arasındaki dönüşümleri incelemeye odaklanabiliriz.

RxJS Interop

Angular 16. versiyonunda observable’lar ile signal’lar arasında dönüşüm gerçekleştirebilmek için @angular/core/rxjs-interop path’inde toSignal ve toObservable fonksiyonları gelmiş bulunmaktadır. Şimdi bu fonksiyonlara tek tek odaklanarak kullanım durumlarının farkındalığını oluşturalım.

toSignal ve toObservable fonksiyonlarının her ikisi de constructor seviyesinde kullanılmalıdır! Aksi taktirde uygulamanın hata vermesine sebebiyet verirler.

  • toSignal Fonksiyonu
    toSignal fonksiyonu ile bir observable nesnesi read-only olan(yani Signal referansına sahip) bir signal’a dönüştürülmektedir. Misal olarak aşağıdaki kod bloğunu incelersek eğer;

    import { Component, effect } from '@angular/core';
    import { interval } from 'rxjs';
    import { toSignal } from '@angular/core/rxjs-interop';
    
    @Component({
      selector: 'app-root',
      standalone: true,
      template: ``
    })
    export class AppComponent {
      constructor() {
        effect(() => console.log(`counter signal'ının değeri : ${this.counter()}`))
      }
      counter$ = interval(1000);
      counter = toSignal(this.counter$);
    }
    

    Angular 16 - Signals Yapılanmasını Detaylı İnceleyelimÖrnekte görüldüğü üzere interval ile 1 saniyelik bir periyoda sahip olan observable yaratılmakta ve bu toSignal fonksiyonu ile signal’a dönüştürülmektedir. Haliyle ‘counter’ signal’ının değeri her 1 saniyede bir değişeceği için her değişime karşılık 12. satırda tanımlanmış olan effect fonksiyonu tepki verecek ve ilgili signal’ın değerini yandaki görselde olduğu gibi console’a yazdıracaktır.

    Burada unutulmaması gereken husus şudur ki; toSignal fonksiyonu, observable’a hemen subscribe olacaktır ve bu durum bazı durumlarda istenmeyen yan etkilere(side effect) sebep olabilir.

    Ayrıca observable’ların bir başlangıç değeri olmayacağı için toSignal fonksiyonu ‘Signal<number | undefined>’ gibi bir referans üretebilmektedir, bundan dolayı ilgili fonksiyonun kullanımı neticesinde üretilecek signal’a ilk değerini vermek istiyorsanız aşağıdaki gibi ‘initialValue’ property’sini kullanabilir ve böylece üretilecek signal’da ki undifined terimini kaldırıp, kesin bir referansla üretilmesini sağlayabilirsiniz.

    @Component({
      selector: 'app-root',
      standalone: true,
      template: ``
    })
    export class AppComponent {
      constructor() {
        effect(() => console.log(`counter signal'ının değeri : ${this.counter()}`))
      }
      counter$ = interval(1000);
      counter = toSignal(this.counter$, { initialValue: 999 });
    }
    

    Yukarıdaki tanım neticesinde ‘counter’ signal’ı ilk olarak 999 değerine sahip olacak sonrasında ise ‘counter$’ observable’ında ki akışa göre değeri güncellenecektir.Angular 16 - Signals Yapılanmasını Detaylı İnceleyelimTabi ayriyeten bu işlemi observable’ın kaynağı hemen yayması durumunda ‘requireSync’ property’sine true değerini vererekte gerçekleştirebiliriz.

    @Component({
      selector: 'app-root',
      standalone: true,
      template: ``
    })
    export class AppComponent {
      constructor() {
        effect(() => console.log(`counter signal'ının değeri : ${this.counter()}`))
      }
      counter$ = interval(1000);
      counter = toSignal(this.counter$, { requireSync: true });
    }
    

    Yukarıdaki koda göz atmadan önce, bir önceki satırda ‘…kaynağı hemen yayması durumunda…’ ibaresine odaklanmanızı istiyorum. Buradan anlaşılan, signal’a dönüştürülmüş observable eğer ki hemen veri yayınlamazsa bu yöntem Angular tarafından hata fırlatılmasına sebebiyet verecektir.Angular 16 - Signals Yapılanmasını Detaylı İnceleyelimHaliyle bu soruna karşın çözüm getirebilmek için ilgili observable’a aşağıdaki gibi bir başlangıç değeri vermemiz yeterli olacaktır.

    import { Component, effect } from '@angular/core';
    import { interval, startWith } from 'rxjs';
    import { toSignal } from '@angular/core/rxjs-interop';
    
    @Component({
      selector: 'app-root',
      standalone: true,
      template: ``
    })
    export class AppComponent {
      constructor() {
        effect(() => console.log(`counter signal'ının değeri : ${this.counter()}`))
      }
      counter$ = interval(1000).pipe(startWith(0));
      counter = toSignal(this.counter$, { requireSync: true });
    }
    

    Ayrıca son olarak toSignal fonksiyonu ile dönüştürülen observable bir hata verdiğinde bunu aşağıdaki gibi try – catch blokları eşliğinde kolayca handle edebilir ve uygulamanızdaki çalışmalara devam edebilirsiniz.

    export class AppComponent {
      constructor() {
        effect(() => console.log(`counter signal'ının değeri : ${this.counter()}`))
      }
      counter$ = interval(1000).pipe(startWith(0));
      counter = toSignal(this.counter$, { requireSync: true });
    
      ngOnInit() {
        try {
          this.counter();
        } catch (e) {
          console.log(e);
        }
      }
    }
    
  • toObservable Fonksiyonu
    Bir signal’ı, observable nesnesine dönüştüren fonksiyondur. Bunun için aşağıdaki kod bloğunu inceleyebilirsiniz.

    import { Component, WritableSignal, effect, signal } from '@angular/core';
    import { toObservable } from '@angular/core/rxjs-interop';
    
    @Component({
      selector: 'app-root',
      standalone: true,
      template: ``
    })
    export class AppComponent {
      count: WritableSignal<number> = signal(1);
      count$ = toObservable(this.count);
      increment() {
        setTimeout(() => {
          this.count.update(data => data + 1);
          this.increment();
        }, 1500);
      }
    
      ngOnInit() {
        this.increment()
        this.count$.subscribe(data => console.log(data));
      }
    }
    

NgRx 🤝 Signals

İçeriğimiz boyunca bu satırlara kadar signal’ler için incelediğimiz tüm niteliklerin esasında state management açısından da bizlere oldukça kolaylık ve bir o kadar da performanslı bir yaklaşım sergileme imkanı getirdiğinin farkındasınızdır. Bu durum, signal’lerin gelecekteki Angular projelerinin çoğunda NgRx’in yerini alma potansiyeline sahip olduğunu göstermektedir. Bu şimdilik bir öngörüdür. Tabi ki de zamanın getirdikleriyle bu öngörüyü olgunlaştıracak yeni durumları bir bütün olarak masaya yatıracak ve hep beraber konuya dair hususi başlık altında, başka bir içeriğimizde incelemede bulunuyor olacağız.

Nihai olarak;
Signal’lar, Angular uygulamalarını daha hafif kılmaya ve zamanla mimariyi Zone.JS gibi bir kütüphanenin etkisinden törpülemeye işaret etmektedirler. Evet, signal’ler henüz erken bir aşamadadır lakin bu durum Angular’ın gidişatı açısından parlak bir geleceğe heyecan yaşamamızı engelleyici bir unsur değildir.

İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Bunlar da hoşunuza gidebilir...

3 Cevaplar

  1. Hakki Yilmaz dedi ki:

    Hocam cok aciklayici ve faydali bir makale olmus .Elinize saglik.Angular 16 da ki yenilikler hakkinda daha cok makale bekliyoruz sizden.

  2. namiq dedi ki:

    tesekkurler

  3. Gençay dedi ki:

    Sevgili okurların dikkatine;

    Angular 17’de mutate fonksiyonu çıkarılmıştır. Onun yerine update fonksiyonu kullanılmalıdır!

    Örnek olarak;

      person: WritableSignal<Person> = signal({ name: "Gençay", surname: "Yıldız" });
      ngOnInit() {
        this.person.update(p => {
          p.name = "123"
          return p;
        })
      }
    

    Bilginize…

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir