web-dev-qa-db-fra.com

Mise à jour de la valeur textarea avec le contenu de CKEditor dans Angular JS

J'utilise la dernière version de CKEditor (version standard) et, partant de cette question , j'ai mis en œuvre une directive angulaire comme celle-ci,

var cmsPlus = angular.module('cmsPlus', []);

cmsPlus.directive('ckEditor', function() {
  return {
    require: '?ngModel',
    link: function(scope, Elm, attr, ngModel) {
      var ck = CKEDITOR.replace(Elm[0]);

      if (!ngModel) return;

      ck.on('pasteState', function() {
        scope.$apply(function() {
          ngModel.$setViewValue(ck.getData());
        });
      });

      ngModel.$render = function(value) {
        ck.setData(ngModel.$viewValue);
      };
    }
  };
});

Cela fonctionne bien lorsque je tape quelque chose dans le mode d'interface graphique CKEditor. Ici, je suis en train de transférer le contenu saisi au modèle ng de textarea. 

Mais lorsque je passe à l'éditeur de code, le contenu mis à jour ne s'affiche pas, même après le retour à l'interface graphique. Il est nécessaire de taper à nouveau quelque chose en mode graphique.

Quel est le problème avec ma directive? Ou puis-je étendre cette directive avec d'autres événements CKEditor?

Je veux ajouter quelques événements supplémentaires pour la soumission de formulaire ou autre chose.

Démo ici.

14
devo

Votre directive fonctionne bien.

Il existe un plugin appelé sourcearea qui contrôle le comportement de CKEditor sur mode source. Je ne voyais aucun événement se déclencher à l'intérieur du code de ce plugin pour gérer les entrées. Il existe cependant deux événements que nous pouvons utiliser pour intercepter le retour de CKEditor à Mode GUI. Les événements sont ariaWidget et dataReady .

J'ai mis à jour votre exemple pour utiliser l'événement dataReady , afin qu'il mette à jour la zone de texte lors du retour. J'ai également changé l'événement pasteState en change , comme Dan Caragea a dit il a été introduit dans la version 4.2. Le violon mis à jour est disponible ici

Une solution quasiment trouvée que j'ai trouvée est d'écouter l'événement key et de mettre à jour le modèle. Il est presque là, car il semble que l'événement est déclenché uniquement pour l'ancienne touche enfoncée. Donc, la dernière clé est toujours manquante.

var cmsPlus = angular.module('cmsPlus', []);

cmsPlus.directive('ckEditor', function() {
  return {
    require: '?ngModel',
    link: function(scope, Elm, attr, ngModel) {
      var ck = CKEDITOR.replace(Elm[0]);

      if (!ngModel) return;

      ck.on('instanceReady', function() {
        ck.setData(ngModel.$viewValue);
      });

      function updateModel() {
          scope.$apply(function() {
              ngModel.$setViewValue(ck.getData());
          });
      }

      ck.on('change', updateModel);
      ck.on('key', updateModel);
      ck.on('dataReady', updateModel);

      ngModel.$render = function(value) {
        ck.setData(ngModel.$viewValue);
      };
    }
  };
});

Quoi qu'il en soit, vous pourrez peut-être comprendre comment résoudre le dernier problème clé. C'est presque là!

EDIT: lien de violon mis à jour pour corriger la version

27
Jonas

Je sais que cette question a déjà reçu une réponse, mais je pensais préciser ce que je devais faire pour intégrer CKEditor 4.4.4 à angularjs 1.2. Voici mon code en coffeescript:

'use strict'

angular.module 'core', []

.directive 'ckeditor', ->
    require: '?ngModel'
    link: (scope, element, attrs, ngModel) ->
        config =
            # CKEditor config goes here

        editor = CKEDITOR.replace element[0], config

        return unless ngModel

        editor.on 'instanceReady', ->
            editor.setData ngModel.$viewValue

        updateModel = ->
            scope.$apply ->
                ngModel.$setViewValue editor.getData()

        editor.on 'change', updateModel
        editor.on 'dataReady', updateModel
        editor.on 'key', updateModel
        editor.on 'paste', updateModel
        editor.on 'selectionChange', updateModel

        ngModel.$render = ->
            editor.setData ngModel.$viewValue

Pour les illettrés de café, voici le javascript compilé:

