
Beyond Angular Signals: Signals & Custom Render Strategies
by Younes Jaaidi โข Apr 7, 2023 โข 8 minutes

@Component({
...
template: `
<div *ngFor="let _ of lines">{{ count() }}</div>
`,
})
export class CounterComponent implements OnInit {
count = signal(0);
lines = Array(10_000);
ngOnInit() {
setInterval(() => this.count.update(value => value + 1), 100);
}
}
@Component({
...
template: `{{ throttledCount() }}`
})
class MyCmp {
count = signal(0);
throttledCount = throttleSignal(this.count, {duration: 1000});
...
}
lazyCount = applyViewportStrategy(this.count, {element});
template: `
<span *lazyViewportSignal="count(); let countValue">{{ countValue }}</span>
<span> x 2 = </span>
<span *lazyViewportSignal="double(); let doubleValue">{{ doubleValue }}</span>
`
@Component({
...
template: `
<div *viewportStrategy>
<span>{{ count() }}</span>
<span> x 2 = </span>
<span>{{ double() }} </span>
</div>
`,
})
export class CounterComponent implements OnInit {
count = Signal(0);
double = computed(() => count());
}
/**
* This doesn't work as expected!
*/
const viewRef = vcr.createEmbeddedView(templateRef);
viewRef.detach();
effect(() => {
console.log('Yeay! we are in!'); // if called more than once
viewRef.detectChanges();
});
onConsumerDependencyMayHaveChanged() {
...
markViewDirty(this._lView);
}
@Directive({
standalone: true,
selector: '[viewportStrategy]',
})
class ViewportStrategyDirective {
private _templateRef = inject(TemplateRef);
private _vcr = inject(ViewContainerRef);
ngOnInit() {
const viewRef = this._vcr.createEmbeddedView(this._templateRef);
}
}
viewRef.detectChanges();
viewRef.detach();
const reactiveViewConsumer = viewRef['_lView'][REACTIVE_TEMPLATE_CONSUMER /* 23 */];
let timeout;
reactiveViewConsumer.onConsumerDependencyMayHaveChanged = () => {
if (timeout != null) {
return;
}
timeout = setTimeout(() => {
viewRef.detectChanges();
timeout = null;
}, 1000);
};
interface ViewRef {
/* This doesn't exist. */
setCustomSignalChangeHandler(callback: () => void);
}
const CounterWithViewportStrategy = withViewportStrategy(() => <div>{count}</div>);
export function App() {
...
return <>
{items.map(() => <CounterWithViewportStrategy count={count}/>}
</>
}
defineComponent({
setup() {
const count = ref(0);
return viewportStrategy(({ rootEl }) => (
<div ref={rootEl}>{ count }</div>
));
},
})