Last active
February 23, 2018 03:11
-
-
Save zamber/f2d15337c245285d498d9a6b94de3117 to your computer and use it in GitHub Desktop.
Angular 4 contenteditable directive
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
based on https://stackoverflow.com/a/40183067/1225741 | |
tested with angular 4.0.3 | |
in your *.component.html: | |
<span #mycomponentvarthingy [appContenteditableModel]="mycomponentvarthingy" | |
(appContenteditableModelChange)="myValueChangeEventHandler($event)"></span> | |
// to trigger editing just set contenteditable to true on the target element ie. | |
@ViewChild('mycomponentvarthingy') mycomponentvarthingy: ElementRef; | |
// func called with (click) on <a> | |
editMyComponentVarThingy(e) { | |
e.preventDefault(); | |
this.mycomponentvarthingy.nativeElement.setAttribute('contenteditable', 'true'); | |
this.mycomponentvarthingy.nativeElement.focus(); | |
} | |
myValueChangeEventHandler(e) { | |
console.log('got val from contenteditable', e); | |
} | |
differs functionally from the SO implementation: | |
- esc for cancel | |
- enter for accept - calls event handler | |
- click out of bounds - calls event handler | |
- doesn't call event handler if the value did not change | |
*/ | |
import {Directive, ElementRef, Input, Output, | |
EventEmitter, OnChanges, OnInit, HostListener } from '@angular/core'; | |
@Directive({ selector: '[appContenteditableModel]' }) | |
export class ContentEditableDirective implements OnChanges, OnInit { | |
origModel: string; | |
/* tslint:disable: no-input-rename no-output-rename */ | |
// couldn't find a better way to implement this | |
@Input('appContenteditableModel') model: any; | |
@Output('appContenteditableModelChange') update = new EventEmitter(); | |
getText = () => this.elementRef.nativeElement.innerText; | |
constructor( private elementRef: ElementRef ) { } | |
ngOnInit() { this.origModel = this.getText() } | |
ngOnChanges(changes) { | |
// console.log(changes); | |
if (changes.model.isFirstChange()) { this.refreshView() } | |
} | |
setContentEditable(b: boolean) { | |
this.elementRef.nativeElement.setAttribute('contenteditable', b); | |
} | |
@HostListener('blur') | |
emitChange() { // focus lost, emit if changed | |
this.setContentEditable(false); | |
const t = this.getText(); | |
if (this.origModel !== t) { this.update.emit(t) } | |
this.origModel = t; | |
} | |
@HostListener('keydown', ['$event']) | |
onKeydown(e) { | |
// console.log('ContentEditableDirective.onEdit'); | |
if (e.which === 13) { // enter | |
e.preventDefault(); | |
this.emitChange(); | |
} else if (e.which === 27) { // esc | |
e.preventDefault(); | |
this.refreshView(); | |
this.setContentEditable(false); | |
} | |
} | |
private refreshView() { | |
this.elementRef.nativeElement.innerText = this.model; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment