Ответ С.Лотта выше почти работает для меня, но в конечном итоге дает мне частичные линии. Оказывается, он искажает данные на границах блоков, поскольку данные содержат блоки чтения в обратном порядке. Когда вызывается '.join (data), блоки находятся в неправильном порядке. Это исправляет это.
def tail(f, window=20):
"""
Returns the last `window` lines of file `f` as a list.
"""
if window == 0:
return []
BUFSIZ = 1024
f.seek(0, 2)
bytes = f.tell()
size = window + 1
block = -1
data = []
while size > 0 and bytes > 0:
if bytes - BUFSIZ > 0:
# Seek back one whole BUFSIZ
f.seek(block * BUFSIZ, 2)
# read BUFFER
data.insert(0, f.read(BUFSIZ))
else:
# file too small, start from begining
f.seek(0,0)
# only read what was not read
data.insert(0, f.read(bytes))
linesFound = data[0].count('\n')
size -= linesFound
bytes -= BUFSIZ
block -= 1
return ''.join(data).splitlines()[-window:]
правильный способ - использовать сеттер. Функция установки вызывается каждый раз, когда изменяется вход.
@Input() set appInputmaxLength(value:string){
// Your code here
console.log(value);
}
Пример можно найти здесь: https://angular.io/guide/structural-directives (директива You не является структурной директивой, но она в этом примере)
Вот демонстрация Stackblitz директивы
Я внес несколько изменений в ваш код, вот что я предлагаю:
[ 1143]appInputMaxLength
на номер Renderer2
настолько, насколько это возможно, для кросс-платформенной совместимости. div
для хранения вашего div и обновите его позже, создайте его с помощью this.renderer.createElement('div')
this.renderer.insertBefore(this.el.nativeElement.parentNode, this.div, this.el.nativeElement.nextSibling)
, чтобы вставить его после хоста input
, получите значение из события, затем получите его длину и обновите div
currentValue
, просто получите длину из входное значение или событие this.renderer.setProperty(this.div, 'innerText', ...);
для обновления текста вашего элемента div this.renderer.removeChild(this.el.nativeElement.parent, this.div)
, так как ngOnDestroy
вызывается после удаления DOM, и ссылка parent
будет нулевой. Вы должны напрямую позвонить this.div.remove()
( см. Этот выпуск Github ).
import { AfterViewInit, Directive, ElementRef, HostListener, Input, OnInit, Renderer2, OnDestroy } from '@angular/core';
@Directive({
selector: '[appInputMaxLength]'
})
export class InputMaxLengthDirective implements OnInit, AfterViewInit, OnDestroy {
@Input() appInputMaxLength: number;
private div: HTMLDivElement;
constructor(private el: ElementRef, private renderer: Renderer2) {}
@HostListener('input', ['$event']) onChange(event) {
this.update(event.target.value.length);
}
ngOnInit() {
this.renderer.setAttribute(this.el.nativeElement, 'maxLength', this.appInputMaxLength.toString());
}
ngOnDestroy() {
if (this.div) {
this.div.remove();
}
}
ngAfterViewInit() {
this.div = this.renderer.createElement('div');
this.renderer.insertBefore(this.el.nativeElement.parentNode, this.div, this.el.nativeElement.nextSibling);
this.update(this.el.nativeElement.value.length);
}
private update(length: number) {
this.renderer.setProperty(this.div, 'innerText', `${length} / ${this.appInputMaxLength}`);
}
}
Используйте его следующим образом со значением ввода числа:
<input type="text" [appInputMaxLength]="10">
Если вы хотите, чтобы ваша директива работала, когда ngModel
привязан к входу, и обновляйте соответственно, если модель изменяется Вы можете получить инъекцию хоста ngModel
и затем подписаться на его valueChange
наблюдаемый:
import { AfterViewInit, Directive, ElementRef, HostListener, Input, OnInit, Renderer2, Optional, OnDestroy } from '@angular/core';
import { NgModel } from '@angular/forms';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Directive({
selector: '[appInputMaxLength]'
})
export class InputMaxLengthDirective implements OnInit, AfterViewInit, OnDestroy {
@Input() appInputMaxLength: number;
private div: HTMLDivElement;
private destroyed$ = new Subject();
constructor(private el: ElementRef, private renderer: Renderer2, @Optional() private ngModel: NgModel) {}
@HostListener('input', ['$event']) onChange(event) {
if (!this.ngModel) {
this.update(event.target.value.length);
}
}
ngOnInit() {
this.renderer.setAttribute(this.el.nativeElement, 'maxLength', this.appInputMaxLength.toString());
if (this.ngModel) {
this.ngModel.valueChanges.pipe(takeUntil(this.destroyed$)).subscribe(value => {
this.update(value.length);
})
}
}
ngAfterViewInit() {
this.div = this.renderer.createElement('div');
this.renderer.insertBefore(this.el.nativeElement.parentNode, this.div, this.el.nativeElement.nextSibling);
this.update(this.el.nativeElement.value.length);
}
ngOnDestroy() {
this.destroyed$.next();
this.destroyed$.complete();
if (this.div) {
this.div.remove();
}
}
private update(length: number) {
this.renderer.setProperty(this.div, 'innerText', `${length} / ${this.appInputMaxLength}`);
}
}
Затем вы можете использовать вашу директиву на входе с ngModel
:
попробуйте этот код, я переписал некоторые коды
import {
AfterViewInit,
Directive,
ElementRef,
HostListener,
Input,
OnInit,
Renderer2,
OnDestroy
} from '@angular/core';
@Directive({
selector: '[appInputmaxLength]'
})
export class InputmaxLengthDirective implements OnInit, AfterViewInit, OnDestroy {
@Input() appInputmaxLength: string;
private currentValue = 0;
countDiv: HTMLDivElement;
parent: any;
constructor(private el: ElementRef<HTMLInputElement>, private renderer: Renderer2) {}
@HostListener('keyup') isChange() {
const countNb = this.el.nativeElement.value.length + 1;
if (countNb <= 1) {
this.currentValue = 0;
this.updateCount();
} else {
this.currentValue = countNb;
this.updateCount();
}
console.log('test: ', this.el.nativeElement.value.length + 1);
}
ngOnInit() {
this.renderer.setAttribute(this.el.nativeElement, 'maxLength', this.appInputmaxLength);
}
ngOnDestroy() {
this.renderer.removeChild(this.parent, this.countDiv);
this.renderer.destroyNode(this.countDiv);
}
updateCount() {
this.countDiv.innerText = this.currentValue + ' / ' + this.appInputmaxLength;
}
ngAfterViewInit() {
this.countDiv = this.renderer.createElement('div');
this.parent = this.renderer.parentNode(this.el.nativeElement);
this.renderer.appendChild(parent, this.countDiv);
this.updateCount();
}
}
в этом коде, я использую рендерер для создания элемента div и обновления его значения везде, где изменяется currentValue
.
Также вы должны уничтожить его при уничтожении директивы, чтобы избежать утечки памяти.