Angular 16 ile sunulan ve geliştiricilerin hayatını kolaylaştıran uygulama performansını artırırken maliyetleri de düşüren yeni bir özellik olan Signals ayrıntılı olarak inceleyeceğiz. Signals, bir değişken veri değişikliğine uğradığında bunu yakalamak için yeni bir yol sunarak kodu daha reaktif hale getirir. Öyleyse başlayalım.

 

z: number = 5;
y: number = 5;
m: number = this.z + this.y
Elimizde z ve y değişkenleri var ve bu değişkenlerin toplamını m değişkeninde gösteriyoruz.
constructor() {
   console.log('ilk toplama işleminin sonucu:', this.m)
    this.y += 10;
    console.log("y'nin değeri:", this.y)
    console.log("z'nin değeri:", this.z)
    console.log('toplam:', this.m)
}

y değişkeninin değeri değişiyor, ancak bu değişikliği mdeğişkeni algılamıyor. Değişiklikleri algılamalarını istiyorsak eğer burada devreye signal yapısı giriyor. Gelin şimdi bu değişkenleri signal türünde tanımlayalım.


z: WritableSignal<number> = signal(5);
y: WritableSignal<number> = signal(5);
m: Signal<number> = computed(() => this.y() + this.z())

constructor() {
  console.log('ilk toplama işleminin sonucu:', this.m())
  this.y.set(10);
  console.log("y'nin değeri:", this.y())
  console.log("z'nin değeri:", this.z())
  console.log('toplam:', this.m())
}

görmüş olduğunuz üzere oluşturulan tüm değişkenleri signal olarak tanımladık ardından y değerine yeni atamış olduğumuz değer m değişkeninde tetiklenip güncellendi.

Buraya kadar kafanızda otursun die ön bir hazırlık yaptım şimdi ise gelin detaylı bir inceleme yapalım.

 

Signals Nedir?

 

Bir değişkenin değerindeki değişiklikleri izleyen ve bir değişiklik meydana geldiğinde bizi tepkisel olarak bilgilendiren yeni bir özelliktir.

 

Signals’in Kullanımı ve Tanımlanması

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

 

 

isLogin: WritableSignal<boolean> = signal(false);

 

 

burada isLogin adında bir signal değişkeni tanımladık ve ilk değer olarak ise true değerini atadık bu signalin tipini ise WritableSignal (hem okunabilir hem ise yazılabilir) olarak tanımladık ancak bu signal boolean tipinde değer alacak.

Gelelim şimdi ise tanımladığımız ya da oluşturmuş olduğumuz bu signal’in değerini ekranda çıktısını görelim.

 

Html tanımlaması:

Ekran çıktısı:

Görselde görüldüğü üzeri oluşturmuş olduğumuz signal değişkeninin değerini görüyorsunuz. Signal değişkeninin değerini okuyabilmek için kesinlikle “değişkenadı()” şeklinde olmalı ayrıca parantezin içine kesinlik bir değer girmeyin eğer girerseniz ise hata verecektir.

 

Şimdi sıra geldi biz varsayılan olarak verdiğimiz signal değerini nasıl değiştirebiliriz?

 

Eğer bir signal değişkenin değerini değiştirmek istiyorsanız signalin bazı fonksiyonlarından yararlanmak zorundayız.

 

1-) Set Fonksiyonu

 

Signal ile oluşturulan bir değişkene ilk değerinin dışında farklı bir değer atamamızı ya da var olan değeri değiştirmemizi sağlayan fonksiyondur.

 

İlk olarak bir tane button oluşturup sonrasında ise buna bir click özelliği atayıp ardından ise buttona tıklandığında daha önceden oluşturulan signal değişkeninin değerini set ise tam tersine çevireceğiz.

Tıklama işlemi yapılmadan önce:

Tıklama işlemi yapıldıktan sonra:

 

varsayılan olan false değerini signal’in sağlamış olduğu set fonksiyonu ile değiştirip true değerini çevirdik.

Burada önemli bir kritik durum var eğer sizler art arda set fonksiyonunu kullanırsanız, template tüm set işlemleri için tek tek ekranda güncelleme yapmaz onun yerine en son gerçekleşecek olan set işlemi için güncelleme yapar.

 

 

Eğer butona tıklanıldığında count signal’ının değeri önce 2, sonra sırayla 3, 4 ve son olarakta 5 olarak değiştirilecektir. Ancak yukarıda dediğim gibi template tüm set işlemleri için ekranda değişilikleri göstermeyip sadece son set işlemi için değişiklik gösterecektir.

 

2-) Update Fonksiyonu

 

Elimizde var olan değer ile güncelleme yapar yani diyelim number tipinde WritableSignal (hem okunabilir hem ise yazılabilir) signalimiz mevcut bunun değeri ilk başta 5 olsun sonradan ise bu 5 değeri belli bir değere göre her çarpımda yeni bir değer elde edip ekranda gösterilsin.

 

 

