{"id":15668,"date":"2023-04-05T14:21:32","date_gmt":"2023-04-05T12:21:32","guid":{"rendered":"https:\/\/vived.io\/angular-signals-w-10-minut-frontend-weekly-vol-132\/"},"modified":"2023-04-07T12:20:54","modified_gmt":"2023-04-07T10:20:54","slug":"angular-signals-w-10-minut-frontend-weekly-vol-132","status":"publish","type":"post","link":"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/","title":{"rendered":"Angular Signals w 10 minut | Frontend Weekly vol. 132"},"content":{"rendered":"\n<p>Wydaje si\u0119, \u017ce spo\u0142eczno\u015b\u0107 Angulara m\u00f3wi tylko o jednym &#8211; Signals. Nowo opublikowane <a href=\"https:\/\/github.com\/angular\/angular\/discussions\/49685\">RFC<\/a> (Request for Comments) jest naprawd\u0119 ogromne i dlatego postanowili\u015bmy go troch\u0119 podsumowa\u0107. Je\u015bli chcesz dog\u0142\u0119bnie zrozumie\u0107 Signals i pozna\u0107 wszystkie decyzje projektowe z nimi zwi\u0105zane &#8211; oryginalne <a href=\"https:\/\/github.com\/angular\/angular\/discussions\/49685\">RFC<\/a> wci\u0105\u017c pozostaje najlepszym miejscem \u017ceby to zrobi\u0107. Je\u015bli jednak nie masz czasu przebija\u0107 si\u0119 przez 5 rozleg\u0142ych dokument\u00f3w, w tym artykule znajdziesz wszystkie najwa\u017cniejsze informacje, a jego przeczytanie nie zajmie Ci wi\u0119cej ni\u017c 10 minut.<\/p>\n\n\n\n<p>Angular Signals s\u0105 ju\u017c dost\u0119pne jako eksperymentalna funkcjonalno\u015b\u0107 w Angular 16 RC (kilku funkcjonalno\u015bci opisanych w tym artykule wci\u0105\u017c jednak brakuje). Nie wiemy kiedy Signalns stan\u0105 si\u0119 stabiln\u0105 funkcjonalno\u015bci\u0105. Zaleca\u0142bym uzbrojenie si\u0119 w cierpliwo\u015b\u0107, poniewa\u017c w RFC jest jeszcze kilka gor\u0105cych punkt\u00f3w dyskusyjnych.<\/p>\n\n\n\n<h2 id=\"dlaczego-potrzebujemy-angular-signals\" data-num=1>Dlaczego potrzebujemy Angular Signals? <\/h2>\n\n\n\n<p>Zasadniczo istniej\u0105 dwa g\u0142\u00f3wne powody: Po pierwsze, <code>zone.js<\/code> jest najwi\u0119ksz\u0105 bol\u0105czk\u0105 programist\u00f3w korzystaj\u0105cych na codzie\u0144 z Angulara. Po drugie, RxJS nie jest najlepsz\u0105 abstrakcj\u0105 do zarz\u0105dzania stanem.  Teraz przyjrzymy si\u0119 obu powodom w szczeg\u00f3\u0142ach.<\/p>\n\n\n\n<p><code>zone.js<\/code> to biblioteka robi\u0105ca monkey-patching* wi\u0119kszo\u015bci asynchronicznych metod przegl\u0105darki, w celu powiadomiania Angulara o mo\u017cliwych zmianach w aplikacji. Kiedy dowolne asynchroniczne API przegl\u0105darki zostaje wywo\u0142ane Angular uruchamia proces Change Detection, aby wykry\u0107, czy co\u015b zmieni\u0142o si\u0119 w aplikacji. Jak mo\u017cna sobie wyobrazi\u0107, to zachowanie prowadzi do wielu nadmiarowych uruchomie\u0144 procesu Change Detection. W rezultacie mechanizm ten jest trudny zar\u00f3wno do optymalizacji jak i debugowania. <a href=\"https:\/\/indepth.dev\/posts\/1515\/deep-dive-into-the-onpush-change-detection-strategy-in-angular\" target=\"_blank\" rel=\"noreferrer noopener\"><code>ChangeDetectionStrategy.OnPush<\/code> <\/a>czyni sytuacj\u0119 nieco lepsz\u0105, ale g\u0142\u00f3wne problemy pozostaj\u0105 takie same. Aplikacje pozbawione <code>zone.js<\/code> by\u0142y niespe\u0142nionym marzeniem deweloper\u00f3w Angulara od lat. Projekty takie jak <a href=\"https:\/\/www.rx-angular.io\/\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">RxAngular<\/a> odzwierciedlaj\u0105 podej\u015bcie spo\u0142eczno\u015bci do rozwi\u0105zania problemu.<\/p>\n\n\n\n<p>RxJS sprawdza si\u0119 \u015bwietnie, gdy potrzebujemy modelowa\u0107 z\u0142o\u017cone zdarzenia dziej\u0105ce si\u0119 na przestrzeni czasu, takie jak limitowanie zapyta\u0144 do backendu czy agregowanie klikni\u0119\u0107 myszk\u0105. Je\u015bli chodzi o zarz\u0105dzanie stanem, RxJS nie sprawdza si\u0119 ju\u017c tak dobrze. Zacznijmy od tego, \u017ce strumie\u0144 nie musi przechowywa\u0107 warto\u015bci i jest to wpisane w architektur\u0119 strumieni. Nawet je\u015bli u\u017cywamy <code>BehaviurSubject<\/code> i <code>shareReplay(1)<\/code> wsz\u0119dzie gdzie to mo\u017cliwe, nasz kod szybko zalany zostje nullami i niebezpiecznym rzutowaniem typ\u00f3w. Strumienie naturalnie grawituj\u0105 w kierunku zimnych strumieni, co prowadzi do wielu zb\u0119dnych kalkulacji i zaskakuj\u0105cych efekt\u00f3w ubocznych. RxJS wprowadza r\u00f3wnie\u017c wiele narzutu dla pocz\u0105tkuj\u0105cych &#8211; na przyk\u0142ad czym jest <code>Subject<\/code> i <code>BehaviourSubject<\/code>, czym s\u0105 zimne i gor\u0105ce strumienie, dlaczego istnieje wiele operator\u00f3w map i kiedy dok\u0142adnie wykonywany jest napisany kod. <\/p>\n\n\n\n<p>Signalns maj\u0105 na celu rozwi\u0105zanie wi\u0119kszo\u015bci problem\u00f3w wymienionych w powy\u017cszych akapitach. Warto jednak pami\u0119ta\u0107, \u017ce ani <code>zone.js<\/code>, ani RxJS nigdzie si\u0119 w najbli\u017cszym czasie nie wybieraj\u0105. Fakt, \u017ce pojawi si\u0119 alternatywny spos\u00f3b na rozwi\u0105zaywanie problem\u00f3w nie oznacza, \u017ce stary automatycznie staje si\u0119 deprecated.<\/p>\n\n\n\n<p>*moneky-patching &#8211; technika u\u017cywana do dynamicznego aktualizowania zachowania kodu w czasie rzeczywistym. monkey-patching jest sposobem na rozszerzenie lub modyfikacj\u0119 kodu runtime j\u0119zyk\u00f3w dynamicznych bez zmiany oryginalnego kodu \u017ar\u00f3d\u0142owego.<\/p>\n\n\n\n<h2 id=\"angular-signals-api\" data-num=2>Angular Signals API<\/h2>\n\n\n\n<p>Cytuj\u0105c oryginalne RFC: Sygna\u0142 jest opakowaniem wok\u00f3\u0142 warto\u015bci, kt\u00f3re jest w stanie powiadomi\u0107 zainteresowanych konsument\u00f3w, gdy ta warto\u015b\u0107 si\u0119 zmienia. Sygna\u0142y mog\u0105 by\u0107 tworzone za pomoc\u0105 factory method <code>signal()<\/code> przyjmuj\u0105c\u0105 warto\u015b\u0107 pocz\u0105tkow\u0105 jako parametr. Sygna\u0142y mog\u0105 by\u0107 komponowane za pomoc\u0105 metody <code>computed<\/code>(). Mo\u017cliwe jest r\u00f3wnie\u017c wywo\u0142ywanie efekt\u00f3w ubocznych, gdy warto\u015b\u0107 sygna\u0142u si\u0119 zmienia, rejestruj\u0105c odpowiedni callback w metodzie <code>effect()<\/code>. W dowolnym momencie mo\u017cemy odczyta\u0107 warto\u015b\u0107 sygna\u0142u wywo\u0142uj\u0105c go jak funkcj\u0119.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">const counterA = signal(2);\nconst counterB = signal(5);\nconst counterSum = computed(() =&gt; counterA() + counterB());\neffect(() =&gt; console.log(`Counters sum is ${counterSum()}`);<\/code><\/pre>\n\n\n\n<p>Warto zauwa\u017cy\u0107, \u017ce zar\u00f3wno <code>computed<\/code>(), jak i <code>effect()<\/code> nie wymagaj\u0105 podania tablicy zale\u017cno\u015bci. Funkcje te automatycznie \u015bledz\u0105 wywo\u0142ania innych sygna\u0142\u00f3w, a nast\u0119pnie subskrybuj\u0105 si\u0119 na zmiany. Mo\u017cemy nawet umie\u015bci\u0107 w nich instrukcje warunkowe i wszystko b\u0119dzie dzia\u0142a\u0107 jak nale\u017cy.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">const counterA = signal(2);\nconst counterB = signal(5);\nconst flag = signal(true);\n\n\/\/ As long as flag is set to true, changes to counterB will not trigger below computed\nconst counterSum = computed(() =&gt; flag() ? counterA() : counterB());<\/code><\/pre>\n\n\n\n<p>Sygna\u0142y mog\u0105 by\u0107 aktualizowane za pomoc\u0105 metod <code>set<\/code>(), <code>update<\/code>() i <code>mutate<\/code>().<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">const user = signal({id: 1, name: &#039;Tomek&#039;});\n\nuser.set({id: 2, name: &#039;Iza&#039;});\nuser.update(user =&gt; ({...user, name: &#039;Izabella&#039;});\nuser.mutate(user =&gt; user.name = &#039;Izabela&#039;);<\/code><\/pre>\n\n\n\n<p>Dwie ostatnie to funkcje pomocnicze, poniewa\u017c mo\u017cna je przepisa\u0107 za pomoc\u0105 <code>set()<\/code> w nast\u0119puj\u0105cy spos\u00f3b:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">\/\/ user.update(user =&gt; ({...user, name: &#039;Izabella&#039;});\nconst currentUser = user();\nuser.set({...currentUser, name: &#039;Izabella&#039;});\n\n\/\/user.mutate(user =&gt; user.name = &#039;Izabela&#039;);\nconst currentUser = user();\ncurrentUser.name = &#039;Izabela&#039;;\nuser.set(currentUser);<\/code><\/pre>\n\n\n\n<p>Kiedykolwiek warto\u015b\u0107 sygna\u0142u si\u0119 zmieni, wszystkie zale\u017cne sygna\u0142u zostan\u0105 oznaczone jako brudne, a wszyskie potrzebne kalkulacje zostan\u0105 wykonane przy nast\u0119pnej pr\u00f3bie odczytu. Je\u015bli nikt nie jest zainteresowany odczytem sygna\u0142\u00f3w, nie ma sensu marnowa\u0107 czasu procesora. U\u017cywaj\u0105c skomplikowanego \u017cargonu informatycznego &#8211; wszystkie sygna\u0142y s\u0105 leniwie.<\/p>\n\n\n\n<p>Jak sygna\u0142y wykrywaj\u0105, czy warto\u015b\u0107 rzeczywi\u015bcie si\u0119 zmieni\u0142a? Dla warto\u015bci prymitywnych u\u017cywany jest operator <code>===<\/code>. Dla warto\u015bci nieprymitywnych sprawdzanie r\u00f3wno\u015bci jest pomijane i sygna\u0142 zawsze zak\u0142ada, \u017ce warto\u015bci si\u0119 zmieni\u0142y. Dzi\u0119ki takiemu zachowaniu mo\u017cemy unikn\u0105\u0107 kosztownej operacji por\u00f3wnywania lub kopiowania obiekt\u00f3w.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">const counterA = signal(10);\neffect(() =&gt; console.log(counterA()));\n\n\/\/ After setting counter to 10 nothing will display in console\ncounterA.set(10);      \n\n\/\/ After setting counter to 15, it will display in console             \ncounterA.set(15);                       <\/code><\/pre>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">\/\/ Assume we have a large array full of complicated objects. \n\/\/ There is no efficient way to compare 2 arrays like this.\nconst largeTableOfObjects = signal([\/*...*\/]);\n\n\/\/ Although array reference have not changed\n\/\/ all signal dependencies will be re-evaluated.\n\/\/ With referencial equality below code wound not trigger any computations.\nlargeTableOfObjects.mutate(objects =&gt; objects.push({})); <\/code><\/pre>\n\n\n\n<p>Oczywi\u015bcie w prawdziwym \u017cyciu b\u0119dzie wiele sytuacji, w kt\u00f3rych por\u00f3wnanie dw\u00f3ch obiekt\u00f3w, bez wzgl\u0119du na to, jak skomplikowanych, b\u0119dzie bardziej wydajne ni\u017c ponowne obliczenie wszystkich zale\u017cno\u015bci sygna\u0142u. Aby rozwi\u0105za\u0107 ten problem, mo\u017cemy przekaza\u0107 funkcj\u0119 <code>equal<\/code> do metod <code>signal<\/code>() i <code>computed()<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">import { isEqual } from &#039;lodash&#039;\n\n\/\/ isEqual might be CPU intensive for large deeply nested objects. \n\/\/ For our simple case it works just fine.\nconst user = signal(\n  {id: 1, name: &#039;Tomek&#039;, age: 27},\n  {equal: (a, b) =&gt; isEqual(a, b)}\n);\n\n\/\/ We define computed signal that performs some heavy computations\nconst userDerived = computed(() =&gt; heavyComputations(user()))\n\n\/\/ Currently none of the below will trigger heavyComputations()\n\/\/ With default equality behaviour all 3 will trigger heavyComputations()\nuser.set({id: 1, name: &#039;Tomek&#039;, age: 27});\nuser.mutate(value =&gt; value.age = 27);\nuser.update(value =&gt; ({...value, age: 27}));<\/code><\/pre>\n\n\n\n<p>Wcze\u015bniej za\u0142o\u017cyli\u015bmy, \u017ce sygna\u0142y mo\u017cemy modyfikowa\u0107 za pomoc\u0105 metod <code>set<\/code>(), <code>update()<\/code> i <code>mutate()<\/code>. Nie jest to do ko\u0144ca prawda, poniewa\u017c sygna\u0142y udost\u0119pniaj\u0105 dwa r\u00f3\u017cne interfejsy: <code>Signal<\/code> i <code>WritableSignal<\/code>. Pierwszy z nich jest tylko do odczytu, a drugi zawiera wszystkie niezb\u0119dne metody mutacji. Nie ma wbudowanego sposobu na przekszta\u0142cenie <code>Signal<\/code> w <code>WritableSignal<\/code>. Tak zaprojektowane API daje mo\u017cliwo\u015b\u0107 wyra\u017anego stwierdzenia, czy chcesz emitowa\u0107 warto\u015bci, czy chcesz, aby Tw\u00f3j klient to robi\u0142.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">const writableSignal: WritableSignal&lt;Int&gt; = signal(0);\nconst readonlySignal: Signal&lt;Int&gt; = signal(0);\n\nconst sum: Signal&lt;Int&gt; = computed(\n  \/\/ You can read from both signals\n  () =&gt; readonlySignal() + writableSignal() \n); \n\n\/\/ This works perfectly fine.\nwritableSignal.set(10); \n\n\/\/ TypeScript compiler will throw en error here. \nreadonlySignal.set(10); \n\n\/\/ TypeScript compiler will throw en error here. \n\/\/ Even casting to any can&#039;t help you.\nsum.set(10); <\/code><\/pre>\n\n\n\n<p>M\u00f3wi\u0105c o przejrzystym przep\u0142ywie danych sygna\u0142y uniemo\u017cliwiaj\u0105 modyfikowanie innych sygna\u0142\u00f3w z poziomu metod <code>computed<\/code>() i <code>effect()<\/code>. Je\u015bl spr\u00f3bujesz zmodyfikowa\u0107 sygna\u0142 z ich poziomu, to rzucony zostanie b\u0142\u0105d. Po raz kolejny jest to bardzo dobry projekt API, poniewa\u017c w takich przypadkach nale\u017cy u\u017cy\u0107 metody <code>computed<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">const countA = signal(0);\nconst countB = signal(0);\nconst sumError = signal(countA() + countB());\n\n \/\/ This will thorw an error!\neffect(() =&gt; sumError.set(countA() + countB()));\n\n\/\/ This is the correct way to define sum of signals\nconst sumCorrect = computed(() =&gt; countA() + countB());<\/code><\/pre>\n\n\n\n<h2 id=\"integracja-angular-signals-z-angularem\" data-num=3>Integracja Angular Signals z Angularem<\/h2>\n\n\n\n<p>Aby zacz\u0105\u0107 u\u017cywa\u0107 sygna\u0142\u00f3w w swoich komponentach, wszystko, co musisz zrobi\u0107, to doda\u0107 <code>signals: true<\/code> do dekoratora <code>@Component<\/code>. Ta flaga modyfikuje strategi\u0119 wykrywania zmian dla komponentu i od tej pory b\u0119dzie on renderowany tylko wtedy, gdy zmieni si\u0119 jeden z sygna\u0142\u00f3w u\u017cywanych w komponencie.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">@Component({\n  signals: true,\n  selector: &#039;simple-counter&#039;,\n  template: `\n  &lt;p&gt;Count: {{ count() }}&lt;\/p&gt;\n  &lt;p&gt;Double Count: {{ doubleCount() }}&lt;\/p&gt;\n  &lt;button (click)=&quot;increment()&quot;&gt;Increment count&lt;\/button&gt;`,\n})\nexport class SimpleCounter {\n  count = signal(0); \n  doubleCount = computed(() =&gt; 2 * count());\n\n  increment() {\n    this.count.update(c =&gt; c + 1);\n  }\n}<\/code><\/pre>\n\n\n\n<p>Je\u015bli komponent jest oparty na sygna\u0142ach, to nie b\u0119dzie on wykorzystywa\u0142 do wykrywania zmian <code>zone.js<\/code>!  Przynajmniej dop\u00f3ki nie zaczniemy u\u017cywa\u0107 starych komponent\u00f3w w jego obr\u0119bie. W takich przypadkach Angular automatycznie podzieli aplikacj\u0119 na strefy i b\u0119dzie do nich aplikowa\u0142 odpowiedni\u0105 strategi\u0119 wykrywania zmian. Nasz komponent nadal b\u0119dzie renderowany tylko wtedy, gdy zmieni si\u0119 jeden z wewn\u0119trznych sygna\u0142\u00f3w, podczas gdy komponent dziecko b\u0119dzie u\u017cywa\u0142 starego dobrego wykrywania zmian opartego na <code>zone.js<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">@Component({\n  signals: true,\n  selector: &#039;simple-counter&#039;,\n  template: `\n  &lt;p&gt;Count {{ count() }}&lt;\/p&gt;\n  &lt;zone-component \/&gt;\n  &lt;button (click)=&quot;increment()&quot;&gt;Increment count&lt;\/button&gt;`,\n})\nexport class SimpleCounter {\n  count = signal(0);\n\n  increment() {\n    this.count.update(c =&gt; c + 1);\n  }\n}<\/code><\/pre>\n\n\n\n<p>Jednym z wa\u017cnych aspekt\u00f3w nowej strategii wykrywania zmian jest fakt, \u017ce nie mo\u017cna ju\u017c imperatywnie modyfikowa\u0107 w\u0142a\u015bciwo\u015bci komponent\u00f3w i oczekiwa\u0107 ponownego renderowania interfejsu. Nadal mo\u017cemy u\u017cywa\u0107 w\u0142a\u015bciwo\u015bci klas w swoim szablonie, ale b\u0119d\u0105 one od\u015bwie\u017cane tylko wtedy, gdy zmieni si\u0119 jeden z sygna\u0142\u00f3w. <\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">@Component({\n  signals: true,\n  selector: &#039;simple-counter&#039;,\n  template: `\n  &lt;p&gt;Signal Count {{ count() }}&lt;\/p&gt;\n  &lt;p&gt;Imperative Count {{ count() }}&lt;\/p&gt;\n  \n  &lt;!-- Clicking this button won&#039;t have any effect in the UI --&gt;\n  &lt;button (click)=&quot;incrementImperative()&quot;&gt;Increment Imperative Count&lt;\/button&gt;\n\n  &lt;!-- Clicking this button will update both counts in the UI --&gt;\n  &lt;button (click)=&quot;incrementSignal()&quot;&gt;Increment Signal Count&lt;\/button&gt;`\n})\nexport class SimpleCounter {\n  count = signal(0);\n  countImperative = 0;\n\n  incrementImperative(): void {\n    countImperative = countImperative + 1;\n  }\n\n  incrementSignal(): void {\n    this.count.update(c =&gt; c + 1);\n  }\n}<\/code><\/pre>\n\n\n\n<p>W komponentach opartych o sygna\u0142y zmieni\u0142 si\u0119 r\u00f3wnie\u017c spos\u00f3b definiowania zmiennych wej\u015bciowych i wyj\u015bciowych. Zamiast dekorowania <code>@Inptut()<\/code> i <code>@Output()<\/code> u\u017cy\u0107 nale\u017cy funkcji <code>input()<\/code> i <code>output()<\/code>. Warto zwr\u00f3\u0107 uwag\u0119 na typ zwracany przez funkcj\u0119 <code>input()<\/code>. Wyra\u017any podzia\u0142 na sygna\u0142y tylko do odczytu i do zapisu zmusza nas do zaprojektowania przejrzystego, jednokierunkowego przep\u0142ywu danych.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">@Component({\n  signals: true,\n  selector: &#039;simple-counter&#039;,\n  template: `\n  &lt;p&gt;Count {{ count() }}&lt;\/p&gt;\n  &lt;button (click)=&quot;onClick()&quot;&gt;Increment Count&lt;\/button&gt;`\n})\nexport class SimpleCounter {\n  count: Signal&lt;number&gt; = input&lt;number&gt;(0);\n  buttonClick: EventEmitter&lt;number&gt; = output&lt;void&gt;();\n\n  onClick(): void {\n    this.buttonClick.emit();\n  }\n}<\/code><\/pre>\n\n\n\n<p>Ostatni\u0105 wa\u017cn\u0105 r\u00f3\u017cnic\u0105 pomi\u0119dzy zwyk\u0142ymi komponentami, a komponentami opartymi na sygna\u0142ac jest nowy cykl \u017cycia komponentu. Poniewa\u017c wykrywanie zmian dzia\u0142a zupe\u0142nie inaczej, stare metody cyklu \u017cycia nie maj\u0105 zbytnio sensu. W nowym podej\u015bciu zamiast implementowa\u0107 interfejsy, rejestrujemy callbacki za pomoc\u0105 magicznych funkcji. <\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">@Component({\n  signals: true,\n  selector: &#039;user-profile&#039;,\n  template: `\n    &lt;h1&gt;Hello {{name()}}!&lt;\/h1&gt;`,\n})\nexport class LifecycleComponent {\n  name = signal(&#039;Tomek&#039;);\n\n  constructor() {\n    afterInit(() =&gt; {\n      \/\/ All inputs have their initial values.\n    });\n    \n    afterRender(() =&gt; {\n      \/\/ After the DOM of *all* components has been fully rendered.\n    });\n    \n    afterNextRender(() =&gt; {\n      \/\/ Same as afterRender, but only runs once.\n    });\n\n    afterRenderEffect(() =&gt; {\n      \/\/ Same as afterRender in terms of timing, \n      \/\/ but runs whenever the signals which it reads have changed.\n      console.log(`DOM was updated due to &#039;${this.name()}&#039;`);\n    });\n    \n    beforeDestroy(() =&gt; {\n      \/\/ This component instance is about to be destroyed.\n    });\n  }\n}<\/code><\/pre>\n\n\n\n<p>Podej\u015bcie magicznych funkcji daje nam kilka niesamowitych mo\u017cliwo\u015bci, takich jak rejestrowanie callback\u00f3w w odpowiedzi na klikni\u0119ciu przycisku.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">@Component({\n  signals: true,\n  selector: &#039;simple-counter&#039;,\n  template: `\n  &lt;p&gt;Count {{ count() }}&lt;\/p&gt;\n  &lt;button (click)=&quot;increment()&quot;&gt;Increment count&lt;\/button&gt;\n  &lt;button (click)=&quot;addCallbacks()&quot;&gt;Add callbacks&lt;\/button&gt;`,\n})\nexport class SimpleCounter {\n  count = signal(0); \/\/ WritableSignal&lt;number&gt;\n\n  increment() {\n    this.count.update(c =&gt; c + 1);\n  }\n\n  addCallbacks() {\n    afterRenderEffect(() =&gt; {\n      \/\/ I need to get notified the next time name changes!\n      console.log(`Count changed: &#039;${this.count()}&#039;`);\n    });\n\n    beforeDestroy(() =&gt; {\n      \/\/ Because I have initialized some stuff in my function\n      \/\/ Now I have to do some additional cleanup \n      unsubscribe();\n    });\n  }\n\n  private unsubscribe() {\n  \/*...*\/\n  }\n}<\/code><\/pre>\n\n\n\n<h2 id=\"angular-signals-i-wspolpraca-z-rxjs\" data-num=4>Angular Signals i wsp\u00f3\u0142praca z RxJS<\/h2>\n\n\n\n<p>Poniewa\u017c Signals i RxJS b\u0119d\u0105 d\u0142ugo wsp\u00f3\u0142istnie\u0107 w wielu aplikacjach, bardzo wa\u017cne jest posiadanie dobrych narz\u0119dzi do prze\u0142\u0105czania si\u0119 pomi\u0119dzy tymi abstrakcjami. Takie narz\u0119dzia b\u0119d\u0105 r\u00f3wnie\u017c mocno wykorzystywane przez sam zesp\u00f3\u0142 Angulara, poniewa\u017c zesp\u00f3\u0142 planuj\u0105 oni dostarcza\u0107 wi\u0119kszo\u015b\u0107 API zar\u00f3wno dla Signals jak i RxJS API. <\/p>\n\n\n\n<p><code>fromObservable()<\/code> to funkcja konweruj\u0105ca <code>Observable<\/code> na <code>Signal<\/code>. Drugim argumentem przekazywanym do funkcji jest warto\u015b\u0107 przechowywana wewn\u0105trz sygna\u0142u do momentu wyemitowania warto\u015bci przez strumie\u0144. Je\u015bli nie podamy warto\u015bci domy\u015blnej i b\u0119dziemy pr\u00f3bowali uzyska\u0107 dost\u0119p do sygna\u0142u przed emisj\u0105 pierwszego wydarzenia, zostanie wyrzucony b\u0142\u0105d. Warto r\u00f3wnie\u017c zauwa\u017cy\u0107, \u017ce <code>fromObservable()<\/code> natychmiast zasubskrybuje si\u0119 na warto\u015b\u0107, aby unikn\u0105\u0107 efekt\u00f3w ubocznych wywo\u0142anych przez odczytanie sygna\u0142u.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">const mySignal = fromObservable(myObservable$);<\/code><\/pre>\n\n\n\n<p><code>fromSignal<\/code> konwertuje strumie\u0144 na sygna\u0142. Konwersja w t\u0105 stron\u0119 jest znacznie prostsza, poniewa\u017c strumienie s\u0105 znacznie bardziej elastyczn\u0105 abstrakcj\u0105.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">const myObsrevable$ = fromObservable(mySignal);<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Zesp\u00f3\u0142 Angulara pracuje nad nowym reaktywnym prymitywem o nazwie Signals. Integracja z frameworkiem idzie bardzo g\u0142\u0119boko, poniewa\u017c przynosi now\u0105 strategi\u0119 wykrywania zmian, nowy cykl \u017cycia komponent\u00f3w i wiele wi\u0119cej.<\/p>\n","protected":false},"author":12,"featured_media":15602,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[273],"tags":[],"class_list":["post-15668","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-frontend-pl"],"acf":{"estimated_reading_time":"11","feature_image_visible":false,"weekly_summary":true,"push_notification_image":"https:\/\/vived.io\/wp-content\/uploads\/2023\/04\/Frontend-Weekly-1200x628_V2.png","feature_image_blog":{"ID":15591,"id":15591,"title":"grid_0","filename":"grid_0.png","filesize":1737669,"url":"https:\/\/vived.io\/wp-content\/uploads\/2023\/04\/grid_0.png","link":"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/grid_0-3\/","alt":"","author":"12","description":"","caption":"","name":"grid_0-3","status":"inherit","uploaded_to":15668,"date":"2023-04-05 12:10:13","modified":"2023-04-07 08:55:03","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/vived.io\/wp-includes\/images\/media\/default.png","width":1536,"height":1024,"sizes":{"thumbnail":"https:\/\/vived.io\/wp-content\/uploads\/2023\/04\/grid_0-150x150.png","thumbnail-width":150,"thumbnail-height":150,"medium":"https:\/\/vived.io\/wp-content\/uploads\/2023\/04\/grid_0-300x200.png","medium-width":300,"medium-height":200,"medium_large":"https:\/\/vived.io\/wp-content\/uploads\/2023\/04\/grid_0-768x512.png","medium_large-width":768,"medium_large-height":512,"large":"https:\/\/vived.io\/wp-content\/uploads\/2023\/04\/grid_0-1024x683.png","large-width":1024,"large-height":683,"1536x1536":"https:\/\/vived.io\/wp-content\/uploads\/2023\/04\/grid_0.png","1536x1536-width":1536,"1536x1536-height":1024,"2048x2048":"https:\/\/vived.io\/wp-content\/uploads\/2023\/04\/grid_0.png","2048x2048-width":1536,"2048x2048-height":1024,"gform-image-choice-sm":"https:\/\/vived.io\/wp-content\/uploads\/2023\/04\/grid_0.png","gform-image-choice-sm-width":300,"gform-image-choice-sm-height":200,"gform-image-choice-md":"https:\/\/vived.io\/wp-content\/uploads\/2023\/04\/grid_0.png","gform-image-choice-md-width":400,"gform-image-choice-md-height":267,"gform-image-choice-lg":"https:\/\/vived.io\/wp-content\/uploads\/2023\/04\/grid_0.png","gform-image-choice-lg-width":600,"gform-image-choice-lg-height":400}}},"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.0 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Angular Signals w 10 minut | Frontend Weekly vol. 132 - Vived<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/\" \/>\n<meta property=\"og:locale\" content=\"pl_PL\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Angular Signals w 10 minut | Frontend Weekly vol. 132 - Vived\" \/>\n<meta property=\"og:description\" content=\"Zesp\u00f3\u0142 Angulara pracuje nad nowym reaktywnym prymitywem o nazwie Signals. Integracja z frameworkiem idzie bardzo g\u0142\u0119boko, poniewa\u017c przynosi now\u0105 strategi\u0119 wykrywania zmian, nowy cykl \u017cycia komponent\u00f3w i wiele wi\u0119cej.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/\" \/>\n<meta property=\"og:site_name\" content=\"Vived\" \/>\n<meta property=\"article:published_time\" content=\"2023-04-05T12:21:32+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-04-07T10:20:54+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/vived.io\/wp-content\/uploads\/2023\/04\/Frontend-Weekly-1200x628_V2.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"628\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Tomasz Borowicz\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/\"},\"author\":{\"name\":\"Tomasz Borowicz\",\"@id\":\"https:\/\/vived.io\/pl\/#\/schema\/person\/9d2a72fe7d0dfbb4092675afbab742bb\"},\"headline\":\"Angular Signals w 10 minut | Frontend Weekly vol. 132\",\"datePublished\":\"2023-04-05T12:21:32+00:00\",\"dateModified\":\"2023-04-07T10:20:54+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/\"},\"wordCount\":1448,\"publisher\":{\"@id\":\"https:\/\/vived.io\/pl\/#organization\"},\"image\":{\"@id\":\"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/vived.io\/wp-content\/uploads\/2023\/04\/Frontend-Weekly-1200x628_V2.png\",\"articleSection\":[\"Frontend\"],\"inLanguage\":\"pl-PL\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/\",\"url\":\"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/\",\"name\":\"Angular Signals w 10 minut | Frontend Weekly vol. 132 - Vived\",\"isPartOf\":{\"@id\":\"https:\/\/vived.io\/pl\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/vived.io\/wp-content\/uploads\/2023\/04\/Frontend-Weekly-1200x628_V2.png\",\"datePublished\":\"2023-04-05T12:21:32+00:00\",\"dateModified\":\"2023-04-07T10:20:54+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/#breadcrumb\"},\"inLanguage\":\"pl-PL\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"pl-PL\",\"@id\":\"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/#primaryimage\",\"url\":\"https:\/\/vived.io\/wp-content\/uploads\/2023\/04\/Frontend-Weekly-1200x628_V2.png\",\"contentUrl\":\"https:\/\/vived.io\/wp-content\/uploads\/2023\/04\/Frontend-Weekly-1200x628_V2.png\",\"width\":1200,\"height\":628},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Strona g\u0142\u00f3wna\",\"item\":\"https:\/\/vived.io\/pl\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Angular Signals w 10 minut | Frontend Weekly vol. 132\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/vived.io\/pl\/#website\",\"url\":\"https:\/\/vived.io\/pl\/\",\"name\":\"Vived\",\"description\":\"platform empowering IT people and technology companies to synergic growth\",\"publisher\":{\"@id\":\"https:\/\/vived.io\/pl\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/vived.io\/pl\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"pl-PL\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/vived.io\/pl\/#organization\",\"name\":\"Vived\",\"url\":\"https:\/\/vived.io\/pl\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pl-PL\",\"@id\":\"https:\/\/vived.io\/pl\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/vived.io\/wp-content\/uploads\/2020\/03\/logo_vived_color.png\",\"contentUrl\":\"https:\/\/vived.io\/wp-content\/uploads\/2020\/03\/logo_vived_color.png\",\"width\":136,\"height\":45,\"caption\":\"Vived\"},\"image\":{\"@id\":\"https:\/\/vived.io\/pl\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/vived.io\/pl\/#\/schema\/person\/9d2a72fe7d0dfbb4092675afbab742bb\",\"name\":\"Tomasz Borowicz\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pl-PL\",\"@id\":\"https:\/\/vived.io\/pl\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/804536d2672538508d43f60ad2108e5aaea76c192653eaf95d4c3934b7d1dbb6?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/804536d2672538508d43f60ad2108e5aaea76c192653eaf95d4c3934b7d1dbb6?s=96&d=mm&r=g\",\"caption\":\"Tomasz Borowicz\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Angular Signals w 10 minut | Frontend Weekly vol. 132 - Vived","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/","og_locale":"pl_PL","og_type":"article","og_title":"Angular Signals w 10 minut | Frontend Weekly vol. 132 - Vived","og_description":"Zesp\u00f3\u0142 Angulara pracuje nad nowym reaktywnym prymitywem o nazwie Signals. Integracja z frameworkiem idzie bardzo g\u0142\u0119boko, poniewa\u017c przynosi now\u0105 strategi\u0119 wykrywania zmian, nowy cykl \u017cycia komponent\u00f3w i wiele wi\u0119cej.","og_url":"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/","og_site_name":"Vived","article_published_time":"2023-04-05T12:21:32+00:00","article_modified_time":"2023-04-07T10:20:54+00:00","og_image":[{"width":1200,"height":628,"url":"https:\/\/vived.io\/wp-content\/uploads\/2023\/04\/Frontend-Weekly-1200x628_V2.png","type":"image\/png"}],"author":"Tomasz Borowicz","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/#article","isPartOf":{"@id":"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/"},"author":{"name":"Tomasz Borowicz","@id":"https:\/\/vived.io\/pl\/#\/schema\/person\/9d2a72fe7d0dfbb4092675afbab742bb"},"headline":"Angular Signals w 10 minut | Frontend Weekly vol. 132","datePublished":"2023-04-05T12:21:32+00:00","dateModified":"2023-04-07T10:20:54+00:00","mainEntityOfPage":{"@id":"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/"},"wordCount":1448,"publisher":{"@id":"https:\/\/vived.io\/pl\/#organization"},"image":{"@id":"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/#primaryimage"},"thumbnailUrl":"https:\/\/vived.io\/wp-content\/uploads\/2023\/04\/Frontend-Weekly-1200x628_V2.png","articleSection":["Frontend"],"inLanguage":"pl-PL"},{"@type":"WebPage","@id":"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/","url":"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/","name":"Angular Signals w 10 minut | Frontend Weekly vol. 132 - Vived","isPartOf":{"@id":"https:\/\/vived.io\/pl\/#website"},"primaryImageOfPage":{"@id":"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/#primaryimage"},"image":{"@id":"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/#primaryimage"},"thumbnailUrl":"https:\/\/vived.io\/wp-content\/uploads\/2023\/04\/Frontend-Weekly-1200x628_V2.png","datePublished":"2023-04-05T12:21:32+00:00","dateModified":"2023-04-07T10:20:54+00:00","breadcrumb":{"@id":"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/#breadcrumb"},"inLanguage":"pl-PL","potentialAction":[{"@type":"ReadAction","target":["https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/"]}]},{"@type":"ImageObject","inLanguage":"pl-PL","@id":"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/#primaryimage","url":"https:\/\/vived.io\/wp-content\/uploads\/2023\/04\/Frontend-Weekly-1200x628_V2.png","contentUrl":"https:\/\/vived.io\/wp-content\/uploads\/2023\/04\/Frontend-Weekly-1200x628_V2.png","width":1200,"height":628},{"@type":"BreadcrumbList","@id":"https:\/\/vived.io\/pl\/angular-signals-w-10-minut-frontend-weekly-vol-132\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Strona g\u0142\u00f3wna","item":"https:\/\/vived.io\/pl\/"},{"@type":"ListItem","position":2,"name":"Angular Signals w 10 minut | Frontend Weekly vol. 132"}]},{"@type":"WebSite","@id":"https:\/\/vived.io\/pl\/#website","url":"https:\/\/vived.io\/pl\/","name":"Vived","description":"platform empowering IT people and technology companies to synergic growth","publisher":{"@id":"https:\/\/vived.io\/pl\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/vived.io\/pl\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"pl-PL"},{"@type":"Organization","@id":"https:\/\/vived.io\/pl\/#organization","name":"Vived","url":"https:\/\/vived.io\/pl\/","logo":{"@type":"ImageObject","inLanguage":"pl-PL","@id":"https:\/\/vived.io\/pl\/#\/schema\/logo\/image\/","url":"https:\/\/vived.io\/wp-content\/uploads\/2020\/03\/logo_vived_color.png","contentUrl":"https:\/\/vived.io\/wp-content\/uploads\/2020\/03\/logo_vived_color.png","width":136,"height":45,"caption":"Vived"},"image":{"@id":"https:\/\/vived.io\/pl\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/vived.io\/pl\/#\/schema\/person\/9d2a72fe7d0dfbb4092675afbab742bb","name":"Tomasz Borowicz","image":{"@type":"ImageObject","inLanguage":"pl-PL","@id":"https:\/\/vived.io\/pl\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/804536d2672538508d43f60ad2108e5aaea76c192653eaf95d4c3934b7d1dbb6?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/804536d2672538508d43f60ad2108e5aaea76c192653eaf95d4c3934b7d1dbb6?s=96&d=mm&r=g","caption":"Tomasz Borowicz"}}]}},"blocks_vived":[{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p>Wydaje si\u0119, \u017ce spo\u0142eczno\u015b\u0107 Angulara m\u00f3wi tylko o jednym - Signals. Nowo opublikowane <a href=\"https:\/\/github.com\/angular\/angular\/discussions\/49685\">RFC<\/a> (Request for Comments) jest naprawd\u0119 ogromne i dlatego postanowili\u015bmy go troch\u0119 podsumowa\u0107. Je\u015bli chcesz dog\u0142\u0119bnie zrozumie\u0107 Signals i pozna\u0107 wszystkie decyzje projektowe z nimi zwi\u0105zane - oryginalne <a href=\"https:\/\/github.com\/angular\/angular\/discussions\/49685\">RFC<\/a> wci\u0105\u017c pozostaje najlepszym miejscem \u017ceby to zrobi\u0107. Je\u015bli jednak nie masz czasu przebija\u0107 si\u0119 przez 5 rozleg\u0142ych dokument\u00f3w, w tym artykule znajdziesz wszystkie najwa\u017cniejsze informacje, a jego przeczytanie nie zajmie Ci wi\u0119cej ni\u017c 10 minut.<\/p>\n","innerContent":["\n<p>Wydaje si\u0119, \u017ce spo\u0142eczno\u015b\u0107 Angulara m\u00f3wi tylko o jednym - Signals. Nowo opublikowane <a href=\"https:\/\/github.com\/angular\/angular\/discussions\/49685\">RFC<\/a> (Request for Comments) jest naprawd\u0119 ogromne i dlatego postanowili\u015bmy go troch\u0119 podsumowa\u0107. Je\u015bli chcesz dog\u0142\u0119bnie zrozumie\u0107 Signals i pozna\u0107 wszystkie decyzje projektowe z nimi zwi\u0105zane - oryginalne <a href=\"https:\/\/github.com\/angular\/angular\/discussions\/49685\">RFC<\/a> wci\u0105\u017c pozostaje najlepszym miejscem \u017ceby to zrobi\u0107. Je\u015bli jednak nie masz czasu przebija\u0107 si\u0119 przez 5 rozleg\u0142ych dokument\u00f3w, w tym artykule znajdziesz wszystkie najwa\u017cniejsze informacje, a jego przeczytanie nie zajmie Ci wi\u0119cej ni\u017c 10 minut.<\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p>Angular Signals s\u0105 ju\u017c dost\u0119pne jako eksperymentalna funkcjonalno\u015b\u0107 w Angular 16 RC (kilku funkcjonalno\u015bci opisanych w tym artykule wci\u0105\u017c jednak brakuje). Nie wiemy kiedy Signalns stan\u0105 si\u0119 stabiln\u0105 funkcjonalno\u015bci\u0105. Zaleca\u0142bym uzbrojenie si\u0119 w cierpliwo\u015b\u0107, poniewa\u017c w RFC jest jeszcze kilka gor\u0105cych punkt\u00f3w dyskusyjnych.<\/p>\n","innerContent":["\n<p>Angular Signals s\u0105 ju\u017c dost\u0119pne jako eksperymentalna funkcjonalno\u015b\u0107 w Angular 16 RC (kilku funkcjonalno\u015bci opisanych w tym artykule wci\u0105\u017c jednak brakuje). Nie wiemy kiedy Signalns stan\u0105 si\u0119 stabiln\u0105 funkcjonalno\u015bci\u0105. Zaleca\u0142bym uzbrojenie si\u0119 w cierpliwo\u015b\u0107, poniewa\u017c w RFC jest jeszcze kilka gor\u0105cych punkt\u00f3w dyskusyjnych.<\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/heading","attrs":[],"innerBlocks":[],"innerHTML":"\n<h2>Dlaczego potrzebujemy Angular Signals? <\/h2>\n","innerContent":["\n<h2>Dlaczego potrzebujemy Angular Signals? <\/h2>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p>Zasadniczo istniej\u0105 dwa g\u0142\u00f3wne powody: Po pierwsze, <code>zone.js<\/code> jest najwi\u0119ksz\u0105 bol\u0105czk\u0105 programist\u00f3w korzystaj\u0105cych na codzie\u0144 z Angulara. Po drugie, RxJS nie jest najlepsz\u0105 abstrakcj\u0105 do zarz\u0105dzania stanem.  Teraz przyjrzymy si\u0119 obu powodom w szczeg\u00f3\u0142ach.<\/p>\n","innerContent":["\n<p>Zasadniczo istniej\u0105 dwa g\u0142\u00f3wne powody: Po pierwsze, <code>zone.js<\/code> jest najwi\u0119ksz\u0105 bol\u0105czk\u0105 programist\u00f3w korzystaj\u0105cych na codzie\u0144 z Angulara. Po drugie, RxJS nie jest najlepsz\u0105 abstrakcj\u0105 do zarz\u0105dzania stanem.  Teraz przyjrzymy si\u0119 obu powodom w szczeg\u00f3\u0142ach.<\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p><code>zone.js<\/code> to biblioteka robi\u0105ca monkey-patching* wi\u0119kszo\u015bci asynchronicznych metod przegl\u0105darki, w celu powiadomiania Angulara o mo\u017cliwych zmianach w aplikacji. Kiedy dowolne asynchroniczne API przegl\u0105darki zostaje wywo\u0142ane Angular uruchamia proces Change Detection, aby wykry\u0107, czy co\u015b zmieni\u0142o si\u0119 w aplikacji. Jak mo\u017cna sobie wyobrazi\u0107, to zachowanie prowadzi do wielu nadmiarowych uruchomie\u0144 procesu Change Detection. W rezultacie mechanizm ten jest trudny zar\u00f3wno do optymalizacji jak i debugowania. <a href=\"https:\/\/indepth.dev\/posts\/1515\/deep-dive-into-the-onpush-change-detection-strategy-in-angular\" target=\"_blank\" rel=\"noreferrer noopener\"><code>ChangeDetectionStrategy.OnPush<\/code> <\/a>czyni sytuacj\u0119 nieco lepsz\u0105, ale g\u0142\u00f3wne problemy pozostaj\u0105 takie same. Aplikacje pozbawione <code>zone.js<\/code> by\u0142y niespe\u0142nionym marzeniem deweloper\u00f3w Angulara od lat. Projekty takie jak <a href=\"https:\/\/www.rx-angular.io\/\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">RxAngular<\/a> odzwierciedlaj\u0105 podej\u015bcie spo\u0142eczno\u015bci do rozwi\u0105zania problemu.<\/p>\n","innerContent":["\n<p><code>zone.js<\/code> to biblioteka robi\u0105ca monkey-patching* wi\u0119kszo\u015bci asynchronicznych metod przegl\u0105darki, w celu powiadomiania Angulara o mo\u017cliwych zmianach w aplikacji. Kiedy dowolne asynchroniczne API przegl\u0105darki zostaje wywo\u0142ane Angular uruchamia proces Change Detection, aby wykry\u0107, czy co\u015b zmieni\u0142o si\u0119 w aplikacji. Jak mo\u017cna sobie wyobrazi\u0107, to zachowanie prowadzi do wielu nadmiarowych uruchomie\u0144 procesu Change Detection. W rezultacie mechanizm ten jest trudny zar\u00f3wno do optymalizacji jak i debugowania. <a href=\"https:\/\/indepth.dev\/posts\/1515\/deep-dive-into-the-onpush-change-detection-strategy-in-angular\" target=\"_blank\" rel=\"noreferrer noopener\"><code>ChangeDetectionStrategy.OnPush<\/code> <\/a>czyni sytuacj\u0119 nieco lepsz\u0105, ale g\u0142\u00f3wne problemy pozostaj\u0105 takie same. Aplikacje pozbawione <code>zone.js<\/code> by\u0142y niespe\u0142nionym marzeniem deweloper\u00f3w Angulara od lat. Projekty takie jak <a href=\"https:\/\/www.rx-angular.io\/\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">RxAngular<\/a> odzwierciedlaj\u0105 podej\u015bcie spo\u0142eczno\u015bci do rozwi\u0105zania problemu.<\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p>RxJS sprawdza si\u0119 \u015bwietnie, gdy potrzebujemy modelowa\u0107 z\u0142o\u017cone zdarzenia dziej\u0105ce si\u0119 na przestrzeni czasu, takie jak limitowanie zapyta\u0144 do backendu czy agregowanie klikni\u0119\u0107 myszk\u0105. Je\u015bli chodzi o zarz\u0105dzanie stanem, RxJS nie sprawdza si\u0119 ju\u017c tak dobrze. Zacznijmy od tego, \u017ce strumie\u0144 nie musi przechowywa\u0107 warto\u015bci i jest to wpisane w architektur\u0119 strumieni. Nawet je\u015bli u\u017cywamy <code>BehaviurSubject<\/code> i <code>shareReplay(1)<\/code> wsz\u0119dzie gdzie to mo\u017cliwe, nasz kod szybko zalany zostje nullami i niebezpiecznym rzutowaniem typ\u00f3w. Strumienie naturalnie grawituj\u0105 w kierunku zimnych strumieni, co prowadzi do wielu zb\u0119dnych kalkulacji i zaskakuj\u0105cych efekt\u00f3w ubocznych. RxJS wprowadza r\u00f3wnie\u017c wiele narzutu dla pocz\u0105tkuj\u0105cych - na przyk\u0142ad czym jest <code>Subject<\/code> i <code>BehaviourSubject<\/code>, czym s\u0105 zimne i gor\u0105ce strumienie, dlaczego istnieje wiele operator\u00f3w map i kiedy dok\u0142adnie wykonywany jest napisany kod. <\/p>\n","innerContent":["\n<p>RxJS sprawdza si\u0119 \u015bwietnie, gdy potrzebujemy modelowa\u0107 z\u0142o\u017cone zdarzenia dziej\u0105ce si\u0119 na przestrzeni czasu, takie jak limitowanie zapyta\u0144 do backendu czy agregowanie klikni\u0119\u0107 myszk\u0105. Je\u015bli chodzi o zarz\u0105dzanie stanem, RxJS nie sprawdza si\u0119 ju\u017c tak dobrze. Zacznijmy od tego, \u017ce strumie\u0144 nie musi przechowywa\u0107 warto\u015bci i jest to wpisane w architektur\u0119 strumieni. Nawet je\u015bli u\u017cywamy <code>BehaviurSubject<\/code> i <code>shareReplay(1)<\/code> wsz\u0119dzie gdzie to mo\u017cliwe, nasz kod szybko zalany zostje nullami i niebezpiecznym rzutowaniem typ\u00f3w. Strumienie naturalnie grawituj\u0105 w kierunku zimnych strumieni, co prowadzi do wielu zb\u0119dnych kalkulacji i zaskakuj\u0105cych efekt\u00f3w ubocznych. RxJS wprowadza r\u00f3wnie\u017c wiele narzutu dla pocz\u0105tkuj\u0105cych - na przyk\u0142ad czym jest <code>Subject<\/code> i <code>BehaviourSubject<\/code>, czym s\u0105 zimne i gor\u0105ce strumienie, dlaczego istnieje wiele operator\u00f3w map i kiedy dok\u0142adnie wykonywany jest napisany kod. <\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p>Signalns maj\u0105 na celu rozwi\u0105zanie wi\u0119kszo\u015bci problem\u00f3w wymienionych w powy\u017cszych akapitach. Warto jednak pami\u0119ta\u0107, \u017ce ani <code>zone.js<\/code>, ani RxJS nigdzie si\u0119 w najbli\u017cszym czasie nie wybieraj\u0105. Fakt, \u017ce pojawi si\u0119 alternatywny spos\u00f3b na rozwi\u0105zaywanie problem\u00f3w nie oznacza, \u017ce stary automatycznie staje si\u0119 deprecated.<\/p>\n","innerContent":["\n<p>Signalns maj\u0105 na celu rozwi\u0105zanie wi\u0119kszo\u015bci problem\u00f3w wymienionych w powy\u017cszych akapitach. Warto jednak pami\u0119ta\u0107, \u017ce ani <code>zone.js<\/code>, ani RxJS nigdzie si\u0119 w najbli\u017cszym czasie nie wybieraj\u0105. Fakt, \u017ce pojawi si\u0119 alternatywny spos\u00f3b na rozwi\u0105zaywanie problem\u00f3w nie oznacza, \u017ce stary automatycznie staje si\u0119 deprecated.<\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p>*moneky-patching - technika u\u017cywana do dynamicznego aktualizowania zachowania kodu w czasie rzeczywistym. monkey-patching jest sposobem na rozszerzenie lub modyfikacj\u0119 kodu runtime j\u0119zyk\u00f3w dynamicznych bez zmiany oryginalnego kodu \u017ar\u00f3d\u0142owego.<\/p>\n","innerContent":["\n<p>*moneky-patching - technika u\u017cywana do dynamicznego aktualizowania zachowania kodu w czasie rzeczywistym. monkey-patching jest sposobem na rozszerzenie lub modyfikacj\u0119 kodu runtime j\u0119zyk\u00f3w dynamicznych bez zmiany oryginalnego kodu \u017ar\u00f3d\u0142owego.<\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/heading","attrs":[],"innerBlocks":[],"innerHTML":"\n<h2>Angular Signals API<\/h2>\n","innerContent":["\n<h2>Angular Signals API<\/h2>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p>Cytuj\u0105c oryginalne RFC: Sygna\u0142 jest opakowaniem wok\u00f3\u0142 warto\u015bci, kt\u00f3re jest w stanie powiadomi\u0107 zainteresowanych konsument\u00f3w, gdy ta warto\u015b\u0107 si\u0119 zmienia. Sygna\u0142y mog\u0105 by\u0107 tworzone za pomoc\u0105 factory method <code>signal()<\/code> przyjmuj\u0105c\u0105 warto\u015b\u0107 pocz\u0105tkow\u0105 jako parametr. Sygna\u0142y mog\u0105 by\u0107 komponowane za pomoc\u0105 metody <code>computed<\/code>(). Mo\u017cliwe jest r\u00f3wnie\u017c wywo\u0142ywanie efekt\u00f3w ubocznych, gdy warto\u015b\u0107 sygna\u0142u si\u0119 zmienia, rejestruj\u0105c odpowiedni callback w metodzie <code>effect()<\/code>. W dowolnym momencie mo\u017cemy odczyta\u0107 warto\u015b\u0107 sygna\u0142u wywo\u0142uj\u0105c go jak funkcj\u0119.<\/p>\n","innerContent":["\n<p>Cytuj\u0105c oryginalne RFC: Sygna\u0142 jest opakowaniem wok\u00f3\u0142 warto\u015bci, kt\u00f3re jest w stanie powiadomi\u0107 zainteresowanych konsument\u00f3w, gdy ta warto\u015b\u0107 si\u0119 zmienia. Sygna\u0142y mog\u0105 by\u0107 tworzone za pomoc\u0105 factory method <code>signal()<\/code> przyjmuj\u0105c\u0105 warto\u015b\u0107 pocz\u0105tkow\u0105 jako parametr. Sygna\u0142y mog\u0105 by\u0107 komponowane za pomoc\u0105 metody <code>computed<\/code>(). Mo\u017cliwe jest r\u00f3wnie\u017c wywo\u0142ywanie efekt\u00f3w ubocznych, gdy warto\u015b\u0107 sygna\u0142u si\u0119 zmienia, rejestruj\u0105c odpowiedni callback w metodzie <code>effect()<\/code>. W dowolnym momencie mo\u017cemy odczyta\u0107 warto\u015b\u0107 sygna\u0142u wywo\u0142uj\u0105c go jak funkcj\u0119.<\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"prismatic\/blocks","attrs":{"language":"typescript"},"innerBlocks":[],"innerHTML":"\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">const counterA = signal(2);\nconst counterB = signal(5);\nconst counterSum = computed(() => counterA() + counterB());\neffect(() => console.log(`Counters sum is ${counterSum()}`);<\/code><\/pre>\n","innerContent":["\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">const counterA = signal(2);\nconst counterB = signal(5);\nconst counterSum = computed(() => counterA() + counterB());\neffect(() => console.log(`Counters sum is ${counterSum()}`);<\/code><\/pre>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p>Warto zauwa\u017cy\u0107, \u017ce zar\u00f3wno <code>computed<\/code>(), jak i <code>effect()<\/code> nie wymagaj\u0105 podania tablicy zale\u017cno\u015bci. Funkcje te automatycznie \u015bledz\u0105 wywo\u0142ania innych sygna\u0142\u00f3w, a nast\u0119pnie subskrybuj\u0105 si\u0119 na zmiany. Mo\u017cemy nawet umie\u015bci\u0107 w nich instrukcje warunkowe i wszystko b\u0119dzie dzia\u0142a\u0107 jak nale\u017cy.<\/p>\n","innerContent":["\n<p>Warto zauwa\u017cy\u0107, \u017ce zar\u00f3wno <code>computed<\/code>(), jak i <code>effect()<\/code> nie wymagaj\u0105 podania tablicy zale\u017cno\u015bci. Funkcje te automatycznie \u015bledz\u0105 wywo\u0142ania innych sygna\u0142\u00f3w, a nast\u0119pnie subskrybuj\u0105 si\u0119 na zmiany. Mo\u017cemy nawet umie\u015bci\u0107 w nich instrukcje warunkowe i wszystko b\u0119dzie dzia\u0142a\u0107 jak nale\u017cy.<\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"prismatic\/blocks","attrs":{"language":"typescript"},"innerBlocks":[],"innerHTML":"\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">const counterA = signal(2);\nconst counterB = signal(5);\nconst flag = signal(true);\n\n\/\/ As long as flag is set to true, changes to counterB will not trigger below computed\nconst counterSum = computed(() => flag() ? counterA() : counterB());<\/code><\/pre>\n","innerContent":["\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">const counterA = signal(2);\nconst counterB = signal(5);\nconst flag = signal(true);\n\n\/\/ As long as flag is set to true, changes to counterB will not trigger below computed\nconst counterSum = computed(() => flag() ? counterA() : counterB());<\/code><\/pre>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p>Sygna\u0142y mog\u0105 by\u0107 aktualizowane za pomoc\u0105 metod <code>set<\/code>(), <code>update<\/code>() i <code>mutate<\/code>().<\/p>\n","innerContent":["\n<p>Sygna\u0142y mog\u0105 by\u0107 aktualizowane za pomoc\u0105 metod <code>set<\/code>(), <code>update<\/code>() i <code>mutate<\/code>().<\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"prismatic\/blocks","attrs":{"language":"typescript"},"innerBlocks":[],"innerHTML":"\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">const user = signal({id: 1, name: 'Tomek'});\n\nuser.set({id: 2, name: 'Iza'});\nuser.update(user => ({...user, name: 'Izabella'});\nuser.mutate(user => user.name = 'Izabela');<\/code><\/pre>\n","innerContent":["\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">const user = signal({id: 1, name: 'Tomek'});\n\nuser.set({id: 2, name: 'Iza'});\nuser.update(user => ({...user, name: 'Izabella'});\nuser.mutate(user => user.name = 'Izabela');<\/code><\/pre>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p>Dwie ostatnie to funkcje pomocnicze, poniewa\u017c mo\u017cna je przepisa\u0107 za pomoc\u0105 <code>set()<\/code> w nast\u0119puj\u0105cy spos\u00f3b:<\/p>\n","innerContent":["\n<p>Dwie ostatnie to funkcje pomocnicze, poniewa\u017c mo\u017cna je przepisa\u0107 za pomoc\u0105 <code>set()<\/code> w nast\u0119puj\u0105cy spos\u00f3b:<\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"prismatic\/blocks","attrs":{"language":"typescript"},"innerBlocks":[],"innerHTML":"\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">\/\/ user.update(user => ({...user, name: 'Izabella'});\nconst currentUser = user();\nuser.set({...currentUser, name: 'Izabella'});\n\n\/\/user.mutate(user => user.name = 'Izabela');\nconst currentUser = user();\ncurrentUser.name = 'Izabela';\nuser.set(currentUser);<\/code><\/pre>\n","innerContent":["\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">\/\/ user.update(user => ({...user, name: 'Izabella'});\nconst currentUser = user();\nuser.set({...currentUser, name: 'Izabella'});\n\n\/\/user.mutate(user => user.name = 'Izabela');\nconst currentUser = user();\ncurrentUser.name = 'Izabela';\nuser.set(currentUser);<\/code><\/pre>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p>Kiedykolwiek warto\u015b\u0107 sygna\u0142u si\u0119 zmieni, wszystkie zale\u017cne sygna\u0142u zostan\u0105 oznaczone jako brudne, a wszyskie potrzebne kalkulacje zostan\u0105 wykonane przy nast\u0119pnej pr\u00f3bie odczytu. Je\u015bli nikt nie jest zainteresowany odczytem sygna\u0142\u00f3w, nie ma sensu marnowa\u0107 czasu procesora. U\u017cywaj\u0105c skomplikowanego \u017cargonu informatycznego - wszystkie sygna\u0142y s\u0105 leniwie.<\/p>\n","innerContent":["\n<p>Kiedykolwiek warto\u015b\u0107 sygna\u0142u si\u0119 zmieni, wszystkie zale\u017cne sygna\u0142u zostan\u0105 oznaczone jako brudne, a wszyskie potrzebne kalkulacje zostan\u0105 wykonane przy nast\u0119pnej pr\u00f3bie odczytu. Je\u015bli nikt nie jest zainteresowany odczytem sygna\u0142\u00f3w, nie ma sensu marnowa\u0107 czasu procesora. U\u017cywaj\u0105c skomplikowanego \u017cargonu informatycznego - wszystkie sygna\u0142y s\u0105 leniwie.<\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p>Jak sygna\u0142y wykrywaj\u0105, czy warto\u015b\u0107 rzeczywi\u015bcie si\u0119 zmieni\u0142a? Dla warto\u015bci prymitywnych u\u017cywany jest operator <code>===<\/code>. Dla warto\u015bci nieprymitywnych sprawdzanie r\u00f3wno\u015bci jest pomijane i sygna\u0142 zawsze zak\u0142ada, \u017ce warto\u015bci si\u0119 zmieni\u0142y. Dzi\u0119ki takiemu zachowaniu mo\u017cemy unikn\u0105\u0107 kosztownej operacji por\u00f3wnywania lub kopiowania obiekt\u00f3w.<\/p>\n","innerContent":["\n<p>Jak sygna\u0142y wykrywaj\u0105, czy warto\u015b\u0107 rzeczywi\u015bcie si\u0119 zmieni\u0142a? Dla warto\u015bci prymitywnych u\u017cywany jest operator <code>===<\/code>. Dla warto\u015bci nieprymitywnych sprawdzanie r\u00f3wno\u015bci jest pomijane i sygna\u0142 zawsze zak\u0142ada, \u017ce warto\u015bci si\u0119 zmieni\u0142y. Dzi\u0119ki takiemu zachowaniu mo\u017cemy unikn\u0105\u0107 kosztownej operacji por\u00f3wnywania lub kopiowania obiekt\u00f3w.<\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"prismatic\/blocks","attrs":{"language":"typescript"},"innerBlocks":[],"innerHTML":"\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">const counterA = signal(10);\neffect(() => console.log(counterA()));\n\n\/\/ After setting counter to 10 nothing will display in console\ncounterA.set(10);      \n\n\/\/ After setting counter to 15, it will display in console             \ncounterA.set(15);                       <\/code><\/pre>\n","innerContent":["\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">const counterA = signal(10);\neffect(() => console.log(counterA()));\n\n\/\/ After setting counter to 10 nothing will display in console\ncounterA.set(10);      \n\n\/\/ After setting counter to 15, it will display in console             \ncounterA.set(15);                       <\/code><\/pre>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"prismatic\/blocks","attrs":{"language":"typescript"},"innerBlocks":[],"innerHTML":"\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">\/\/ Assume we have a large array full of complicated objects. \n\/\/ There is no efficient way to compare 2 arrays like this.\nconst largeTableOfObjects = signal([\/*...*\/]);\n\n\/\/ Although array reference have not changed\n\/\/ all signal dependencies will be re-evaluated.\n\/\/ With referencial equality below code wound not trigger any computations.\nlargeTableOfObjects.mutate(objects => objects.push({})); <\/code><\/pre>\n","innerContent":["\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">\/\/ Assume we have a large array full of complicated objects. \n\/\/ There is no efficient way to compare 2 arrays like this.\nconst largeTableOfObjects = signal([\/*...*\/]);\n\n\/\/ Although array reference have not changed\n\/\/ all signal dependencies will be re-evaluated.\n\/\/ With referencial equality below code wound not trigger any computations.\nlargeTableOfObjects.mutate(objects => objects.push({})); <\/code><\/pre>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p>Oczywi\u015bcie w prawdziwym \u017cyciu b\u0119dzie wiele sytuacji, w kt\u00f3rych por\u00f3wnanie dw\u00f3ch obiekt\u00f3w, bez wzgl\u0119du na to, jak skomplikowanych, b\u0119dzie bardziej wydajne ni\u017c ponowne obliczenie wszystkich zale\u017cno\u015bci sygna\u0142u. Aby rozwi\u0105za\u0107 ten problem, mo\u017cemy przekaza\u0107 funkcj\u0119 <code>equal<\/code> do metod <code>signal<\/code>() i <code>computed()<\/code>.<\/p>\n","innerContent":["\n<p>Oczywi\u015bcie w prawdziwym \u017cyciu b\u0119dzie wiele sytuacji, w kt\u00f3rych por\u00f3wnanie dw\u00f3ch obiekt\u00f3w, bez wzgl\u0119du na to, jak skomplikowanych, b\u0119dzie bardziej wydajne ni\u017c ponowne obliczenie wszystkich zale\u017cno\u015bci sygna\u0142u. Aby rozwi\u0105za\u0107 ten problem, mo\u017cemy przekaza\u0107 funkcj\u0119 <code>equal<\/code> do metod <code>signal<\/code>() i <code>computed()<\/code>.<\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"prismatic\/blocks","attrs":{"language":"typescript"},"innerBlocks":[],"innerHTML":"\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">import { isEqual } from 'lodash'\n\n\/\/ isEqual might be CPU intensive for large deeply nested objects. \n\/\/ For our simple case it works just fine.\nconst user = signal(\n  {id: 1, name: 'Tomek', age: 27},\n  {equal: (a, b) => isEqual(a, b)}\n);\n\n\/\/ We define computed signal that performs some heavy computations\nconst userDerived = computed(() => heavyComputations(user()))\n\n\/\/ Currently none of the below will trigger heavyComputations()\n\/\/ With default equality behaviour all 3 will trigger heavyComputations()\nuser.set({id: 1, name: 'Tomek', age: 27});\nuser.mutate(value => value.age = 27);\nuser.update(value => ({...value, age: 27}));<\/code><\/pre>\n","innerContent":["\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">import { isEqual } from 'lodash'\n\n\/\/ isEqual might be CPU intensive for large deeply nested objects. \n\/\/ For our simple case it works just fine.\nconst user = signal(\n  {id: 1, name: 'Tomek', age: 27},\n  {equal: (a, b) => isEqual(a, b)}\n);\n\n\/\/ We define computed signal that performs some heavy computations\nconst userDerived = computed(() => heavyComputations(user()))\n\n\/\/ Currently none of the below will trigger heavyComputations()\n\/\/ With default equality behaviour all 3 will trigger heavyComputations()\nuser.set({id: 1, name: 'Tomek', age: 27});\nuser.mutate(value => value.age = 27);\nuser.update(value => ({...value, age: 27}));<\/code><\/pre>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p>Wcze\u015bniej za\u0142o\u017cyli\u015bmy, \u017ce sygna\u0142y mo\u017cemy modyfikowa\u0107 za pomoc\u0105 metod <code>set<\/code>(), <code>update()<\/code> i <code>mutate()<\/code>. Nie jest to do ko\u0144ca prawda, poniewa\u017c sygna\u0142y udost\u0119pniaj\u0105 dwa r\u00f3\u017cne interfejsy: <code>Signal<\/code> i <code>WritableSignal<\/code>. Pierwszy z nich jest tylko do odczytu, a drugi zawiera wszystkie niezb\u0119dne metody mutacji. Nie ma wbudowanego sposobu na przekszta\u0142cenie <code>Signal<\/code> w <code>WritableSignal<\/code>. Tak zaprojektowane API daje mo\u017cliwo\u015b\u0107 wyra\u017anego stwierdzenia, czy chcesz emitowa\u0107 warto\u015bci, czy chcesz, aby Tw\u00f3j klient to robi\u0142.<\/p>\n","innerContent":["\n<p>Wcze\u015bniej za\u0142o\u017cyli\u015bmy, \u017ce sygna\u0142y mo\u017cemy modyfikowa\u0107 za pomoc\u0105 metod <code>set<\/code>(), <code>update()<\/code> i <code>mutate()<\/code>. Nie jest to do ko\u0144ca prawda, poniewa\u017c sygna\u0142y udost\u0119pniaj\u0105 dwa r\u00f3\u017cne interfejsy: <code>Signal<\/code> i <code>WritableSignal<\/code>. Pierwszy z nich jest tylko do odczytu, a drugi zawiera wszystkie niezb\u0119dne metody mutacji. Nie ma wbudowanego sposobu na przekszta\u0142cenie <code>Signal<\/code> w <code>WritableSignal<\/code>. Tak zaprojektowane API daje mo\u017cliwo\u015b\u0107 wyra\u017anego stwierdzenia, czy chcesz emitowa\u0107 warto\u015bci, czy chcesz, aby Tw\u00f3j klient to robi\u0142.<\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"prismatic\/blocks","attrs":{"language":"typescript"},"innerBlocks":[],"innerHTML":"\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">const writableSignal: WritableSignal&lt;Int> = signal(0);\nconst readonlySignal: Signal&lt;Int> = signal(0);\n\nconst sum: Signal&lt;Int> = computed(\n  \/\/ You can read from both signals\n  () => readonlySignal() + writableSignal() \n); \n\n\/\/ This works perfectly fine.\nwritableSignal.set(10); \n\n\/\/ TypeScript compiler will throw en error here. \nreadonlySignal.set(10); \n\n\/\/ TypeScript compiler will throw en error here. \n\/\/ Even casting to any can't help you.\nsum.set(10); <\/code><\/pre>\n","innerContent":["\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">const writableSignal: WritableSignal&lt;Int> = signal(0);\nconst readonlySignal: Signal&lt;Int> = signal(0);\n\nconst sum: Signal&lt;Int> = computed(\n  \/\/ You can read from both signals\n  () => readonlySignal() + writableSignal() \n); \n\n\/\/ This works perfectly fine.\nwritableSignal.set(10); \n\n\/\/ TypeScript compiler will throw en error here. \nreadonlySignal.set(10); \n\n\/\/ TypeScript compiler will throw en error here. \n\/\/ Even casting to any can't help you.\nsum.set(10); <\/code><\/pre>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p>M\u00f3wi\u0105c o przejrzystym przep\u0142ywie danych sygna\u0142y uniemo\u017cliwiaj\u0105 modyfikowanie innych sygna\u0142\u00f3w z poziomu metod <code>computed<\/code>() i <code>effect()<\/code>. Je\u015bl spr\u00f3bujesz zmodyfikowa\u0107 sygna\u0142 z ich poziomu, to rzucony zostanie b\u0142\u0105d. Po raz kolejny jest to bardzo dobry projekt API, poniewa\u017c w takich przypadkach nale\u017cy u\u017cy\u0107 metody <code>computed<\/code>.<\/p>\n","innerContent":["\n<p>M\u00f3wi\u0105c o przejrzystym przep\u0142ywie danych sygna\u0142y uniemo\u017cliwiaj\u0105 modyfikowanie innych sygna\u0142\u00f3w z poziomu metod <code>computed<\/code>() i <code>effect()<\/code>. Je\u015bl spr\u00f3bujesz zmodyfikowa\u0107 sygna\u0142 z ich poziomu, to rzucony zostanie b\u0142\u0105d. Po raz kolejny jest to bardzo dobry projekt API, poniewa\u017c w takich przypadkach nale\u017cy u\u017cy\u0107 metody <code>computed<\/code>.<\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"prismatic\/blocks","attrs":{"language":"typescript"},"innerBlocks":[],"innerHTML":"\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">const countA = signal(0);\nconst countB = signal(0);\nconst sumError = signal(countA() + countB());\n\n \/\/ This will thorw an error!\neffect(() => sumError.set(countA() + countB()));\n\n\/\/ This is the correct way to define sum of signals\nconst sumCorrect = computed(() => countA() + countB());<\/code><\/pre>\n","innerContent":["\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">const countA = signal(0);\nconst countB = signal(0);\nconst sumError = signal(countA() + countB());\n\n \/\/ This will thorw an error!\neffect(() => sumError.set(countA() + countB()));\n\n\/\/ This is the correct way to define sum of signals\nconst sumCorrect = computed(() => countA() + countB());<\/code><\/pre>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/heading","attrs":[],"innerBlocks":[],"innerHTML":"\n<h2>Integracja Angular Signals z Angularem<\/h2>\n","innerContent":["\n<h2>Integracja Angular Signals z Angularem<\/h2>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p>Aby zacz\u0105\u0107 u\u017cywa\u0107 sygna\u0142\u00f3w w swoich komponentach, wszystko, co musisz zrobi\u0107, to doda\u0107 <code>signals: true<\/code> do dekoratora <code>@Component<\/code>. Ta flaga modyfikuje strategi\u0119 wykrywania zmian dla komponentu i od tej pory b\u0119dzie on renderowany tylko wtedy, gdy zmieni si\u0119 jeden z sygna\u0142\u00f3w u\u017cywanych w komponencie.<\/p>\n","innerContent":["\n<p>Aby zacz\u0105\u0107 u\u017cywa\u0107 sygna\u0142\u00f3w w swoich komponentach, wszystko, co musisz zrobi\u0107, to doda\u0107 <code>signals: true<\/code> do dekoratora <code>@Component<\/code>. Ta flaga modyfikuje strategi\u0119 wykrywania zmian dla komponentu i od tej pory b\u0119dzie on renderowany tylko wtedy, gdy zmieni si\u0119 jeden z sygna\u0142\u00f3w u\u017cywanych w komponencie.<\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"prismatic\/blocks","attrs":{"language":"typescript"},"innerBlocks":[],"innerHTML":"\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">@Component({\n  signals: true,\n  selector: 'simple-counter',\n  template: `\n  &lt;p>Count: {{ count() }}&lt;\/p>\n  &lt;p>Double Count: {{ doubleCount() }}&lt;\/p>\n  &lt;button (click)=\"increment()\">Increment count&lt;\/button>`,\n})\nexport class SimpleCounter {\n  count = signal(0); \n  doubleCount = computed(() => 2 * count());\n\n  increment() {\n    this.count.update(c => c + 1);\n  }\n}<\/code><\/pre>\n","innerContent":["\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">@Component({\n  signals: true,\n  selector: 'simple-counter',\n  template: `\n  &lt;p>Count: {{ count() }}&lt;\/p>\n  &lt;p>Double Count: {{ doubleCount() }}&lt;\/p>\n  &lt;button (click)=\"increment()\">Increment count&lt;\/button>`,\n})\nexport class SimpleCounter {\n  count = signal(0); \n  doubleCount = computed(() => 2 * count());\n\n  increment() {\n    this.count.update(c => c + 1);\n  }\n}<\/code><\/pre>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p>Je\u015bli komponent jest oparty na sygna\u0142ach, to nie b\u0119dzie on wykorzystywa\u0142 do wykrywania zmian <code>zone.js<\/code>!  Przynajmniej dop\u00f3ki nie zaczniemy u\u017cywa\u0107 starych komponent\u00f3w w jego obr\u0119bie. W takich przypadkach Angular automatycznie podzieli aplikacj\u0119 na strefy i b\u0119dzie do nich aplikowa\u0142 odpowiedni\u0105 strategi\u0119 wykrywania zmian. Nasz komponent nadal b\u0119dzie renderowany tylko wtedy, gdy zmieni si\u0119 jeden z wewn\u0119trznych sygna\u0142\u00f3w, podczas gdy komponent dziecko b\u0119dzie u\u017cywa\u0142 starego dobrego wykrywania zmian opartego na <code>zone.js<\/code>.<\/p>\n","innerContent":["\n<p>Je\u015bli komponent jest oparty na sygna\u0142ach, to nie b\u0119dzie on wykorzystywa\u0142 do wykrywania zmian <code>zone.js<\/code>!  Przynajmniej dop\u00f3ki nie zaczniemy u\u017cywa\u0107 starych komponent\u00f3w w jego obr\u0119bie. W takich przypadkach Angular automatycznie podzieli aplikacj\u0119 na strefy i b\u0119dzie do nich aplikowa\u0142 odpowiedni\u0105 strategi\u0119 wykrywania zmian. Nasz komponent nadal b\u0119dzie renderowany tylko wtedy, gdy zmieni si\u0119 jeden z wewn\u0119trznych sygna\u0142\u00f3w, podczas gdy komponent dziecko b\u0119dzie u\u017cywa\u0142 starego dobrego wykrywania zmian opartego na <code>zone.js<\/code>.<\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"prismatic\/blocks","attrs":{"language":"typescript"},"innerBlocks":[],"innerHTML":"\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">@Component({\n  signals: true,\n  selector: 'simple-counter',\n  template: `\n  &lt;p>Count {{ count() }}&lt;\/p>\n  &lt;zone-component \/>\n  &lt;button (click)=\"increment()\">Increment count&lt;\/button>`,\n})\nexport class SimpleCounter {\n  count = signal(0);\n\n  increment() {\n    this.count.update(c => c + 1);\n  }\n}<\/code><\/pre>\n","innerContent":["\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">@Component({\n  signals: true,\n  selector: 'simple-counter',\n  template: `\n  &lt;p>Count {{ count() }}&lt;\/p>\n  &lt;zone-component \/>\n  &lt;button (click)=\"increment()\">Increment count&lt;\/button>`,\n})\nexport class SimpleCounter {\n  count = signal(0);\n\n  increment() {\n    this.count.update(c => c + 1);\n  }\n}<\/code><\/pre>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p>Jednym z wa\u017cnych aspekt\u00f3w nowej strategii wykrywania zmian jest fakt, \u017ce nie mo\u017cna ju\u017c imperatywnie modyfikowa\u0107 w\u0142a\u015bciwo\u015bci komponent\u00f3w i oczekiwa\u0107 ponownego renderowania interfejsu. Nadal mo\u017cemy u\u017cywa\u0107 w\u0142a\u015bciwo\u015bci klas w swoim szablonie, ale b\u0119d\u0105 one od\u015bwie\u017cane tylko wtedy, gdy zmieni si\u0119 jeden z sygna\u0142\u00f3w. <\/p>\n","innerContent":["\n<p>Jednym z wa\u017cnych aspekt\u00f3w nowej strategii wykrywania zmian jest fakt, \u017ce nie mo\u017cna ju\u017c imperatywnie modyfikowa\u0107 w\u0142a\u015bciwo\u015bci komponent\u00f3w i oczekiwa\u0107 ponownego renderowania interfejsu. Nadal mo\u017cemy u\u017cywa\u0107 w\u0142a\u015bciwo\u015bci klas w swoim szablonie, ale b\u0119d\u0105 one od\u015bwie\u017cane tylko wtedy, gdy zmieni si\u0119 jeden z sygna\u0142\u00f3w. <\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"prismatic\/blocks","attrs":{"language":"typescript"},"innerBlocks":[],"innerHTML":"\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">@Component({\n  signals: true,\n  selector: 'simple-counter',\n  template: `\n  &lt;p>Signal Count {{ count() }}&lt;\/p>\n  &lt;p>Imperative Count {{ count() }}&lt;\/p>\n  \n  &lt;!-- Clicking this button won't have any effect in the UI -->\n  &lt;button (click)=\"incrementImperative()\">Increment Imperative Count&lt;\/button>\n\n  &lt;!-- Clicking this button will update both counts in the UI -->\n  &lt;button (click)=\"incrementSignal()\">Increment Signal Count&lt;\/button>`\n})\nexport class SimpleCounter {\n  count = signal(0);\n  countImperative = 0;\n\n  incrementImperative(): void {\n    countImperative = countImperative + 1;\n  }\n\n  incrementSignal(): void {\n    this.count.update(c => c + 1);\n  }\n}<\/code><\/pre>\n","innerContent":["\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">@Component({\n  signals: true,\n  selector: 'simple-counter',\n  template: `\n  &lt;p>Signal Count {{ count() }}&lt;\/p>\n  &lt;p>Imperative Count {{ count() }}&lt;\/p>\n  \n  &lt;!-- Clicking this button won't have any effect in the UI -->\n  &lt;button (click)=\"incrementImperative()\">Increment Imperative Count&lt;\/button>\n\n  &lt;!-- Clicking this button will update both counts in the UI -->\n  &lt;button (click)=\"incrementSignal()\">Increment Signal Count&lt;\/button>`\n})\nexport class SimpleCounter {\n  count = signal(0);\n  countImperative = 0;\n\n  incrementImperative(): void {\n    countImperative = countImperative + 1;\n  }\n\n  incrementSignal(): void {\n    this.count.update(c => c + 1);\n  }\n}<\/code><\/pre>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p>W komponentach opartych o sygna\u0142y zmieni\u0142 si\u0119 r\u00f3wnie\u017c spos\u00f3b definiowania zmiennych wej\u015bciowych i wyj\u015bciowych. Zamiast dekorowania <code>@Inptut()<\/code> i <code>@Output()<\/code> u\u017cy\u0107 nale\u017cy funkcji <code>input()<\/code> i <code>output()<\/code>. Warto zwr\u00f3\u0107 uwag\u0119 na typ zwracany przez funkcj\u0119 <code>input()<\/code>. Wyra\u017any podzia\u0142 na sygna\u0142y tylko do odczytu i do zapisu zmusza nas do zaprojektowania przejrzystego, jednokierunkowego przep\u0142ywu danych.<\/p>\n","innerContent":["\n<p>W komponentach opartych o sygna\u0142y zmieni\u0142 si\u0119 r\u00f3wnie\u017c spos\u00f3b definiowania zmiennych wej\u015bciowych i wyj\u015bciowych. Zamiast dekorowania <code>@Inptut()<\/code> i <code>@Output()<\/code> u\u017cy\u0107 nale\u017cy funkcji <code>input()<\/code> i <code>output()<\/code>. Warto zwr\u00f3\u0107 uwag\u0119 na typ zwracany przez funkcj\u0119 <code>input()<\/code>. Wyra\u017any podzia\u0142 na sygna\u0142y tylko do odczytu i do zapisu zmusza nas do zaprojektowania przejrzystego, jednokierunkowego przep\u0142ywu danych.<\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"prismatic\/blocks","attrs":{"language":"typescript"},"innerBlocks":[],"innerHTML":"\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">@Component({\n  signals: true,\n  selector: 'simple-counter',\n  template: `\n  &lt;p>Count {{ count() }}&lt;\/p>\n  &lt;button (click)=\"onClick()\">Increment Count&lt;\/button>`\n})\nexport class SimpleCounter {\n  count: Signal&lt;number> = input&lt;number>(0);\n  buttonClick: EventEmitter&lt;number> = output&lt;void>();\n\n  onClick(): void {\n    this.buttonClick.emit();\n  }\n}<\/code><\/pre>\n","innerContent":["\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">@Component({\n  signals: true,\n  selector: 'simple-counter',\n  template: `\n  &lt;p>Count {{ count() }}&lt;\/p>\n  &lt;button (click)=\"onClick()\">Increment Count&lt;\/button>`\n})\nexport class SimpleCounter {\n  count: Signal&lt;number> = input&lt;number>(0);\n  buttonClick: EventEmitter&lt;number> = output&lt;void>();\n\n  onClick(): void {\n    this.buttonClick.emit();\n  }\n}<\/code><\/pre>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p>Ostatni\u0105 wa\u017cn\u0105 r\u00f3\u017cnic\u0105 pomi\u0119dzy zwyk\u0142ymi komponentami, a komponentami opartymi na sygna\u0142ac jest nowy cykl \u017cycia komponentu. Poniewa\u017c wykrywanie zmian dzia\u0142a zupe\u0142nie inaczej, stare metody cyklu \u017cycia nie maj\u0105 zbytnio sensu. W nowym podej\u015bciu zamiast implementowa\u0107 interfejsy, rejestrujemy callbacki za pomoc\u0105 magicznych funkcji. <\/p>\n","innerContent":["\n<p>Ostatni\u0105 wa\u017cn\u0105 r\u00f3\u017cnic\u0105 pomi\u0119dzy zwyk\u0142ymi komponentami, a komponentami opartymi na sygna\u0142ac jest nowy cykl \u017cycia komponentu. Poniewa\u017c wykrywanie zmian dzia\u0142a zupe\u0142nie inaczej, stare metody cyklu \u017cycia nie maj\u0105 zbytnio sensu. W nowym podej\u015bciu zamiast implementowa\u0107 interfejsy, rejestrujemy callbacki za pomoc\u0105 magicznych funkcji. <\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"prismatic\/blocks","attrs":{"language":"typescript"},"innerBlocks":[],"innerHTML":"\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">@Component({\n  signals: true,\n  selector: 'user-profile',\n  template: `\n    &lt;h1>Hello {{name()}}!&lt;\/h1>`,\n})\nexport class LifecycleComponent {\n  name = signal('Tomek');\n\n  constructor() {\n    afterInit(() => {\n      \/\/ All inputs have their initial values.\n    });\n    \n    afterRender(() => {\n      \/\/ After the DOM of *all* components has been fully rendered.\n    });\n    \n    afterNextRender(() => {\n      \/\/ Same as afterRender, but only runs once.\n    });\n\n    afterRenderEffect(() => {\n      \/\/ Same as afterRender in terms of timing, \n      \/\/ but runs whenever the signals which it reads have changed.\n      console.log(`DOM was updated due to '${this.name()}'`);\n    });\n    \n    beforeDestroy(() => {\n      \/\/ This component instance is about to be destroyed.\n    });\n  }\n}<\/code><\/pre>\n","innerContent":["\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">@Component({\n  signals: true,\n  selector: 'user-profile',\n  template: `\n    &lt;h1>Hello {{name()}}!&lt;\/h1>`,\n})\nexport class LifecycleComponent {\n  name = signal('Tomek');\n\n  constructor() {\n    afterInit(() => {\n      \/\/ All inputs have their initial values.\n    });\n    \n    afterRender(() => {\n      \/\/ After the DOM of *all* components has been fully rendered.\n    });\n    \n    afterNextRender(() => {\n      \/\/ Same as afterRender, but only runs once.\n    });\n\n    afterRenderEffect(() => {\n      \/\/ Same as afterRender in terms of timing, \n      \/\/ but runs whenever the signals which it reads have changed.\n      console.log(`DOM was updated due to '${this.name()}'`);\n    });\n    \n    beforeDestroy(() => {\n      \/\/ This component instance is about to be destroyed.\n    });\n  }\n}<\/code><\/pre>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p>Podej\u015bcie magicznych funkcji daje nam kilka niesamowitych mo\u017cliwo\u015bci, takich jak rejestrowanie callback\u00f3w w odpowiedzi na klikni\u0119ciu przycisku.<\/p>\n","innerContent":["\n<p>Podej\u015bcie magicznych funkcji daje nam kilka niesamowitych mo\u017cliwo\u015bci, takich jak rejestrowanie callback\u00f3w w odpowiedzi na klikni\u0119ciu przycisku.<\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"prismatic\/blocks","attrs":{"language":"typescript"},"innerBlocks":[],"innerHTML":"\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">@Component({\n  signals: true,\n  selector: 'simple-counter',\n  template: `\n  &lt;p>Count {{ count() }}&lt;\/p>\n  &lt;button (click)=\"increment()\">Increment count&lt;\/button>\n  &lt;button (click)=\"addCallbacks()\">Add callbacks&lt;\/button>`,\n})\nexport class SimpleCounter {\n  count = signal(0); \/\/ WritableSignal&lt;number>\n\n  increment() {\n    this.count.update(c => c + 1);\n  }\n\n  addCallbacks() {\n    afterRenderEffect(() => {\n      \/\/ I need to get notified the next time name changes!\n      console.log(`Count changed: '${this.count()}'`);\n    });\n\n    beforeDestroy(() => {\n      \/\/ Because I have initialized some stuff in my function\n      \/\/ Now I have to do some additional cleanup \n      unsubscribe();\n    });\n  }\n\n  private unsubscribe() {\n  \/*...*\/\n  }\n}<\/code><\/pre>\n","innerContent":["\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">@Component({\n  signals: true,\n  selector: 'simple-counter',\n  template: `\n  &lt;p>Count {{ count() }}&lt;\/p>\n  &lt;button (click)=\"increment()\">Increment count&lt;\/button>\n  &lt;button (click)=\"addCallbacks()\">Add callbacks&lt;\/button>`,\n})\nexport class SimpleCounter {\n  count = signal(0); \/\/ WritableSignal&lt;number>\n\n  increment() {\n    this.count.update(c => c + 1);\n  }\n\n  addCallbacks() {\n    afterRenderEffect(() => {\n      \/\/ I need to get notified the next time name changes!\n      console.log(`Count changed: '${this.count()}'`);\n    });\n\n    beforeDestroy(() => {\n      \/\/ Because I have initialized some stuff in my function\n      \/\/ Now I have to do some additional cleanup \n      unsubscribe();\n    });\n  }\n\n  private unsubscribe() {\n  \/*...*\/\n  }\n}<\/code><\/pre>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/heading","attrs":[],"innerBlocks":[],"innerHTML":"\n<h2>Angular Signals i wsp\u00f3\u0142praca z RxJS<\/h2>\n","innerContent":["\n<h2>Angular Signals i wsp\u00f3\u0142praca z RxJS<\/h2>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p>Poniewa\u017c Signals i RxJS b\u0119d\u0105 d\u0142ugo wsp\u00f3\u0142istnie\u0107 w wielu aplikacjach, bardzo wa\u017cne jest posiadanie dobrych narz\u0119dzi do prze\u0142\u0105czania si\u0119 pomi\u0119dzy tymi abstrakcjami. Takie narz\u0119dzia b\u0119d\u0105 r\u00f3wnie\u017c mocno wykorzystywane przez sam zesp\u00f3\u0142 Angulara, poniewa\u017c zesp\u00f3\u0142 planuj\u0105 oni dostarcza\u0107 wi\u0119kszo\u015b\u0107 API zar\u00f3wno dla Signals jak i RxJS API. <\/p>\n","innerContent":["\n<p>Poniewa\u017c Signals i RxJS b\u0119d\u0105 d\u0142ugo wsp\u00f3\u0142istnie\u0107 w wielu aplikacjach, bardzo wa\u017cne jest posiadanie dobrych narz\u0119dzi do prze\u0142\u0105czania si\u0119 pomi\u0119dzy tymi abstrakcjami. Takie narz\u0119dzia b\u0119d\u0105 r\u00f3wnie\u017c mocno wykorzystywane przez sam zesp\u00f3\u0142 Angulara, poniewa\u017c zesp\u00f3\u0142 planuj\u0105 oni dostarcza\u0107 wi\u0119kszo\u015b\u0107 API zar\u00f3wno dla Signals jak i RxJS API. <\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p><code>fromObservable()<\/code> to funkcja konweruj\u0105ca <code>Observable<\/code> na <code>Signal<\/code>. Drugim argumentem przekazywanym do funkcji jest warto\u015b\u0107 przechowywana wewn\u0105trz sygna\u0142u do momentu wyemitowania warto\u015bci przez strumie\u0144. Je\u015bli nie podamy warto\u015bci domy\u015blnej i b\u0119dziemy pr\u00f3bowali uzyska\u0107 dost\u0119p do sygna\u0142u przed emisj\u0105 pierwszego wydarzenia, zostanie wyrzucony b\u0142\u0105d. Warto r\u00f3wnie\u017c zauwa\u017cy\u0107, \u017ce <code>fromObservable()<\/code> natychmiast zasubskrybuje si\u0119 na warto\u015b\u0107, aby unikn\u0105\u0107 efekt\u00f3w ubocznych wywo\u0142anych przez odczytanie sygna\u0142u.<\/p>\n","innerContent":["\n<p><code>fromObservable()<\/code> to funkcja konweruj\u0105ca <code>Observable<\/code> na <code>Signal<\/code>. Drugim argumentem przekazywanym do funkcji jest warto\u015b\u0107 przechowywana wewn\u0105trz sygna\u0142u do momentu wyemitowania warto\u015bci przez strumie\u0144. Je\u015bli nie podamy warto\u015bci domy\u015blnej i b\u0119dziemy pr\u00f3bowali uzyska\u0107 dost\u0119p do sygna\u0142u przed emisj\u0105 pierwszego wydarzenia, zostanie wyrzucony b\u0142\u0105d. Warto r\u00f3wnie\u017c zauwa\u017cy\u0107, \u017ce <code>fromObservable()<\/code> natychmiast zasubskrybuje si\u0119 na warto\u015b\u0107, aby unikn\u0105\u0107 efekt\u00f3w ubocznych wywo\u0142anych przez odczytanie sygna\u0142u.<\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"prismatic\/blocks","attrs":{"language":"typescript"},"innerBlocks":[],"innerHTML":"\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">const mySignal = fromObservable(myObservable$);<\/code><\/pre>\n","innerContent":["\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">const mySignal = fromObservable(myObservable$);<\/code><\/pre>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"core\/paragraph","attrs":[],"innerBlocks":[],"innerHTML":"\n<p><code>fromSignal<\/code> konwertuje strumie\u0144 na sygna\u0142. Konwersja w t\u0105 stron\u0119 jest znacznie prostsza, poniewa\u017c strumienie s\u0105 znacznie bardziej elastyczn\u0105 abstrakcj\u0105.<\/p>\n","innerContent":["\n<p><code>fromSignal<\/code> konwertuje strumie\u0144 na sygna\u0142. Konwersja w t\u0105 stron\u0119 jest znacznie prostsza, poniewa\u017c strumienie s\u0105 znacznie bardziej elastyczn\u0105 abstrakcj\u0105.<\/p>\n"]},{"blockName":null,"attrs":[],"innerBlocks":[],"innerHTML":"\n\n","innerContent":["\n\n"]},{"blockName":"prismatic\/blocks","attrs":{"language":"typescript"},"innerBlocks":[],"innerHTML":"\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">const myObsrevable$ = fromObservable(mySignal);<\/code><\/pre>\n","innerContent":["\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\">const myObsrevable$ = fromObservable(mySignal);<\/code><\/pre>\n"]}],"_links":{"self":[{"href":"https:\/\/vived.io\/pl\/wp-json\/wp\/v2\/posts\/15668","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vived.io\/pl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vived.io\/pl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vived.io\/pl\/wp-json\/wp\/v2\/users\/12"}],"replies":[{"embeddable":true,"href":"https:\/\/vived.io\/pl\/wp-json\/wp\/v2\/comments?post=15668"}],"version-history":[{"count":15,"href":"https:\/\/vived.io\/pl\/wp-json\/wp\/v2\/posts\/15668\/revisions"}],"predecessor-version":[{"id":15694,"href":"https:\/\/vived.io\/pl\/wp-json\/wp\/v2\/posts\/15668\/revisions\/15694"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/vived.io\/pl\/wp-json\/wp\/v2\/media\/15602"}],"wp:attachment":[{"href":"https:\/\/vived.io\/pl\/wp-json\/wp\/v2\/media?parent=15668"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vived.io\/pl\/wp-json\/wp\/v2\/categories?post=15668"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vived.io\/pl\/wp-json\/wp\/v2\/tags?post=15668"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}