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

Joh*_*sch 64 javascript angularjs

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

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

$scope.description = "Oh, how I love porcupines..."
Run Code Online (Sandbox Code Playgroud)

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

这个问题说要做我在下面解释的内容.它接受的答案与我提出的相同"解决方案":AngularJS:数据绑定模式 - 仅在单击"保存"时保存更改,或者如果单击"取消"则忘记更改

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

main.js(摘录):

$scope.descriptionUncommitted = $scope.description;

$scope.commitChanges = function () {
  $scope.description = $scope.descriptionUncommitted;
}
Run Code Online (Sandbox Code Playgroud)

main.html(摘录):

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

<button ng-click="commitChanges()">Save</button>
Run Code Online (Sandbox Code Playgroud)

问题在于它不是声明性的!事实上,它在任何其他地方都不像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. };
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

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

这样的事情是不是比控制器中的代码更容易在一个大网站上反复做20个模态的工作?还是我疯了?

gar*_*rst 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];
      };
    }
  };
Run Code Online (Sandbox Code Playgroud)

然后:

  <div shadow="data">
    <input ng-model="data">
    <button ng-click="commit()">save</button>
  </div>
Run Code Online (Sandbox Code Playgroud)

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

以下是工作示例:jsbin

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

编辑:

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

Edit2,角度版本1.2.x.

根据此更改,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]);
      };
    }
  };
});
Run Code Online (Sandbox Code Playgroud)

示例:jsbin

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


Sil*_*fer 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>
Run Code Online (Sandbox Code Playgroud)

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

  • 提交按钮不能用于ng-form,那么只有在按下特定按钮(不提交)时才能告诉模型更新? (2认同)
  • 提交确实适用于ng-form,只需在表单标记中添加ng-submit ="..."属性,当单击提交按钮时,它将被执行.但是,此解决方案的一个问题是您无法进行实时验证,因为未提交验证,因为在提交表单之前未提交更改. (2认同)
  • 值得注意的是,如果有多个字段,您还可以直接在表单上使用`ng-model-options`和`$ rollbackViewValue()`. (2认同)
  • 这很好.但是,请记住,通过点击提交,模型会更新.如果将数据传输到服务器并且请求返回错误,则会出现问题.用户可能希望立即取消,但您无法回滚视图值.如果有人对这个问题有所了解,请告诉我.在那之前,我将不得不坚持复制.:-( (2认同)

vit*_*ets 11

面对同样的问题,并通过这个线程,我想出的lazy-model指令完全相同,ng-model但只在提交表单时保存更改.

用法:

<input type="text" lazy-model="user.name">
Run Code Online (Sandbox Code Playgroud)

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

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

lazyModel指令代码:(
更好地使用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);
            });
         });
        };  
     }
  };
});
Run Code Online (Sandbox Code Playgroud)

GitHub上的实际源代码


Sha*_*dio 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();
}
Run Code Online (Sandbox Code Playgroud)

然后在你看来:

<form name="myForm">
Run Code Online (Sandbox Code Playgroud)

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