görmüş olduğunuz gibi ilk başta count değişkenimizin değeri 1 sonradan ise her butona tıkladığımda count değerimizin değeri 5 ile çarpıp yeni bir değer elde edecek ben ne kadar click olayı yaparsam count değişkenimizin değeri artacak.

 

 

ilk tıklamada geldi 1*5 yapıp 5 değerini elde etti ve bunu count değişkenimize adı şimdi ise tekrar click olayını yaparsam bu sefer 5*5 yapıp yeni değerini tekrardan count değişkenine atayacak.

 

 

tıklama olayı tekrardan gerçekleşip count değişkenine atandı.

 

Peki biz nesneleri güncelleme işlemini nasıl yapacağız derseniz ise yine update fonksiyonu ile yapacağız.

 


interface User {
  name: string,
  lastName: string,
  age: number
}@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, RouterOutlet],
  template: `
    <button (click)="btnClick()">Göster</button>
  `,
  styleUrl: './app.component.scss'
})
export class AppComponent {
  users: WritableSignal<User> = signal<User>({
    name: "Mücahit",
    lastName: "Yener",
    age: 26
  })
  btnClick(): void {
    console.log(this.users())
    this.users.update((value: User) => {
      value.age = 25;
      return value
    })
    console.log(this.users())
  }
}

 

Elimde users adında bir signal türünde değişken var ve buna değer olarak bir nesne atamışım bu nesnede bulunan age bilgisini butona tıklandığında güncelleyeceğiz.

 

 

Tıklama işlemi tamamlandıktan sonra console üzerinden age bilgisinin ilk hali ve sonrasında ise update fonksiyonu yardımıyla değiştirip son hali görmüş olduk.

 

this.users.update((value: User) => {
      value.age = 25;
      return value
    })this.users.update((value: User) => {
     return value.age = 25;
    })

 

Bazı arkadaşlar burada neden 2. yazmış olduğumuz kod yerine 1. yazmış olduğumuz kodu kullandık die sorarsa bu users değişkeni bir signal ayrıca bu bir nesne biz bu users değişkenine return olarak 25 değerini dönemeyiz çünkü users bir nesne olduğundan geriye nesne dönmek zorundayız.

 

Peki elimde signal türünde bir array var bu arrayin içerisine yeni bir nesne nasıl atayabilirim diyorsanız eğer aşağıdaki kodu incelleyebilirsiniz:

 


  users: WritableSignal<User[]> = signal<User[]>
  ([
    {name: "Mücahit", lastName: "Yener", age: 26, status: false},
  ])

  btnClick(): void {
    console.log(this.users())
    this.users.update((item) => {
      return [...item, {name: "Efe", lastName: "Yener", age: 26, status: false}]
    })
    console.log(this.users())
  }

 

user adında bir değişkenim var yine ancak bu sefer nesne değil bu bir array ve içerisinde nesne barındırıyor burada button click yardımıyla var olanı alıp yeni nesnemiz ile user değişkenimizi update ediyoruz.

 

 

console üzerinden de görebiliyoruz önce tek bir değer varken sonrasında ikinci değeri bir nevi push yapıyoruz.

 

3-) Mutate Fonksiyonu (Kaldırıldı)

 

4-) Computed Fonksiyonu

 

Computed fonksiyonu, kendisine verilen singal türündeki değişkenleri anlık olarak takip eder ve bu değişikliklere bağlı olarak geriye signal türünde değer döndüren fonksiyondur.

 

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, RouterOutlet],
  template: `
    <button (click)="btnClick()">Göster</button>
    {{this.result()}}
  `,
  styleUrl: './app.component.scss'
})
export class AppComponent {
  count: WritableSignal<number> = signal<number>(1)
  result: Signal<string> = computed(() => `count: ${this.count()}`)

  btnClick(): void {
    this.count.update((count: number) => count * 2)
  }
}

 

Burada 2 tane değişken var ancak bunlardan biri computed(result) diğeri ise signal(count)tur.

 

count değişkeni her değiştiğinde yani buttona click olayı gerçekleştiği sırada result değişkeni tetiklenecektir çünkü result değişkeni signal türünde oluşturulan count değişkenini takip ediyor. Peki bu computed fonksiyonunu bize ne fayda sağlıyor diyorsanız içerisinde kullanılan signallardan en az birinde olacak değişime karşı tepki vermekte ve yeniden tetiklenmektedir.

 

5-) Effect Fonksiyonu

 

Tanımlamış olduğunuz signal türündeki değişkenlerin değerleri değiştiğinde tetiklenmesini istediğiniz kodlar varsa bunları effect fonksiyonu yardımıyla çalıştırabiliriz.

 

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, RouterOutlet],
  template: `
    <button (click)="btnClick()">Göster</button>
  `,
  styleUrl: './app.component.scss'
})
export class AppComponent {
  x: WritableSignal<number> = signal<number>(1);
  y: WritableSignal<number> = signal<number>(1);

