once-click
import { Directive, Input, OnDestroy, OnInit, HostListener, Output, EventEmitter, Renderer2, ElementRef } from '@angular/core';
import { throttleTime } from 'rxjs/operators';
import { Subject, Subscription } from 'rxjs';
\
\
@Directive({
// tslint:disable-next-line: directive-selector
selector: '[click.once]'
})
export class OnceClickDirective implements OnInit, OnDestroy {
// tslint:disable-next-line:no-output-rename
@Output('click.once') clickCall: EventEmitter<MouseEvent> = new EventEmitter();
@Input() duration = 1000; // 必须是数字,传入时要用绑定语法
private $sub = new Subject<any>();
private subscription: Subscription;
constructor(
private renderer: Renderer2, // 导入Renderer
private element: ElementRef
) { }
ngOnInit() {
this.subscription = this.$sub.pipe(
throttleTime(this.duration)
).subscribe((e: MouseEvent) => {
this.clickCall.emit(e);
});
}
@HostListener('click', ['$event']) // 监听宿主点击事件
clickEvent(event: MouseEvent) {
event.preventDefault(); // 阻止浏览器的默认行为
event.stopPropagation(); // 阻止事件冒泡
this.$sub.next(event);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
once-enter
Directive,
Input,
OnDestroy,
OnInit,
HostListener,
Output,
EventEmitter,
Renderer2,
ElementRef
} from '@angular/core';
import { debounceTime, throttleTime } from 'rxjs/operators';
import { Subject, Subscription } from 'rxjs';
\
\
@Directive({
selector: '[enter.once]'
})
export class OnceEnterDirective implements OnInit, OnDestroy {
@Output('enter.once') enterCall: EventEmitter<MouseEvent> = new EventEmitter();
@Input() duration = 1000; // 必须是数字,传入时要用绑定语法
private $sub = new Subject<any>();
private subscription: Subscription;
constructor(
private renderer: Renderer2, // 导入Renderer
private element: ElementRef
) {}
ngOnInit() {
this.subscription = this.$sub
.pipe(debounceTime(300), throttleTime(this.duration))
.subscribe((e: MouseEvent) => {
this.enterCall.emit(e);
});
}
\
\
@HostListener('keyup.Enter', ['$event'])
enterEvent(event: MouseEvent) {
event.preventDefault(); // 通常是不需要冒泡的
event.stopPropagation();
this.$sub.next(event);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
auto-focus
@Directive({
selector: '[appAutoFocus]'
})
export class AutoFocusDirective implements AfterContentInit {
constructor(private el: ElementRef) {}
ngAfterContentInit(): void {
setTimeout(() => {
this.el.nativeElement.focus();
}, 500);
}
}
detect-scroll
import { Directive, HostListener, Output, EventEmitter, Input } from '@angular/core';
export interface ScrollEvent {
isReachingBottom: boolean;
isReachingTop: boolean;
originalEvent: Event;
isWindowEvent: boolean;
}
declare const window: Window;
@Directive({
selector: '[detectScroll]'
})
export class DetectScrollDirective {
@Output() public onScroll = new EventEmitter<ScrollEvent>();
@Input() public bottomOffset = 10;
@Input() public topOffset = 10;
constructor() {}
// handle host scroll
@HostListener('scroll', ['$event']) scrolled($event: Event) {
this.elementScrollEvent($event);
}
// handle window scroll
@HostListener('window:scroll', ['$event']) windowScrolled($event: Event) {
this.windowScrollEvent($event);
}
protected windowScrollEvent($event: Event) {
const target = <Document>$event.target;
const scrollTop =
window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
const isReachingTop = scrollTop < this.topOffset;
const isReachingBottom =
target.body.offsetHeight - (window.innerHeight + scrollTop) < this.bottomOffset;
const emitValue: ScrollEvent = {
isReachingBottom,
isReachingTop,
originalEvent: $event,
isWindowEvent: true
};
this.onScroll.emit(emitValue);
}
protected elementScrollEvent($event: Event) {
const target = <HTMLElement>$event.target;
const scrollPosition = target.scrollHeight - target.scrollTop;
const offsetHeight = target.offsetHeight;
const isReachingTop = target.scrollTop < this.topOffset;
const isReachingBottom = scrollPosition - offsetHeight < this.bottomOffset;
const emitValue: ScrollEvent = {
isReachingBottom,
isReachingTop,
originalEvent: $event,
isWindowEvent: false
};
this.onScroll.emit(emitValue);
}
}