在AngularJS模态对话框中是否有处理“取消”的模式?

时间:2013-05-30 13:54:51

标签: javascript angularjs

注意:这不是关于使用AngularJS显示模态对话框,该主题有很多问题和答案!

这个问题是关于如何在页面上的模式对话框中对OK和Cancel做出反应。假设你的范围只包含一个变量:

$scope.description = "Oh, how I love porcupines..."

如果我在页面上为您提供模态对话框并在该对话框中使用ng-model =“description”,那么您所做的所有更改实际上都是在您键入时实时对描述本身进行的。这很糟糕,因为那你怎么取消那个对话?

这个问题说要做我在下面解释的内容。接受的答案与我提出的“解决方案”相同:AngularJS: Data-bound modal - save changes only when "Save" is clicked, or forget changes if "Cancel" is clicked

我可以看到如果单击按钮调出一个模态返回到后面的函数并创建模态的相关数据的临时副本然后弹出模态,该怎么做。然后“确定”(或“保存”或其他)可以将临时值复制到实际的模型值。

main.js(摘录):

$scope.descriptionUncommitted = $scope.description;

$scope.commitChanges = function () {
  $scope.description = $scope.descriptionUncommitted;
}

main.html(摘录):

<input type="text" ng-model="descriptionUncommitted"/>

<button ng-click="commitChanges()">Save</button>

问题是它不是声明性的!事实上,它在任何其他地方都不像AngularJS。这几乎就像我们需要一个ng-model-uncommitted =“description”,他们可以在这里做出他们想要的所有改变,但是当我们用另一个声明触发它们时它们才会被提交。插件中是否有这样的东西或AngularJS本身添加它?

编辑:似乎有一个不同的做法的例子可能是有序的。

main.js:

$scope.filename = "panorama.jpg";
$scope.description = "A panorama of the mountains.";

