javascript - 在 DOM 中移动 Controller 时保留元素/范围状态

标签 javascript angularjs

在一个 Angular 应用程序中,我想将一个元素从 DOM 树的一部分移动到另一部分,而不必重新加载附加到移动元素的 Controller 。

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

在这个 plunker 中,唯一要保留的数据是切换器状态,但实际上它可能是非常大量的数据,可能需要花费大量精力来初始化。

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

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

我考虑将 Controller 从旧范围移动到新范围,但我觉得这会导致关闭问题,因为旧 Controller 可能引用不再在 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);
        });
    };
}]);

最佳答案

根据我的经验,将应用程序状态与 View / Controller 耦合并不是最佳实践,但这里有一种方法: revised 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';
    };
  };
}

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

可能是处理 的更好方法

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

假设您有模板,与您的 plunker 中引入的模板相同:
<div ng-class="{ red: (tc.color === 'red'), green: (tc.color === 'green') }" ng-click="tc.toggleColor()">Toggler</div> 

你想移动tc包含所有切换相关状态的对象到 DOM 中的另一个节点,您可以:
  • 发送事件 向下范围层次结构 $scope.$broadcast('nameOfEvent', tc)如果目标节点是源的后代$scope
  • 发送事件 向上范围层次结构 如果 $scope.$emit('nameOfEvent', tc)如果目标节点是源的祖先 $scope
  • 发送事件 到您的应用程序中的所有节点通过注入(inject) $rootScope$rootScope.$broadcast('nameOfEvent', tc)如果目标节点可以是任何一个。

  • 调用 $scope 事件 API 的任何这些方法将允许您传输数据,在本例中为 tc对象,从一个节点到另一个节点。与目标节点关联的目标 Controller 可以等待事件被分派(dispatch),监听器函数能够接收传输的数据:
    function bindDataToController(destinationCtrl){
      return function(event, tc){
        angular.extend(destinationCtrl, tc);
      };
    } 
    
    $scope.$on('nameOfEvent', bindDataToController(this));
    

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

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

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

    在您的目标 Controller 中,注入(inject) $templateCache ,这将允许您检索模板:
     var template = $templateCache.get('toggle.html');
    

    将 $compile 注入(inject)您的目标 Controller 并使用目标 $scope 对模板进行 $compile (也注入(inject)目标 Controller ):
    var $template = $compile(template)($scope);
    

    然后将编译后的模板附加到 $element (也注入(inject)到目标 Controller 中)与您的目标 Controller 相关联:
    $element.append($template);
    

    如果您只想编译和附加 'toggle.html'收到传输的数据后将模板复制到 DOM,您可以将上述命令式编译代码放在 bindDataToController 的返回函数中,注册为 'nameOfEvent' 的事件监听器.就这样!... 一个 DOM 节点已被“移动”,具有先前在源位置建立的任何复杂状态。

    关于javascript - 在 DOM 中移动 Controller 时保留元素/范围状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32579737/

    相关文章:

    javascript - 从另一个js文件中的变量调用函数

    javascript - 将元素的 runat 设置为 ="server"时,ASP.NET javascript 代码不起作用

    javascript - jQuery,循环遍历只有部分名称或 ID 已知的元素

    javascript - 在 Angular 单元测试中,如何清理 $timeout promise ,以免它们持续存在?

    javascript - AngularJs - 将事件广播到单个 Controller

    javascript - 将网页上的 [object HTMLElement] 渲染为元素

    javascript - Cordova 使用 addEventListener 从表单中获取值

    javascript - Angular URL 标记

    javascript - 如果一个条件设置为 true 并且剩下的 ng-show 为 false,如何处理 AngularJS 中的多个 ng-show 和一个 ng-hide

    angularjs - 在输入文本框的占位符中渲染特殊字符