在DOM中移动控制器时保留元素/范围状态

sah*_*wah 6 javascript angularjs

在角度应用程序中,我想将一个元素从DOM树的一部分移动到另一部分,而不必重新加载附加到移动元素的控制器.

我创建了一个简化的plunker来说明:http://plnkr.co/edit/sqBRM3ZQ5G9xpiNd1MXm?p = preview

在这个plunker中,唯一要保留的数据是toggler状态,但实际上它可能是非常大量的数据,这可能需要花费很多精力才能初始化.

我想要做的是能够将模板1从指令1移动到指令2,但我想保留切换器的状态.在这种情况下,如果单击切换器使其变为绿色,并单击"从列表1移动到2"两次,它将移动到指令2但将颜色重置为红色.

为了避免这种情况,我可以在不通知角度的情况下移动元素,但这显然会创建一个破坏的应用程序状态.

我想过将控制器从旧范围移动到新范围,但我觉得这会导致关闭问题,因为旧控制器可能引用了不再在DOM中的旧元素等.

有没有一个很好的方法来解决这个问题?

添加一些代码因为StackOverflow需要它,但是请参考plunker:

angular.module('app').controller('bodyController', ['$element', function ($element) {

    this.contents1 = [{
        path: 'template1.html'
    }, {
        path: 'template2.html'
    }];

    this.contents2 = [{
        path: 'template3.html'
    }, {
        path: 'template4.html'
    }];

    this.move12 = function () {
        this.contents2.push(this.contents1.pop());
    };

    this.move21 = function () {
        this.contents1.push(this.contents2.pop());
    };

    this.naiveMove12 = function () {
        var $elem = $($element);
        $elem.find('container-list:eq(0) > div').last()
            .appendTo($elem.find('container-list').eq(1));
    };

    this.naiveMove21 = function () {
        var $elem = $($element);
        $elem.find('container-list:eq(1) > div').last()
            .appendTo($elem.find('container-list').eq(0));
    };

    this.logScope = function () {
        var $elem = $($element);
        [].forEach.call($elem.find('container-list'), function (elem, idx) {
            console.log(idx + ': ', angular.element(elem).isolateScope().containerListCtrl);
        });
    };
}]);
Run Code Online (Sandbox Code Playgroud)

jbm*_*rom 0

根据我的经验,将应用程序状态与视图/控制器耦合并不是最佳实践,但这里有一种方法可以做到这一点:修订后的 plunkr

有一些小的变化template1.html。唯一的其他变化是templateController.js

angular.module('app').controller('templateController', statefulTemplateCtrl());

function statefulTemplateCtrl() {
  var state = {color: 'red'};
  return function(){
    this.state = state;

    // this.color = 'red';

    this.toggleColor = function () {
        state.color = (state.color === 'red') ? 'green' : 'red';
    };
  };
}
Run Code Online (Sandbox Code Playgroud)

现在使用的函数templateController已配置为维护对state对象的引用。现在,如果您使用 移动模板bodyCtrl.move12(),您将能够保持累积的切换状态,而不会破坏应用程序状态。这是可行的,因为控制器通过闭包引用了外部对象。因此,它可以在新的 DOM 节点上重新实例化并重新编译,而ng-include无需重新实例化state

可能是更好的处理方式

使用服务和/或$scope事件 API 在应用程序中的不同 DOM 节点之间共享状态。

假设您有模板,与您的 plunker 中引入的模板相同:

<div ng-class="{ red: (tc.color === 'red'), green: (tc.color === 'green') }" ng-click="tc.toggleColor()">Toggler</div> 
Run Code Online (Sandbox Code Playgroud)

如果您想将tc包含所有切换相关状态的对象移动到 DOM 中的另一个节点,您可以:

  • 如果目标节点是源节点的后代,沿着范围层次结构调度事件$scope.$broadcast('nameOfEvent', tc)$scope
  • 如果目标节点是源节点的祖先,则向作用域层次结构向上调度事件$scope.$emit('nameOfEvent', tc)$scope
  • 通过注入将事件分派到应用程序中的所有节点$rootScope$rootScope.$broadcast('nameOfEvent', tc)如果目标节点可以是其中之一。

调用 $scope 事件 API 的任何方法都将允许您将数据(在本例中为tc对象)从一个节点传输到另一个节点。与目标节点关联的目标控制器可以等待事件通过能够接收传输数据的侦听器函数进行分派:

function bindDataToController(destinationCtrl){
  return function(event, tc){
    angular.extend(destinationCtrl, tc);
  };
} 

$scope.$on('nameOfEvent', bindDataToController(this));
Run Code Online (Sandbox Code Playgroud)

在收到触发的 后,传输的数据对象tc将会绑定到与新目标节点关联的控制器'nameOfEvent'。现在源数据已与目标控制器对象合并,请确保目标控制器的名称与controllerAs源控制器的名称相同(在本例中)'tc',以便源模板在最终编译后可以正确引用目标控制器并附加到目标 DOM 节点(该过程将在下面讨论)。

正如您提到的,删除和添加 DOM 节点有些微不足道,因此我没有在这里讨论它,但如果您希望轻松访问源视图/模板以根据需要在应用程序中的任何位置添加和删除,您可以使用$templateCache

回到切换示例,如果您的切换视图/模板本身已经是templateUrl与文件路径一起使用的指令的一部分'toggle.html',那么您的模板将已经被放入并存储在$templateCache键下'toggle.html'

在您的目标控制器中,注入$templateCache,这将允许您检索模板:

 var template = $templateCache.get('toggle.html');
Run Code Online (Sandbox Code Playgroud)

将 $compile 注入到目标控制器中,并使用目标 $scope 来 $compile 模板(也注入到目标控制器中):

var $template = $compile(template)($scope);
Run Code Online (Sandbox Code Playgroud)

然后将编译后的模板附加到与$element目标控制器关联的(也注入到目标控制器中):

$element.append($template);
Run Code Online (Sandbox Code Playgroud)

如果您只想'toggle.html'在收到传输的数据后编译模板并将其附加到 DOM,则可以将上述命令式编译代码放在 的返回函数内bindDataToController,注册为 的事件侦听器'nameOfEvent'。现在你已经看到了!...一个 DOM 节点已经被“移动”到了之前在源位置建立的任何复杂状态。