0

我正在开发一个生成 json 的角度表单构建器。除了一件事,一切都很好。

你可以在这里找到一个例子:http: //jsfiddle.net/dJRS5/8/

HTML:

<div ng-app='app'>
    <div class='formBuilderWrapper' id='builderDiv' ng-controller="FormBuilderCtrl" >
        <div class='configArea' data-ng-controller="elementDrag">    
            <h2>drag/drop</h2>
            <form name="form" novalidate class='editBloc'>
                <div data-ng-repeat="field in fields" class='inputEdit'>
                    <data-ng-switch on="field.type">
                        <div class='labelOrder' ng-class='{column : !$last}' drag="$index" dragStyle="columnDrag" drop="$index" dropStyle="columnDrop">{{field.type}}
                        </div>
                        <label for="{{field.name}}" data-ng-bind-html-unsafe="field.caption"></label>

                        <input data-ng-switch-when="Text" type="text" placeholder="{{field.placeholder}}" data-ng-model="field.value" />

                        <p data-ng-switch-when="Text/paragraph" data-ng-model="field.value" data-ng-bind-html-unsafe="field.paragraph"></p>

                            <span data-ng-switch-when="Yes/no question">
                                <p data-ng-bind-html-unsafe="field.yesNoQuestion"></p>
                                <input type='radio' name="yesNoQuestion" id="yesNoQuestion_yes" value="yesNoQuestion_yes" />
                                <label for="yesNoQuestion_yes">Oui</label>
                                <input type='radio' name="yesNoQuestion" id="yesNoQuestion_no" value="yesNoQuestion_no"/>
                                <label for="yesNoQuestion_no">Non</label>
                            </span>     
            <p data-ng-switch-when="Submit button" class='submit' data-ng-model="field.value">
                    <input value="{{field.name}}" type="submit">
                </p>                        
                    </data-ng-switch>
                </div>
            </form>
        </div>        
        <div id='previewArea' data-ng-controller="formWriterCtrl">
            <h2>preview</h2>    
            <div data-ng-repeat="item in fields" content="item" class='templating-html'></div>
        </div>        
    </div>
</div>

