Angular:如何在模型更新后重新渲染已编译的模板?

时间:2013-08-22 13:36:23

标签: angularjs angularjs-directive

我正在处理一个生成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,但我不知道如何解决它。

请你帮帮我吗?

谢谢

1 个答案:

答案 0 :(得分:1)

我只看到你设置了隐藏的&#39;在链接函数中基于该规则的元素上的类(在是/否之后)。这只是为DOM元素调用一次 - 首次创建时。更新数据模型不会重新创建元素,它会在适当的位置更新它。如果你想这样做,你需要一种能够重新创建它的机制。

我认为有三种方法可以做到这一点:

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

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

  3. 如果你的用例实际上就是这么简单(如果这不是你想要做的更复杂的事情)你可以用CSS来做这样的事情。像

    这样的简单规则
    .yes-no-question + .text-paragraph { display: none; }
    

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