  constructor() {
    effect(() => {
      if (this.x()) {
        console.log('x değişti')
      } else if (this.y()) {
        console.log('y değişti')
      }
    })
  }
  btnClick(): void {
    this.x.update((count: number) => count * 2)
  }
}

 

x ve y adında signal türünde 2 tane değişkenim var bu değişkenlerden biri olan x değişkenini button yardımıyla update ediyorum bu update sonucunca effect fonksiyonu devreye girip x değişkeninin değerini değiştiğini algılayıp if kontrolü çalışıyor.

 

 

console üzerinden de gördüğümüz üzere tıklama işlemi sonrasında x değeri update edilip effect fonksiyonu devreye girip hangisinin değişitiğini algılıyor.

 

Peki effect içerisinde signal türünde bir değişkeni değiştirmek mümkün mü?

 

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, RouterOutlet],
  template: `
    <button (click)="btnClick()">Göster</button>
  `,
  styleUrl: './app.component.scss'
})
export class AppComponent {
  x: WritableSignal<number> = signal<number>(1);
  y: WritableSignal<number> = signal<number>(1);

  constructor() {
    effect(() => {
      this.x.update((count: number) => count * 2)
    })
  }
  btnClick(): void {
    this.x.update((count: number) => count * 2)
  }

 

 

ilk incelememde aklıma benimde gelmedi değil :) ancak böyle bir söz konusun durum normal şartlarda mevcut değil ancak bunun için ekstra bir özellik olan allowSignalWritessahip olursak değişim mevcut oluyor ama bu çokta önerilmiyor.

 

effect(() => {
  this.x.update((count: number) => count * 2)
}, { allowSignalWrites: true })

 

şeklinde kodumuzu düzenlersek console üzerinden herhangi bir hata ile karşılaşmayacağız ancak bu örnekte dikkat etmeniz gereken effect fonksiyonunda herhangi bir şart durumu yok biz değişkenimizi ilk tanımladığımızda 1 değerini atadığımızda varsayılan olarak bundan ötürü effect tetiklenecektir anlık olarak 1 değerini 2 olarak güncelleyecektir sonrasında ise sizler click işlemi yaptığınız taktirde 2 değeri 4 olup ardından effect fonksiyonu sayesinde 8 olacaktır bu durum kafanızı karıştırmasın.

 

Sıra geldi effect ile inceleyeceğimiz başka bir konuya bu ise cleanup function. Effect içerisinde uzun süren işlemler yapabilirsiniz bu işlemler bitmeden başka bir signal türü değiştiğinde tekrardan tetiklenecektir bundan dolayı ne kadar değişiklik olursa olsun her zaman son yapılan değişiklikten sonra effect fonksiyonu tetiklensin bunun için basit bir örnek yapıp konuyu bitirelim.

 


@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, RouterOutlet],
  template: `
    <button (click)="btnClick()">Tıkla</button>`,
  styleUrl: './app.component.scss'
})
export class AppComponent {

  count: WritableSignal<number> = signal<number>(1);

  constructor() {

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

  btnClick(): void {
    this.count.update((count: number) => count * 2)
  }
  

gifte görmüş olduğunuz ekranın kod çıktısı yukarıdaki ancak biz bunu yukarıda dediğim gibi değişim süreci ne zaman sonlanırsa o zaman çalıştır şeklinde yapacağız onun için ise aşağıdaki kodu inceleyebilirsiniz.

 

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

 

gif üzerinde görüldüğü üzere art arda tıklamayı işemini onCleanup ile dinliyor aktif olan setTimeoutu clearTimeout ile sonlandırıyor hızlı bir şekilde art arda tıklama işlemi bitince ise setTimeout normal bir şekilde çalışıyor.

 

Umarım buraya kadar anlattıklarımı anlamışsınızdır ya da umarım ben anlatabilmişimdir. :)

 

Peki bu signal için bir iki örnek verebilir misiniz die sorarsanız ben genellikle kullanıcı login olup olmadığı durumları kontrol edip ona göre navbarda bulunan login ve logout alanlarını gösterip gizliyorum ya da elimde bir ürün listesi vardır sürekli bir yerlerde servis çağıracağıma ya da subjectlerle veriyi elimde tutacağıma signal sayesinde rahat ve kısa kodlar veriyi elimde tutup ortak olan tüm alanlara rahat bir şekilde çekiyorum bu sayede verilerde oluşan değişiklikler anlık olarak değişken nerelerde kullanıldıysa oralarda gösteriliyor.

 

Örnek say say bitmez ancak performan anlamında da signaller mükemmel.

 

Umarım faydalı örnekler olmuştur kendinize iyi bakın görüşmek üzere selametle kalın.