Since I published my Master Angular 17 Study guide and Master Angular 17.1 and 17.2 Study guide, the Angular team released a new minor version: 17.3.
This article is also available on dev.to with better source code syntax highlighting.
🎯Changes and new features
In this article, I list out the most important changes and new features, also share resources that will teach you how these new Angular features work:
output()
API for emitting output from a componentoutputFromObservable()
helper function for transforming an observable to a component'soutput()
outputToObservable()
helper function for converting a component's output to an observablehostAttributeToken()
class, it creates a token that can be used to inject static attributes of the host nodeTypescript 5.4 support
📌New output()
API
Official docs: output
PR: Finalizing output() and outputFromObservable() APIs
Angular 17.3 introduced the new output()
API, for emitting output from a component. This API is designed to complement the input()
and model()
APIs, and it's type safe:
@Component({
selector: 'app-output',
standalone: true,
template: `
<button (click)="onClick.emit()">Button</button>
<input #in type="text" (keyup)="onChange.emit(in.value)" />
`,
})
export class OutputComponent {
onClick = output(); // 👈 OutputEmitterRef<void>
onChange = output<string>(); // 👈 OutputEmitterRef<string>
}
@Component({
selector: 'app-output-wrapper',
standalone: true,
imports: [OutputComponent],
template: ` <app-output (onClick)="log('onClick')" (onChange)="log('onChange', $event)" /> `,
})
export class OutputWrapperComponent {
log(t1: string, t2: string = '') { console.log(t1, t2); }
}
// after you click on the button, then type 'test' into the input field,
// the messages on the console are:
// onClick
// onChange t
// onChange te
// onChange tes
// onChange test
📌outputFromObservable()
and outputToObservable()
helper functions
Official docs: outputFromObservable, outputToObservable
PR: Finalizing output() and outputFromObservable() APIs
In addition the new output()
API, Angular 17.3 also contains the outputFromObservable()
helper function for transforming an observable to a component's output()
:
@Component({
selector: 'app-output2',
standalone: true,
template: `<button (click)="onClick$.next('click2')">Button</button>`,
})
export class Output2Component {
onClick$ = new BehaviorSubject(''); // 👈 BehaviorSubject<string>
onClick = outputFromObservable(this.onClick$); // 👈 OutputRef<string>
}
@Component({
selector: 'app-output-wrapper2',
standalone: true,
imports: [Output2Component],
template: `<app-output2 (onClick)="log('onClick', $event)" />`,
})
export class OutputWrapper2Component {
log(t1: string, t2: string = '') { console.log(t1, t2); }
}
// after you click on the button, the message on the console is:
// onClick click2
There is also a new outputToObservable()
helper function for converting a component's output to an observable:
@Component({
selector: 'app-output3',
standalone: true,
template: `<button (click)="onClick.emit()">Button</button>`,
})
export class Output3Component {
onClick = output(); // 👈 OutputEmitterRef<void>
}
@Component({
selector: 'app-output-wrapper3',
standalone: true,
imports: [Output3Component],
template: `<app-output3/>`, // 👈 no (onClick)="..." here!
})
export class OutputWrapper3Component implements OnInit {
childComponent = viewChild(Output3Component);
destroyRef = inject(DestroyRef);
ngOnInit(): void {
const childComponent = this.childComponent();
if (childComponent) {
const onClick$ = outputToObservable(childComponent.onClick) // 👈
.pipe(takeUntilDestroyed(this.destroyRef));
onClick$.subscribe(() => console.log('onClick'));
}
}
}
// after you click on the button, the message on the console is:
// onClick
📌HostAttributeToken()
class
Official docs: HostAttributeToken
PR: feat(core): add API to inject attributes on the host node
The HostAttributeToken()
class creates a token that can be used to inject static attributes of the host node. It works similarly to constructor(@Attribute('value') type: string)
, but uses the newer inject()
API instead of the @Attribute
decorator.
@Component({
selector: 'app-hat',
standalone: true,
template: `<div>{{ value }}</div>
<div>{{ value2 }}</div>`,
})
export class HatComponent {
// 👇 required, we get a DI error if the attribute is not specified
value = inject(new HostAttributeToken('value'));
// 👇 optional attribute
value2 = inject(new HostAttributeToken('value2'),
{ optional: true }) || 'Default value';
}
@Component({
selector: 'app-hat-wrapper',
standalone: true,
imports: [HatComponent],
// we don't specify the optional 'value2' attribute,
// 👇 so its value is 'Default value'
template: `<app-hat value="Hello" />`,
})
export class HatWrapperComponent {}
In his article, Netanel Basal explains in more details how this new feature works.
📌Typescript 5.4 support
PR: feat(core): support TypeScript 5.4
Daniel Rosenwasser highlighted the most interesting new features of Typescript 5.4 in his announcement:
Preserved Narrowing in Closures Following Last Assignments
The
NoInfer
Utility TypeObject.groupBy
andMap.groupBy
Support for
require()
calls in--moduleResolution bundler
and--module preserve
Checked Import Attributes and Assertions
Quick Fix for Adding Missing Parameters
Auto-Import Support for Subpath Imports
👨💻About the author
My name is Gergely Szerovay, I work as a frontend development chapter lead. Teaching (and learning) Angular is one of my passions. I consume content related to Angular on a daily basis — articles, podcasts, conference talks, you name it.
I created the Angular Addict Newsletter so that I can send you the best resources I come across each month. Whether you are a seasoned Angular Addict or a beginner, I got you covered.
Next to the newsletter, I also have a publication called Angular Addicts. It is a collection of the resources I find most informative and interesting. Let me know if you would like to be included as a writer.
Let’s learn Angular together! Subscribe here 🔥
Follow me on Substack, Medium, Dev.to, Twitter or LinkedIn to learn more about Angular!