'use strict';
angular.module('core', []).directive('ckeditor', function() {
    return {
      require: '?ngModel',
      link: function(scope, element, attrs, ngModel) {
        var config, editor, updateModel;
        config = {
            // CKEditor config goes here
        }
        editor = CKEDITOR.replace(element[0], config);
        if (!ngModel) {
          return;
        }
        editor.on('instanceReady', function() {
          return editor.setData(ngModel.$viewValue);
        });
        updateModel = function() {
          return scope.$apply(function() {
            return ngModel.$setViewValue(editor.getData());
          });
        }};
        editor.on('change', updateModel);
        editor.on('dataReady', updateModel);
        editor.on('key', updateModel);
        editor.on('paste', updateModel);
        editor.on('selectionChange', updateModel);
        return ngModel.$render = function() {
          return editor.setData(ngModel.$viewValue);
        };
      }
    };
  }
);

Puis dans le HTML:

<textarea ckeditor data-ng-model="myModel"></textarea>

Maintenant, pour une explication.

J'ai ajouté des gestionnaires de modification de la pâte et de la sélection par souci d'exhaustivité, mais il s'est avéré que la gestion de la modification de la sélection était nécessaire. J'ai découvert que si je sélectionnais tout et appuyais sur supprimer, alors, sans prendre le focus de l'éditeur, soumit le formulaire, les modifications n'étaient pas répercutées dans le modèle lors de la soumission. Le gestionnaire de changement de sélection résout ce problème.

L'intégration de CKEditor avec angularjs est une mission essentielle de mon projet. Par conséquent, si je trouve plus de «Gotchas», je mettrai à jour cette réponse.

9
Mjonir74

J'espère que le problème est similaire au mien. L'éditeur CK a ses propres éléments qu'il injecte dans le DOM et Angular est déjà restitué. Vous devrez donc configurer un écouteur pour le il passe à l'éditeur de code. Si vous n'écoutez pas le changement, alors Angular ne peut pas se lier correctement car il ignore les modifications apportées dans le DOM. Je suis tombé sur quelque chose de similaire avec tinymce et le modal qui apparaît.

Voici une autre ressource liée à mon problème

0
dasper

Pour moi, la réponse de @ Mjonir74 a fonctionné, mais dès que j’ai eu plusieurs instances de rédacteur sur une page et que je devais également prendre en compte le mode d’édition, et pas seulement le mode de création, les choses ne fonctionnaient plus correctement lorsque vous reveniez à la page contenant l'éditeur. En gros en mode édition, la première fois que vous visitiez la page, tout allait bien, le texte était dans l'éditeur comme il se doit. mais les visites successives sur la même page laissaient l'éditeur vide, pas de texte.

Voici comment cela a fonctionné pour moi:

    app.directive('appWysiwygBlock', function() {
    return {
        require: 'ngModel',
        restrict: 'E',
        templateUrl: 'modules/app/templates/directives/wysiwyg-block.html',
        scope: {
            error: '=',
            config: '='
        },
        link: function(scope, element, attrs, ngModel) {

            if (typeof CKEDITOR == 'undefined' || !ngModel) {
                return;
            }

            scope.required = attrs.required || false;
            scope.cols = attrs.cols || 6;

            scope.label = attrs.label || attrs.name;
            scope.name = attrs.name || scope.label;
            if (scope.name) {
                scope.name = scope.name.toLowerCase().replace(/[^a-z0-9]/gi, '_');
            }

            var defaultConfig, config, editor, updateModel;

            config = scope.config || {};
            defaultConfig = {
                customConfig: '/modules/app/ckeditor-config.js'
            };

            config = element.extend({}, defaultConfig, config);
            editor = CKEDITOR.replace(element.find('textarea:first')[0], config);

            updateModel = function() {
                return scope.$apply(function() {
                    return ngModel.$setViewValue(editor.getData());
                });
            };

            editor.on('instanceReady', function() {
                editor.on('change', updateModel);
                editor.on('dataReady', updateModel);
                editor.on('key', updateModel);
                editor.on('paste', updateModel);
                editor.on('selectionChange', updateModel);
                return editor.setData(ngModel.$viewValue);
            });

            return ngModel.$render = function() {
                return editor.setData(ngModel.$viewValue);
            };
        }
    };
});

et je l'utilise comme 

<app-wysiwyg-block label="Description" name="description" ng-model="item.description" error="fieldErrors.description" required="true" cols="12"></app-wysiwyg-block>

n'importe quel nombre de fois dans une page et cela fonctionne très bien dans tous les modes.

0
Twisted1919