Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@zamber
Last active February 23, 2018 03:11
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zamber/f2d15337c245285d498d9a6b94de3117 to your computer and use it in GitHub Desktop.
Save zamber/f2d15337c245285d498d9a6b94de3117 to your computer and use it in GitHub Desktop.
Angular 4 contenteditable directive
/*
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