问题
so I have been trying to convert my application into angular universal, and for the most part it has been fine. But I read some "gotchas" earlier: https://github.com/onespeed-articles/angular-universal-gotchas
It suggests that I should not be using timeout
which is annoying, because in angularjs you used it to make things fire after a view was rendered so you could be sure that dimensions of elements were correct, etc.
I have also read that it still persists in angular 6 so I have this directive:
import { Directive, ElementRef, AfterViewInit, Input, HostListener } from '@angular/core';
@Directive({
selector: '[pyb-button-group]'
})
export class ButtonGroupDirective implements AfterViewInit {
@Input() className: string;
@HostListener('window:resize', ['$event'])
onResize() {
let resize = this.resize;
let element = this.element;
let className = this.className;
setTimeout(function () {
resize(element, className);
}, 500);
}
constructor(private element: ElementRef) {
}
ngAfterViewInit() {
this.resize(this.element, this.className);
}
resize(nativeElement, className) {
let elements = nativeElement.nativeElement.getElementsByClassName(className || 'btn-choice');
let headerHeight = 0;
for (var i = 0; i < elements.length; i++) {
let element = elements[i];
let header = element.getElementsByClassName('header');
if (!header.length) return;
header = header[0];
header.style.height = 'auto'; // Reset when resizing the window
let height = header.offsetHeight;
if (height > headerHeight) headerHeight = height;
}
for (var i = 0; i < elements.length; i++) {
let element = elements[i];
let header = element.getElementsByClassName('header');
if (!header.length) return;
header = header[0];
header.style.height = headerHeight + 'px';
}
}
}
When the browser resizes, it waits for .5 of a second before it tries to resize the button group. If this is bad practice, what should I use instead?
回答1:
When the browser resizes, it waits for .5 of a second before it tries to resize the button group. If this is bad practice, what should I use instead?
You are delaying every resize event. Generally, you want to debounce and handle only one event every 500ms. This can be done using an observable, but make sure to unsubscribe when the component is destroyed.
observableFromEvent(window, 'resize')
.pipe(debounceTime(500))
.subscribe(() => this.resize(this.element, this.className))
Also @HostListener()
never fires events during server-side rendering. There is no DOM on the server to trigger window resize events.
setTimeout(function () {
resize(element, className);
}, 500);
The setTimeout
global function that you are using here has a different function signature in NodeJS. When you run the application in Angular Universe it runs inside NodeJS to generate the HTML before it goes to the web browser. setTimeout returns a number on web browsers but returns a resource handle in NodeJS.
If you're going to run your application in Angular Universal then it means you're writing source code that is decoupled from the web browser. That means no direct manipulation of the DOM, no global variables and no global window variable.
回答2:
You want that code to run in browser not in server. The link you provided gives you the solution:
if (isPlatformBrowser(this.platformId)) {
//your setTimeout here
}
来源:https://stackoverflow.com/questions/53787940/angular-universal-timeout-should-not-be-used