JS:

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

    app.controller('FormBuilderCtrl', ['$scope', function ($scope){
        $scope.fields = [{"type":"Text/paragraph","paragraph":"hello1"},{"type":"Yes/no question","yesNoQuestion":"following items must be hidden","yes":"yes","no":"no"},{"type":"Text/paragraph","paragraph":"hello2"},{"type":"Submit button","name":"last item"}]  ;
    }]);


    app.controller('elementDrag', ["$scope", "$rootScope", function($scope, $rootScope, $compile) {
        $rootScope.$on('dropEvent', function(evt, dragged, dropped) {
            if($scope.fields[dropped].type == 'submitButton' || $scope.fields[dragged].type == 'submitButton'){
                return;
            }
            var tempElement = $scope.fields[dragged];
            $scope.fields[dragged] = $scope.fields[dropped];
            $scope.fields[dropped] = tempElement;
            $scope.$apply();
        });
    }]);

    app.directive("drag", ["$rootScope", function($rootScope) {
        function dragStart(evt, element, dragStyle) {
            if(element.hasClass('column')){
                element.addClass(dragStyle);
                evt.dataTransfer.setData("id", evt.target.id);
                evt.dataTransfer.effectAllowed = 'move';
            }
        };
        function dragEnd(evt, element, dragStyle) {
            element.removeClass(dragStyle);
        };
        return {
            restrict: 'A',
            link: function(scope, element, attrs)  {
                if(scope.$last === false){
                    attrs.$set('draggable', 'true');
                    scope.dragStyle = attrs["dragstyle"];
                    element.bind('dragstart', function(evt) {
                        $rootScope.draggedElement = scope[attrs["drag"]];
                        dragStart(evt, element, scope.dragStyle);
                    });
                    element.bind('dragend', function(evt) {
                        dragEnd(evt, element, scope.dragStyle);
                    });
                }
            }
        }
    }]);

    app.directive("drop", ['$rootScope', function($rootScope) {
        function dragEnter(evt, element, dropStyle) {
            element.addClass(dropStyle);
            evt.preventDefault();
        };
        function dragLeave(evt, element, dropStyle) {
            element.removeClass(dropStyle);
        };
        function dragOver(evt) {
            evt.preventDefault();
        };
        function drop(evt, element, dropStyle) {
            evt.preventDefault();
            element.removeClass(dropStyle);
        };
        return {
            restrict: 'A',
            link: function(scope, element, attrs)  {
                if(scope.$last === false){
                    scope.dropStyle = attrs["dropstyle"];
                    element.bind('dragenter', function(evt) {
                        dragEnter(evt, element, scope.dropStyle);
                    });
                    element.bind('dragleave', function(evt) {
                        dragLeave(evt, element, scope.dropStyle);
                    });
                    element.bind('dragover', dragOver);
                    element.bind('drop', function(evt) {
                        drop(evt, element, scope.dropStyle);
                        var dropData = scope[attrs["drop"]];
                        $rootScope.$broadcast('dropEvent', $rootScope.draggedElement, dropData);
                    });
                }
            }
        }
    }]);



    app.controller('formWriterCtrl', ['$scope', function ($scope){

    }]);  


    app.directive('templatingHtml', function ($compile) {
        var previousElement;
        var previousIndex;
        var i=0;
        var inputs = {};

        var paragraphTemplate = '<p data-ng-bind-html-unsafe="content.paragraph"></p>';    
        var noYesQuestionTemplate = '<p data-ng-bind-html-unsafe="content.yesNoQuestion"></p><input id="a__index__yes" type="radio" name="a__index__"><label for="a__index__yes" />{{content.yes}}</label><input id="a__index__no" class="no" type="radio" name="a__index__" /><label for="a__index__no">{{content.no}}</label>';
        var submitTemplate = '<p class="submit"><input value="{{content.name}}" type="submit" /></p>';

        var getTemplate = function(contentType, contentReplace, contentRequired) {
            var template = '';
            switch(contentType) {
                case 'Text/paragraph':
                    template = paragraphTemplate;
                    break;
                case 'Yes/no question':
                    template = noYesQuestionTemplate;
                    break;
            case 'Submit button':
                    template = submitTemplate;
                    break;
            }

            template = template.replace(/__index__/g, i);        
            return template;
        }

        var linker = function(scope, element, attrs) {
            i++;
            elementTemplate = getTemplate(scope.content.type);
            element.html(elementTemplate);
            if(previousElement == 'Yes/no question'){
                element.children().addClass('hidden');
                element.children().addClass('noYes'+previousIndex); 
            }
            if(scope.content.type == 'Yes/no question'){
                previousElement = scope.content.type;
                previousIndex = i; 
            }
            $compile(element.contents())(scope);
        }

        return {
            restrict: "C",
            link: linker,
            scope:{
                content:'='
            }
        };
    });

在示例中有两个区域: - 第一个在 Json 上执行 ngRepeat 并允许通过拖放重新排序项目 - 第二个区域也执行 ngRepeat,它是由使用编译功能的指令模板化的预览。如果某些元素在我所说的“是/否问题”之后,它们就会被隐藏

以下是表单生成器生成的 Json 示例:

$scope.fields = 

[{"type":"Text/paragraph","paragraph":"hello1"},{"type":"Yes/no question","yesNoQuestion":"following items must be hidden","yes":"yes","no":"no"},  
 {"type":"Text/paragraph","paragraph":"hello2"},{"type":"Submit button","name":"last item"}]  ;

当页面加载一切正常时,Hello1 是可见的,Hello2 是隐藏的。

但是当我在“是/否问题”之后删除 Hello1 时,dom 元素被重新组织,但 Hello1 没有隐藏。我认为它来自 $compile 但我不知道如何解决它。

你能帮我解决这个问题吗?

谢谢

4

1 回答 1

1

我只看到您在链接函数中根据该规则(在是/否之后)在元素上设置“隐藏”类。只对 DOM 元素调用一次 - 在它第一次创建时。更新数据模型不会重新创建元素,它会在原地更新它。如果您想这样做,您将需要一个重新创建它的机制。

我看到了三种方法可以做到这一点:

  1. 在您的链接器功能中,听与dropEvent您在上面听的相同。这比你想象的更有效(它非常快),你可以重新评估是否应用这个hidden类。

  2. 使用 ngIf 之类的东西或在您的集合中重新创建它以强制完全重新创建元素。这不是那么有效,但有时由于各种原因仍然是可取的。

  3. 如果您的用例实际上如此简单(如果这不是您尝试做的更复杂的事情的简化),您可以使用 CSS 来做这样的事情。一个简单的规则,如

    .yes-no-question + .text-paragraph { display: none; }
    

    使用同级目标可以直接处理此问题,而无需做太多工作。显然,这在它可以做的事情上受到了更多的限制,但如果它涵盖了您的需求,它是最有效的选择。

于 2014-07-17T14:16:23.190 回答