angular-ui / ui-tinymce

AngularUI wrapper for TinyMCE
MIT License
488 stars 371 forks source link

ui-tinymce directive used into custom directive template : ngModel not updated #171

Closed klu00 closed 8 years ago

klu00 commented 8 years ago

Hi,

I currently have an issue when I call ui-tinymce directive in a custom directive. The custom directive is used to load dynamically links from backend for tinymce advlink plugin (+ load tinymce options object associated with a key passed as an attribute to the directive).

Here is my controller :

module.controller('Ctrl', function ($scope) {
    $scope.test = {
        val: "gfsgfdgh"
    };
});

Here is how I call the directive in HTML: <tinymce-custom type="minimal" ng-model="test.val"></tinymce-custom>

And here is my directive :

module.directive('tinymceCustom', function($location, TinyService, Module, GenerateurPage) {
    return {
        restrict: 'E',
        replace: true,
        require:"ngModel",
        scope: {
            ngModel: '='
        },
        link: function(scope, element, attrs, ngModel){

            scope.loaded = {
                modules: false,
                pages: false,
                tinymce: false
            };

            scope.tinyOptions = {};

            var link_list = [];

            var modules = [];
            var pages = [];

            Module.findByOrganisme({}, function (data) {
                data.forEach(function(module) {
                    modules.push({title: module.libelle, value: "/modules/"+module.id});
                });
                link_list.push({title: "Modules", menu: modules});

                scope.loaded.modules = true;
                initTiny();
            });

            GenerateurPage.findByOrganisme({}, function(data) {
                data.forEach(function(page) {
                    pages.push({title: page.titre, value: "/#/generateurPage/afficherPage?id=/"+page.id});
                });
                link_list.push({title: "Pages", menu: pages});

                scope.loaded.pages = true;
                initTiny();
            });

            function initTiny() {
                if (!scope.loaded.modules || !scope.loaded.pages) {
                    return false;
                }

                scope.tinyOptions = TinyService.options(attrs.type);
                console.log(scope);
                scope.tinyOptions.link_list = link_list;

                scope.loaded.tinymce = true;
            }

        },
        template: '<div ng-if="loaded.tinymce"><textarea ui-tinymce="tinyOptions" ng-model="ngModel"></textarea>'
    };
});

The problem is that the model passed to ui-tinymce directive is not updated when changing the text with the editor, and the text in the editor is not updated when the model from the controller is changed... BUT, the initial ngModel value is passed to ui-tinymce directive, so I think that is the data binding that is broken. Tried to watch it with $watch but nothing happens. I can't figure how to fix it so I'm now looking for some help...

Thx

deeg commented 8 years ago

I am seeing a similar issue. Will try to investigate further and fix.

This is happening even when I include this pull request: https://github.com/angular-ui/ui-tinymce/pull/166

klu00 commented 8 years ago

Don't know exactly what happen but here is how I fixed it :

<textarea tinymce-custom='basic' ng-model='myVar'></textarea>
return {
        restrict: 'A',
        priority:999,
        terminal:true, // prevent lower priority directives to compile after it
        scope: true,
        require: ['?ngModel'],
        link: function(scope, el, attrs) {

            // default is basic template
            var type = attrs.tinymceCustom ? attrs.tinymceCustom : 'basic';

            function loadTinyOptions(name) {
                var loaded = {
                    modules: false,
                    pages: false,
                    tinymce: false
                };

                var link_list = [];

                var deferred = $q.defer();

                var initTiny = function() {
                    if (!loaded.modules || !loaded.pages) {
                        return false;
                    }

                    var tinyOptions = TinyService.options(name);
                    tinyOptions.link_list = link_list;

                    deferred.resolve(tinyOptions);
                };

                Module.findByOrganisme({}, function (data) {
                    var modules = [];

                    data.forEach(function(module) {
                        modules.push({title: module.libelle, value: "/modules/"+module.id});
                    });
                    link_list.push({title: "Modules", menu: modules});

                    loaded.modules = true;
                    initTiny();
                });

                GenerateurPage.findByOrganisme({}, function(data) {
                    var pages = [];
                    data.forEach(function(page) {
                        pages.push({title: page.titre, value: "/#/generateurPage/afficherPage?id=/"+page.id});
                    });
                    link_list.push({title: "Pages", menu: pages});

                    loaded.pages = true;

                    initTiny();
                });

                return deferred.promise;
            }

            loadTinyOptions(type).then(function(data) {
                scope._tinyOptions = data;
                el.removeAttr('tinymce-custom'); // necessary to avoid infinite compile loop
                el.attr('ui-tinymce', '{{_tinyOptions}}');
                $compile(el)(scope);
            });
        }
    };
deeg commented 8 years ago

@klu00, I looked into this more, and I'm not sure it is actually a bug.

I realized that when using ng-model with ui-tinymce, you need to pass a value which contains a '.'

ng-model="data.content"

Since the directive is creating a child scope, if you put a primitive scope variable in there, the two way data binding is lost. This article explains it well.

I would try changing out ng-model="ngModel" to something with a '.' in it.

I suspect many of the ng-model issues listed against this library have to do with this. I'm going to go through them one by one and try to find out. This is a known angular implementation pitfall.

I will update the documentation to include some information about this so less people (including myself) fall into this trap.

If you are still having issues after trying what I suggested, please attach Plunker or Fiddle re-creating it so we can look into fixing it.

klu00 commented 8 years ago

@deeg thx for the answer, finally I changed the approach as you can see in my previous post, but I think you are right, that's probably the ng-model with a primitive type and not an object that is causing the error.