cg-contenteditable, un directive AngularJS pour gérer l'attribut contenteditable
Cela fait maintenant deux ans et demi que je travaille avec AngularJS comme framework pour tous mes projets front. Au fil des projets, on amasse de plus en plus d’expériences et certains bouts de codes deviennent indispensables. Voici donc le premier article d’une série d’outils (directives, configs…) que j’intègre à presque tous mes projets AngularJS.
AngularJS gère très bien le “binding” des éléments input
, select
et textarea
grâce à la
directive ngModel
.
En revanche, pour l’attribut contenteditable
, rien n’est prévu.
Voici donc la directive cg-contenteditable
qui permet de combler ce manque (c’est
en CoffeeScript mais vous pouvez voir la version compilée dans le CodePen à la fin
de cet article) :
L’idée de cette directive est de gérer l’attribut contenteditable
de la même façon
qu’Angular gère les éléments ciblés par ngModel
. Pour être cohérent au niveau du DOM
on va donc retrouver l’attribut ng-model
qui nous servira à gérer le “double binding”
des données.
Cette directive se divise en trois parties. Dans un premier temps : (1) l’initialisation;
on ajoute l’attribut contenteditable
à l’élément. Ensuite, (2) on va écouter l’événement
input
afin de mettre à jour le model
quand on tape dans l’élément. À noter l’utilisation de
scope.$evalAsync()
qui va déclancher un “$digest cycle” pour véritablement mettre à jour
l’objet représenté par ngModel
. Enfin (3) on regarde les changements sur le ngModel
avec un
$watch
pour les répercuter sur l’élément.
Pour finir, voici un petit CodePen qui montre comment on l’utilise :
angular.module('whatever', {})
angular.module('whatever').controller 'test', ($scope) ->
$scope.item =
description: null
$scope.$watch 'item.description', ->
console.log $scope.item
$scope.item.description = 'test'
angular.module('whatever').directive 'cgContenteditable', ->
restrict: 'A'
scope:
ngModel: '='
link: (scope, elem, attrs) ->
elem = elem[0]
elem.setAttribute 'contenteditable', true
elem.addEventListener 'input', ->
return if scope.ngModel is elem.innerHTML
scope.ngModel = elem.innerHTML
scope.$evalAsync()
scope.$watch 'ngModel', ->
return unless scope.ngModel
return if scope.ngModel is elem.innerHTML
elem.innerHTML = scope.ngModel