ITNEXT

ITNEXT is a platform for IT developers & software engineers to share knowledge, connect…

Follow publication

Angular v16 signals, everything old is new again

Jose I Santa Cruz G
ITNEXT
Published in
6 min readMar 31, 2023

--

Photo by Alex King on Unsplash

What does the rootScope have to do with signals?

watch(variable: string): any {
let idx: number = this.keyIndex[variable];
try{
// let watchVar: Observable<any> = this.dataObservables[idx];
// return watchVar;
return this.dataArray[idx].asObservable();
} catch(err) {
throw new Error('Variable ' + variable + ' not present. Try setItem("' + variable + '", this.' + variable + ') first.');
}

}

unWatch(variable: string): void {
console.log('unWatch ', variable);
let idx: number = this.keyIndex[variable];
try{
let dataValue = this.dataArray[idx].value;
this.dataArray[idx].next(dataValue);
this.dataArray[idx].complete();
} catch(err) {
throw new Error('Variable ' + variable + ' not present. Try setItem("' + variable + '", this.' + variable + ') first.');
}
}
this.isJedi$ = this.rootScope.watch('isJedi')     
.takeUntil(this.ngUnsubscribe)
.subscribe(variable => {
this.isJedi = (variable != undefined) ? variable.value : undefined;
console.log('isJedi watcher ', variable);
});

Still don't see the relation with signals…

signals…
<div class="card"
[ngClass]="{
'hilite': hilite()
}">

The test app

  "dependencies": {
"@angular/animations": "^16.0.0-next.5",
"@angular/common": "^16.0.0-next.5",
"@angular/compiler": "^16.0.0-next.5",
"@angular/core": "^16.0.0-next.5",
"@angular/forms": "^16.0.0-next.5",
"@angular/platform-browser": "^16.0.0-next.5",
"@angular/platform-browser-dynamic": "^16.0.0-next.5",
"@angular/router": "^16.0.0-next.5",
"@nrwl/angular": "^15.8.9",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "^0.13.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^16.0.0-next.5",
"@angular-devkit/core": "^16.0.0-next.0",
"@angular-devkit/schematics": "^16.0.0-next.0",
"@angular-eslint/eslint-plugin": "~15.0.0",
"@angular-eslint/eslint-plugin-template": "~15.0.0",
"@angular-eslint/template-parser": "~15.0.0",
"@angular/cli": "^16.0.0-next.5",
"@angular/compiler-cli": "^16.0.0-next.5",
"@angular/language-service": "^16.0.0-next.5",
"@nrwl/cypress": "15.8.9",
"@nrwl/eslint-plugin-nx": "15.8.9",
"@nrwl/jest": "15.8.9",
"@nrwl/js": "15.8.9",
"@nrwl/linter": "15.8.9",
"@nrwl/nx-cloud": "latest",
"@nrwl/workspace": "15.8.9",
"@schematics/angular": "^16.0.0-next.5",
"@types/jest": "^29.4.0",
"@types/node": "16.11.7",
"@typescript-eslint/eslint-plugin": "^5.36.1",
"@typescript-eslint/parser": "^5.36.1",
"cypress": "^12.2.0",
"eslint": "~8.15.0",
"eslint-config-prettier": "8.1.0",
"eslint-plugin-cypress": "^2.10.3",
"jest": "^29.4.1",
"jest-environment-jsdom": "^29.4.1",
"jest-preset-angular": "~13.0.0",
"nx": "15.8.9",
"prettier": "^2.6.2",
"ts-jest": "^29.0.5",
"ts-node": "10.9.1",
"typescript": "~4.9.5"
}
import { ChangeDetectionStrategy, Component, inject, OnChanges, OnInit, signal, SimpleChanges, DoCheck } from '@angular/core';
import { CommonModule, NgFor, NgIf } from '@angular/common';
import { UserService } from '../../services/user.service';
import { User } from '../../types/user.interface';
import { UserCardComponentSignaled } from '../../components/user-car-signaled/user-card.component';
import { GLOBAL_SIGNAL_SERVICE } from '../../services/global-signal.service';

@Component({
selector: 'ng16-signals-with-signals',
standalone: true,
imports: [
CommonModule,
UserCardComponentSignaled,
NgFor,
NgIf
],
templateUrl: './with-signals.component.html',
styleUrls: ['./with-signals.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class WithSignalsComponent implements OnInit, OnChanges, DoCheck {
userService = inject(UserService);
globalSignalService = inject(GLOBAL_SIGNAL_SERVICE);
users = signal<Array<User>>([]);
currentUser = this.globalSignalService.getSignal<User>('currentUser');

constructor() { }

ngDoCheck(): void {
console.log('ngDoCheck');
}

ngOnInit(): void {
console.log('ngOnInit');
this.userService.retrieveUsers().subscribe(users => {
this.users.set(users);
});
}

ngOnChanges(changes: SimpleChanges): void {
console.log('ngOnChanges', changes);
}
}
import { inject, Injectable, InjectionToken, signal, WritableSignal } from '@angular/core';

@Injectable({
providedIn: 'root'
})
export class GlobalSignalService {

signalMap = new Map<string, WritableSignal<unknown>>();

constructor() { }

getSignal<T>(key: string): WritableSignal<T> {
if (!this.signalMap.has(key)) {
this.signalMap.set(key, signal<T | undefined>(undefined));
}
return this.signalMap.get(key) as WritableSignal<T>;
}

setSignal<T>(key: string, value: T): void {
this.getSignal<T>(key).set(value);
}
}

export let GLOBAL_SIGNAL_SERVICE = new InjectionToken<GlobalSignalService>('GLOBAL_SIGNAL_SERVICE', {
providedIn: 'root',
factory: () => inject(GlobalSignalService)
});
  //...
setCurrent(): void {
console.log('setCurrent', this.user);
this.globalSignalService.setSignal('currentUser', this.user);
}

Will signals erradicate RxJS?

Will signals change the way we use Angular

Don't get confused :P

--

--

Published in ITNEXT

ITNEXT is a platform for IT developers & software engineers to share knowledge, connect, collaborate, learn and experience next-gen technologies.

Written by Jose I Santa Cruz G

Polyglot senior software engineer, amateur guitar & bass player, geek, husband and dog father. Not precisely in that order.

Responses (4)