Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

model change handler is not executed after sorting or nesting items #13

Open
zahycs opened this issue Aug 18, 2015 · 6 comments
Open

model change handler is not executed after sorting or nesting items #13

zahycs opened this issue Aug 18, 2015 · 6 comments

Comments

@zahycs
Copy link

zahycs commented Aug 18, 2015

Hello
after nesting or sorting items by dragging them ,I'm not able to add more items to ng-nestable,

i.e after this handler get executed , the model will never change , so the html is not rebuilt
root.on('change', function () {
$ngModel.$setViewValue(root.nestable('serialize'));
$scope && $scope.$root && $scope.$root.$$phase || $scope.$apply();
}

Thanks

@malevolm
Copy link

malevolm commented Nov 4, 2015

I'm having the same problem. Did you end up fixing it yourself?

@zahycs
Copy link
Author

zahycs commented Nov 5, 2015

Hello @malevolm

I tried to figure out how did I solve it , but I could not remember , but the version is working fine for me.

Please note that I've added a handle to the dragable element you may need to include this class in your CSS file

.dd3-item > button {
margin-left: 40px;
}
.dd3-handle {
position: absolute;
margin: 0;
left: 0;
top: 0;
cursor: pointer;
width: 40px;
text-indent: 100%;
white-space: nowrap;
overflow: hidden;
border: 1px solid #ebebeb;
background: #fff;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.dd3-handle:before {
content: '≡';
display: block;
position: absolute;
left: 0;
top: 10px;
width: 100%;
text-align: center;
text-indent: 0;
color: #ccc;
font-size: 20px;
font-weight: normal;
}
.dd3-handle:hover {
background: #f7f7f7;
}

/**
 * Angular nestable 0.0.1
 * Copyright (c) 2014 Kamil Pekala
 * https://github.com/kamilkp/ng-nestable
 */

/**
 * Sample output HTML
    <div class="dd">
        <ol class="dd-list">
            <li class="dd-item" data-id="1">
                <!-- item element -->
            </li>
            <li class="dd-item" data-id="2">
                <!-- item element -->
            </li>
            <li class="dd-item" data-id="3">
                <!-- item element -->
                <ol class="dd-list">
                    <li class="dd-item" data-id="4">
                        <!-- item element -->
                    </li>
                    <li class="dd-item" data-id="5">
                        <!-- item element -->
                    </li>
                </ol>
            </li>
        </ol>
    </div>
 */

/**
 * Sample model object
    [
        {
            item: {},
            children: []
        },
        {
            item: {},
            children: [
                {
                    item: {},
                    children: []
                }
            ]
        },
        {
            item: {},
            children: []
        }
    ]
 */

;(function(window, document, angular, undefined){
    angular.module('ng-nestable', [])
        .provider('$nestable', function(){
            var modelName = '$item';
            var defaultOptions = {};

            this.$get = function(){
                return {
                    modelName: modelName,
                    defaultOptions: defaultOptions
                };
            };

            /**
             * Method to set model variable for nestable elements
             * @param  {[string]} value
             */
            this.modelName = function(value){
                modelName = value;
            };

            /**
             * Method to set default nestable options
             * @param  {[object]} value
             * You can change the follow options:

                maxDepth        : number of levels an item can be nested (default 5)
                group           : group ID to allow dragging between lists (default 0)

                listNodeName    : The HTML element to create for lists (default 'ol')
                itemNodeName    : The HTML element to create for list items (default 'li')
                rootClass       : The class of the root element .nestable() was used on (default 'dd')
                listClass       : The class of all list elements (default 'dd-list')
                itemClass       : The class of all list item elements (default 'dd-item')
                dragClass       : The class applied to the list element that is being dragged (default 'dd-dragel')
                handleClass     : The class of the content element inside each list item (default 'dd-handle')
                collapsedClass  : The class applied to lists that have been collapsed (default 'dd-collapsed')
                placeClass      : The class of the placeholder element (default 'dd-placeholder')
                emptyClass      : The class used for empty list placeholder elements (default 'dd-empty')
                expandBtnHTML   : The HTML text used to generate a list item expand button (default '<button data-action="expand">Expand></button>')
                collapseBtnHTML : The HTML text used to generate a list item collapse button (default '<button data-action="collapse">Collapse</button>')

             */
            this.defaultOptions = function(value){
                defaultOptions = value;
            };
        })
        .directive('ngNestable', ['$compile', '$nestable', function($compile, $nestable){
            return {
                restrict: 'A',
                require: 'ngModel',
                compile: function(element){
                    var itemTemplate = element.html();
                    element.empty();
                    return function($scope, $element, $attrs, $ngModel){
                        var options = $.extend(
                            { maxDepth: 2 },
                            $nestable.defaultOptions,
                            $scope.$eval($attrs.ngNestable)
                        );
                        $scope.$watchCollection(function(){
                            return $ngModel.$modelValue;
                        }, function(model){
                            if(model){

                                /**
                                 * we are running the formatters here instead of watching on $viewValue because our model is an Array
                                 * and angularjs ngModel watcher watches for "shallow" changes and otherwise the possible formatters wouldn't
                                 * get executed
                                 */
                                model = runFormatters(model, $ngModel);
                                // TODO: optimize as rebuilding is not necessary here
                                var root = buildNestableHtml(model, itemTemplate);
                                $element.empty().append(root);
                                $compile(root)($scope);
                                root.nestable(options);
                                root.on('change', function(){
                                    $ngModel.$setViewValue(root.nestable('serialize'));
                                    $scope && $scope.$root && $scope.$root.$$phase || $scope.$apply();
                                });
                            }
                        });
                    };
                },
                controller: angular.noop
            };

            function buildNestableHtml(model, tpl){
                var root = $('<div class="dd"></div>');
                var rootList = $('<ol class="dd-list"></ol>').appendTo(root);
                model.forEach(function f(item){
                    var list = Array.prototype.slice.call(arguments).slice(-1)[0];
                    if(!(list instanceof $)) list = rootList;

                    var listItem = $('<li class="dd-item dd3-item"></li>');
                    var listHandle = $('<div  class="dd-handle dd3-handle">Drag</div>')
                    var listElement = $('<div ng-nestable-item class="dd3-content"></div>');
                    listHandle.appendTo(listItem);
                    listElement.append(tpl).appendTo(listItem);
                    list.append(listItem);
                    listItem.data('item', item.item);
                    if(isArray(item.children) && item.children.length > 0){
                        var subRoot = $('<ol class="dd-list"></ol>').appendTo(listItem);
                        item.children.forEach(function(item){
                            f.apply(this, Array.prototype.slice.call(arguments).concat([subRoot]));
                        });
                    }
                });

                return root;
            }

            function isArray(arr){
                return Object.prototype.toString.call(arr) === '[object Array]';
            }

            function runFormatters(value, ctrl){
                var formatters = ctrl.$formatters,
                idx = formatters.length;

                ctrl.$modelValue = value;
                while(idx--) {
                    value = formatters[idx](value);
                }

                return value;
            }
    }])
    .directive('ngNestableItem', ['$nestable', function($nestable){
        return {
            scope: true,
            require: '^ngNestable',
            link: function($scope, $element){
                $scope[$nestable.modelName] = $element.parent().data('item');
            }
        };
    }]);
})(window, document, window.angular);

@AbdoDabbas
Copy link

I just changed $watchCollection to $watch and passed the third parameter as true.

@duylddev
Copy link

I tried both of ways. but nothing changed :(
does anyone have absolute answer?

@cation
Copy link

cation commented Dec 5, 2016

You will notice above
if(model && element.is(':empty') is now if (model) to allow all changes to run the formatters once you have updated the array.

@softwarenacho
Copy link

Is there an official solution?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants