Elko
Elko

Reputation: 21

How to create an input where display and model value differ?

I'm trying to create an input that displays a time format but holds a value in minutes. For instance: 1:30 holds a value of 90.

My first option was to use a directive. Now if I change the value in the input to 75 the value displayed should be 1:15. I prefer not to use the @Output and EventEmitter and set up a method to change my value.

Component:

public value = 90;

View:

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

Directive (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);
    }
}

The (display)value of my input is set to '1:15', great... But the value displayed in the view still shows my initial value 90 where I expect it to be 75.

Upvotes: 2

Views: 1738

Answers (3)

You can use pipe to show minutes to hours format.

StackBlitz Demo

Component:

public value: number = 90;

View:

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

Pipe:

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);
  }
}

Edit You can use same pipe in component for getting hours

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

Upvotes: 0

V Djuric
V Djuric

Reputation: 71

You can just use keyUp event, and calculate the time every time you add/delete a number

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);
}
}

This way you dont need any extra code, and the value updates the moment you change it.

Upvotes: 0

Eliseo
Eliseo

Reputation: 57909

I like use @HotListener blur and focus see stackblitz

In blur, you transform the value of the htmlElement. In Focus parse the value. Be carefully, you must use ngOnInit to show the correct value at first time. If we are using the directive in a input [(ngModel)] we must use a setTimeOut -else, the first time not show the correct value-

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]));
  }
}

Upvotes: 1

Related Questions