$scope.persist = function () { // Some function to hit a back end service. };

main.html中:

<form>
  <input type="text" ng-model-uncommitted="filename"/>
  <input type="text" ng-model-uncommitted="description"/>

  <button ng-commit ng-click="persist()">Save</button>
  <button ng-discard>Cancel</button>
</form>

我在它周围贴了一个表格标签,因为我不知道你如何对这些项目进行分组,所以很明显它是同一个“交易”的一部分(因为没有更好的词)。但是需要某种方式,这可以全部自动发生,模型变量的克隆副本用于初始值,用于输入和自动更新,验证等,然后最终丢弃或复制到相同的值,如果用户决定提交,则最初用于创建它们。

在大型网站中,对于20个模态,一次又一次地执行这项工作,是否比控制器中的代码更容易?还是我疯了?

6 个答案:

答案 0 :(得分:23)

基本上,如果某些东西不是声明性的,你可以使用指令

 .directive('shadow', function() {
  return {
    scope: {
      target: '=shadow'            
    },
    link: function(scope, el, att) {
      scope[att.shadow] = angular.copy(scope.target);

      scope.commit = function() {
        scope.target = scope[att.shadow];
      };
    }
  };

然后:

  <div shadow="data">
    <input ng-model="data">
    <button ng-click="commit()">save</button>
  </div>

因此data指令中的shadow将是原始data副本。 当单击按钮时,它将被复制回原始文件。

以下是工作示例:jsbin

我没有在这个例子之外测试它,所以它可能在其他情况下不起作用,但我认为它给出了可能性的概念。

修改

使用对象而不是字符串的另一个示例,以及表单中的多个字段(此处需要额外的angular.copy):jsbin

Edit2,角度版本1.2.x

根据change, 指令内的input不再访问隔离范围。另一种方法是创建一个非隔离的子范围(scope:true),以保存数据副本并访问父范围以进行保存。

因此,对于更高版本的角度,这与之前略有修改的方法相同:

.directive('shadow', function() {
  return {
    scope: true,
    link: function(scope, el, att) {
      scope[att.shadow] = angular.copy(scope[att.shadow]);

      scope.commit = function() {
        scope.$parent[att.shadow] = angular.copy(scope[att.shadow]);
      };
    }
  };
});

示例:jsbin

请注意,使用$parent的问题是,如果最终中间有另一个范围,它可能会中断。

答案 1 :(得分:21)

从Angular 1.3开始,有ngModelOptions指令允许本地实现相同的行为。

<form name="userForm">
    <input type="text" ng-model="user.name" ng-model-options="{ updateOn: 'submit' }" name="userName">
    <button type="submit">save</button>
    <button type="button"  ng-click="userForm.userName.$rollbackViewValue();">cancel</button>
</form>

JSFiddle:http://jsfiddle.net/8btk5/104/

答案 2 :(得分:11)

面对同样的问题并通过这个主题,我想出了lazy-model指令,其工作方式与ng-model完全相同,但仅在提交表单时保存更改

用法:

<input type="text" lazy-model="user.name">

请注意将其包装到<form>标签中,否则懒惰模型将不知道何时将更改推送到原始模型。

完整的工作演示http://jsfiddle.net/8btk5/3/

lazyModel指令代码:
(更好地使用actual version on github

app.directive('lazyModel', function($parse, $compile) {
  return {
    restrict: 'A',  
    require: '^form',
    scope: true,
    compile: function compile(elem, attr) {
        // getter and setter for original model
        var ngModelGet = $parse(attr.lazyModel);
        var ngModelSet = ngModelGet.assign;  
        // set ng-model to buffer in isolate scope
        elem.attr('ng-model', 'buffer');
        // remove lazy-model attribute to exclude recursion
        elem.removeAttr("lazy-model");
        return function postLink(scope, elem, attr) {
          // initialize buffer value as copy of original model 
          scope.buffer = ngModelGet(scope.$parent);
          // compile element with ng-model directive poining to buffer value   
          $compile(elem)(scope);
          // bind form submit to write back final value from buffer
          var form = elem.parent();
          while(form[0].tagName !== 'FORM') {
            form = form.parent();
          }
          form.bind('submit', function() {
            scope.$apply(function() {
                ngModelSet(scope.$parent, scope.buffer);
            });
         });
         form.bind('reset', function(e) {
            e.preventDefault();
            scope.$apply(function() {
                scope.buffer = ngModelGet(scope.$parent);
            });
         });
        };  
     }
  };
});

Actual source code on GitHub

答案 3 :(得分:6)

你好像在想这个。没有插件,因为这个过程非常简单。如果您需要模型的原始副本,请创建一个并将其保留在控制器中。如果用户取消,请将模型重置为您的副本,并使用FormController。$ setPristine()方法再次使表单保持原始状态。

//Controller:

myService.findOne({$route.current.params['id']}, function(results) {
    $scope.myModel = results;
    var backup = results;
}

//cancel
$scope.cancel = function() {
    $scope.myModel = backup;
    $scope.myForm.$setPristine();
}

然后在你看来:

<form name="myForm">

您需要命名表单以创建$ scope.myForm控制器。

答案 4 :(得分:4)

另一种方法是在编辑模型之前复制模型,然后在取消时恢复原始模型。角度控制器代码:

//on edit, make a copy of the original model and store it on scope
function edit(model){
  //put model on scope for the cancel method to access
  $scope.modelBeingEdited = model;
  //copy from model -> scope.originalModel
  angular.copy(model,$scope.originalModel);  
}

function cancelEdit(){
  //copy from scope.original back to your model 
  angular.copy($scope.originalModel, $scope.modelBeingEdited)  
}
因此,在打开模态对话框时,调用编辑功能并将指针传递给要编辑的模型。 然后确保您的模态对话框绑定到$ scope.editingModel。 取消时,调用取消功能,它将复原原始值。

希望有人帮助!

答案 5 :(得分:0)

我尝试保持简单,使其具有声明性,而不依赖于表单标签或其他内容。

一个简单的指令:

.directive("myDirective", function(){
return {
  scope: {
    item: "=myDirective"
  },
  link: function($scope){
    $scope.stateEnum = {
      view: 0, 
      edit: 1
    };

    $scope.state = $scope.stateEnum.view;

    $scope.edit = function(){
      $scope.tmp1 = $scope.item.text;
      $scope.tmp2 = $scope.item.description;
      $scope.state = $scope.stateEnum.edit;
    };

    $scope.save = function(){
      $scope.item.text = $scope.tmp1;
      $scope.item.description = $scope.tmp2;
      $scope.state = $scope.stateEnum.view;
    };

    $scope.cancel = function(){
      $scope.state = $scope.stateEnum.view;
    };
  },
  templateUrl: "viewTemplate.html"
};
})

viewTemplate.html:

<div>
  <span ng-show="state == stateEnum.view" ng-click="edit()">{{item.text}}, {{item.description}}</span>
  <div ng-show="state == stateEnum.edit"><input ng-model="tmp1" type="text"/> <input ng-model="tmp2" type="text"/><a href="javascript:void(0)" ng-click="save()">save</a> <a href="javascript:void(0)" ng-click="cancel()">cancel</a></div>
</div>

然后设置上下文(项目):

<div ng-repeat="item in myItems">
  <div my-directive="item"></div>
</div>

查看实际操作:http://plnkr.co/edit/VqoKQoIyhtYnge2hzrFk?p=preview