Как создать входные данные, в которых значение отображения и модели отличаются?

#angular #directive

#angular #директива

Вопрос:

Я пытаюсь создать входные данные, которые отображают формат времени, но содержат значение в минутах. Например: 1:30 содержит значение 90.

Моим первым вариантом было использовать директиву. Теперь, если я изменю значение во входных данных на 75, отображаемое значение должно быть 1: 15. Я предпочитаю не использовать @Output и EventEmitter и настроить метод для изменения моего значения.

Компонент:

 public value = 90;
  

Вид:

 <p><input [(appTimespan)]="value"></p>
<p>Value: {{ value }}</p>
  

Директива (snip):

 @Directive({ selector: '[appTimespan]'})
export class TimespanDirective {
    @Input( 'appTimespan' ) value: string;
    // @Output( 'out' ) update = new EventEmitter();

    constructor(private element: ElementRef) {
        // just to indicate the directive is applied
        element.nativeElement.style.backroundColor = 'yellow';
    }

    @HostListener('change') onChange() {
        // skip the code for calculations and formatting
        this.value = 75;
        this.element.nativeElement.value = '1:15';
        // this.update.emit(75);
    }
}
  

(Отображаемое) значение моего ввода установлено на ‘1: 15’, отлично…
Но значение, отображаемое в представлении, по-прежнему показывает мое начальное значение 90, где я ожидаю, что оно будет 75.

Комментарии:

1. вы хотите отобразить форматированное время в самом вводе или в отдельном div? например, в: <p>Value: {{ value }}</p> Как пользователь собирается изменять такие значения, как: 1:15, 1,16, etc или 90,91,etc ?

2. Если это только w.r.t для отображения, создание простого канала для отображения вычисленного значения должно сработать.

3. Мне нужно значение в компоненте. Я показываю только для отладки. Пользователь может вводить такие значения, как ‘0:45′, ’30’ или даже ‘0.25’. Вычисления не проблема. Пользовательский ввод должен храниться в значении поля компонента в виде числа, а timeformat должен быть «значением» ввода.

Ответ №1:

Мне нравится использовать @HotListener размытие и фокусировку, см. stackblitz

В режиме размытия вы преобразуете значение HTMLElement. В фокусе проанализируйте значение. Будьте осторожны, вы должны использовать ngOnInit, чтобы с первого раза отобразить правильное значение. Если мы используем директиву input [(ngModel)], мы должны использовать setTimeout -иначе в первый раз не будет отображаться правильное значение-

 export class TestPipe  implements OnInit {

  private el: any;
  constructor(private elementRef: ElementRef) {
    this.el = this.elementRef.nativeElement;
  }
  @HostListener("focus", ["$event.target.value"])
  onFocus() {
    this.el.value = this.parse(this.el.value); 
  }

  @HostListener("blur", ["$event.target.value"])
  onBlur(value) {
    this.el.value = this.transform(value);
  }

  ngOnInit()
  {
    setTimeout(()=>{
      this.el.value = this.transform(this.el.value);
    })

  }
  transform(value:any)
  {
      let minutes= value;
      let hour=Math.floor(minutes/60);
      let rest=minutes%60;
      return hour ":" ('00' rest).slice(-2);
  }
  parse(value:any)
  {
    let hours=value.split(":");
    return '' (( hours[0])*60 ( hours[1]));
  }
}
  

Комментарии:

1. Спасибо. У меня было решение AngularJS, похожее на это. Там я использовал форматеры и анализаторы ngmodel в директиве. Я ищу способ вернуть значение модели в виде числа и отобразить значение на входе в виде строки времени.

Ответ №2:

Вы можете просто использовать событие ввода и вычислять время каждый раз, когда вы добавляете / удаляете число

HTML:

 <input type="number" [(ngModel)]="value"  (keyup)="changeDisplayValue()" />
<p>Value: {{ displayValue }}</p>
  

TS:

  export class AppComponent  {
 public value: number = 90;  
 displayValue: any;

constructor(){
 this.displayValue = this.convertTime(this.value);
}

changeDisplayValue(){
  this.displayValue = this.convertTime(this.value);
}

convertTime(minutesValue) {
  let h = Math.floor(minutesValue / 60);
  let m = minutesValue % 60;  
  return (h < 10 ? '0'   h : h)   ':'   (m < 10 ? '0'   m : m);
}
}
  

Таким образом, вам не нужен дополнительный код, и значение обновляется в тот момент, когда вы его меняете.

Комментарии:

1. Tnx для вашего ввода, но…. Мне действительно нужна (легко повторно используемая) директива / компонент. Таким образом, получается много повторяющегося кода.

Ответ №3:

Вы можете использовать канал для отображения минут в формате часов.

Демонстрация StackBlitz

Компонент:

 public value: number = 90;
  

Вид:

 <input type="number" [(ngModel)]="value" />
<p>Value: {{ value | toTime }}</p>
  

Канал:

 import { Pipe, PipeTransform } from '@angular/core';

@Pipe({name: 'toTime'})
export class ToTime implements PipeTransform {

  convertMinsToHrsMins(minutes) {
    let h:any = Math.floor(minutes / 60);
    let m:any = minutes % 60;
    h = h < 10 ? '0'   h : h;
    m = m < 10 ? '0'   m : m;
    return h   ':'   m;
  }

  transform(value: number): string {
    return this.convertMinsToHrsMins(value);
  }
}
  

Редактировать
Вы можете использовать один и тот же канал в компоненте для получения часов

 submit(){
  console.log(this.toTime.transform(this.value))
}
  

Комментарии:

1. Tnx, но проблема НЕ в форматировании. Мне нужно значение в компоненте.

2. @Elko Вот для чего нужны каналы. Канал в основном используется для преобразования значения в представлении, а не в компоненте

3. @Elko Вы можете использовать один и тот же канал в компоненте, подобном this.toTime.transform(this.value) . Я обновил демонстрацию stackblitz, пожалуйста, проверьте.

4. Tnx. Опять же, вычисления и форматирование не являются проблемой. Цель состоит в том, чтобы преобразовать обычный текстовый ввод во ввод временного интервала, где значение, отображаемое на входе (после изменения), является форматом времени, а значение модели — в минутах (число).

5. @Javascript Hupp Technologies Да! Точно. Значение модели в минутах и input.value представляют собой строку.