ui-sortable icon indicating copy to clipboard operation
ui-sortable copied to clipboard

Problem with nested items being sorted

Open ZtheLeader opened this issue 6 years ago • 3 comments

Hi I've implemented nested sortable items setup in surveys module in my project. i.e. I can sort Individual questions AND I can create an empty group, drop a single question into it and like this I fill up the group and I can sort the groups in hierarchy as well. Like this:

screen shot 2018-06-07 at 4 09 37 pm

Here's my code:

<!-- Group type questions: -->
                        <div class="row connector group-type-question-min-height-compulsory myHandle" ng-if="question.type == allQuestionTypes.group" ui-sortable="sortableOptionsGroup" ng-model="question.questions">
                        
                            <div class="col-md-6 col-sm-6 question-overview">
                                <div class="ellipsis">
                                    <i class="fa fa-bars"></i>
                        
                                    <input type="checkbox" ng-value="0" ng-model="question._selected" />
                                    <span ng-attr-title="{{question.name}}" ng-if="question.type == allQuestionTypes.group">
                                        <b>{{question.name}}</b>
                                    </span>
                        
                                </div>
                            </div>
                            <div class="col-md-6 col-sm-6 answer-overview pull-right word-wrap">
                                <span class="badge">{{showQuestionTypeText(question.type)}}</span>
                                <button type="button" ng-if="question.type != allQuestionTypes.group" class="button btn btn-success btn-sm" ng-click="$event.stopPropagation(); editSurveyQuestion(question, $index)">
                                    {{app.translateByLocale('label_edit')}}
                                </button>
                                <button type="button" ng-if="question.type == allQuestionTypes.group" class="button btn btn-success btn-sm" ng-click="$event.stopPropagation(); openModal(question, $index)">
                                    {{app.translateByLocale('label_edit')}}
                                </button>
                                <span class="delete-icon">
                                    <button class="btn btn-sm btn-danger" ng-click="$event.stopPropagation(); removeGroup(question)">
                                        <i class="fa fa-trash"></i>
                                    </button>
                                </span>
                            </div>
                            <div class="col-md-12 col-sm-12 question-overview group-type-question-bar" ng-if="question.questions.length == 0">
                                <div class="ellipsis text-center">
                                    <i class="fa fa-info-circle"></i>
                                    <span>
                                        {{app.translateByLocale('group_questions_drag_info')}}
                                    </span>
                                </div>
                                
                            </div>
                            <div ng-repeat="subQuestion in question.questions" class="group-type-question">
                                <div class="col-md-5 col-sm-5 question-overview" style="margin-left: 34px">
                                    <div class="ellipsis">
                                        <i class="fa fa-bars myHandle"></i>
                                        <!-- <input type="checkbox" ng-value="0" ng-model="subQuestion._selected" /> -->
                                        <span ng-attr-title="{{subQuestion.text}}">
                                            {{subQuestion.text}}
                                        </span>
                                    </div>
                                </div>
                                <div class="col-md-6 col-sm-6 answer-overview pull-right word-wrap">
                                <span class="badge">{{subQuestion.predefined_mcq_type.title}}</span>
                                    <span class="badge">{{showQuestionTypeText(subQuestion.type)}}</span>
                                    <button type="button" class="button btn btn-success btn-xs" ng-click="$event.stopPropagation(); editSurveyQuestion(subQuestion, $index)">
                                        {{app.translateByLocale('label_edit')}}
                                    </button>
                                    <span class="delete-icon">
                                        <button class="btn btn-xs btn-danger" ng-click="$event.stopPropagation(); removeSurveyGroupQuestion(subQuestion, question.questions)">
                                            <i class="fa fa-trash"></i>
                                        </button>
                                    </span>
                                </div>
                                </div>
                        </div>

Related Functions from Controller:

 * Defines the options for Group type questions to be used in both Survey creation and Edit
 * @param {object} scope 
 */
    function sortableOptionsGroup(scope) {

        let sortableGroup = {
            handle: '.myHandle',
            receive: function (event, ui) {
                sortingReceivedFunction(event, ui, scope);
            },
            remove: function (event, ui) {
                sortingRemovedFunction(event, ui, scope);
            },
            axis: 'y',
            'ui-floating': false,
            dropOnEmpty: true,
            cursor: 'move',
            tolerance: 'pointer',
            connectWith: '.connector'
        }
        return sortableGroup;
    }

    /**
     * invoked when an item is added to the main group
     * 
     * @param {obj} event 
     * @param {obj} ui 
     * @param {obj} scope 
     * 
     * @returns none
     */
    function sortingReceivedFunction(event, ui, scope) {       
        let obj = ui.item.sortable.model;
        let index = ui.item.sortable.index;
        let group_type = null;
        let invalidIncomingQuestion = false;
        
        //Group Questions Types Maintaining
        // when second and more questions are added:
        if (ui.item.sortable.droptargetModel.length >= 1){
            _.map(ui.item.sortable.droptargetModel, function (questionObject) {
                if (questionObject.type == allQuestionTypes.mcq && ui.item.sortable.model.type == allQuestionTypes.mcq && (questionObject.predefined_mcq_type.key != ui.item.sortable.model.predefined_mcq_type.key)) {
                    invalidIncomingQuestion = true;
                }
            });
            if(invalidIncomingQuestion){
                showErrorMessage(localeService.translateByLocale("group_question_error4"));
                cancelDraging(index, obj, scope, ui);
                return;
            }
            else if (ui.item.sortable.droptargetModel.length == maxQuestionLimitInGroup) {
                showErrorMessage(localeService.translateByLocale("group_question_error3"));
                cancelDraging(index, obj, scope, ui);
            }
            else if (incomingMcqQuestionValidation(ui.item.sortable.model)) {
                cancelDraging(index, obj, scope, ui);
            }
            else if (incomingCustomQuestionValidation(ui.item.sortable.model)){
                cancelDraging(index, obj, scope, ui);
            }
            else {
                let groupQuestionId = ui.item.sortable.model.id;
                scope.questions.data.forEach(function (item, i, val) {
                    if (scope.questions.data[i].id == groupQuestionId) {
                        scope.questions.data[i].isSelected = false;
                    }
                });
            }

        }
        // when first question is added:
        else if (incomingMcqQuestionValidation(ui.item.sortable.model)) {
            cancelDraging(index, obj, scope, ui);
        }
        else if (incomingCustomQuestionValidation(ui.item.sortable.model)){
            cancelDraging(index, obj, scope, ui);
        }
        else {
            let groupQuestionId = ui.item.sortable.model.id;
            scope.questions.data.forEach(function (item, i, val) {
                if (scope.questions.data[i].id == groupQuestionId) {
                    scope.questions.data[i].isSelected = false;
                }
            });
        }
    }
    /**
     * invoked when an item is removed from the main group 
     * 
     * @param {obj} event 
     * @param {obj} ui 
     * @param {obj} scope 
     * 
     * @returns none
     */
    function sortingRemovedFunction(event, ui, scope) {
        let groupQuestionId = ui.item.sortable.model.id;
        scope.questions.data.forEach(function (item, i, val) {
            if (scope.questions.data[i].id == groupQuestionId) {
                scope.questions.data[i].isSelected = true;
            }
        });
    }

Things are working pretty fine but there are few problems.

1- When there are two adjacent groups and I try to drag an individual question into upper group, it always gets added into the lower/second group.

See in the image: I added here is the text for question in group 1 but it added in group 2. Model of group 1 is in console screen shot 2018-06-07 at 4 23 56 pm

2- I can't sort the questions into a group.

Can someone help me here on it. I've been trying quite hard on it.

ZtheLeader avatar Jun 07 '18 12:06 ZtheLeader

Wow that's a lot of code... My first question will be whether you checked the Tree with dynamic template example found in README. Are you defining the ui-sortable on each sub-sortable tree?

thgreasi avatar Jun 07 '18 12:06 thgreasi

1- I checked that and that was too complex to understand. Also all of my module is working fine except these two bugs so don't wan't to replace or modify all the code base. What I think is I'm missing some little thing which is causing all this.

2- Yes. as group are added dynamically into the parent sortable array so I put a separate ui-sortable for sub-sortable trees as well. So there are 2 ui-sortable. One for parent array and one for sub-sortable array.

ZtheLeader avatar Jun 25 '18 07:06 ZtheLeader

What version are you using? Can you track by id or something in your ng-repeats? Can you share a bit more info about the top level question's code?

thgreasi avatar Jun 25 '18 13:06 thgreasi