64

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

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

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

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

有这个问题说要做我在下面解释的事情。接受的答案是我想出的相同“解决方案”:AngularJS:数据绑定模式 - 仅在单击“保存”时保存更改,或者如果单击“取消”则忘记更改

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

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本身添加了它?

编辑:似乎有一个不同方式的例子可能是为了。

主.js:

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

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

主.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 个模式一遍又一遍地完成这项工作吗?还是我疯了?

4

6 回答 6

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内部将是原始的副本。单击按钮时,它将被复制回原始状态。shadowdata

这是工作示例:jsbin

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

编辑:

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

Edit2,角度版本 1.2.x

根据此更改input指令内部不再访问隔离范围。一种替代方法是创建一个非隔离的子范围 ( scope:true),以保存数据的副本并访问父范围以保存它。

因此,对于更高版本的 angular,这与之前的方法相同,稍作修改即可:

.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, 的问题是,如果最终中间有另一个范围,它可能会中断。

于 2013-05-30T16:04:14.847 回答
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/

于 2015-03-16T12:59:34.237 回答
11

面对同样的问题,通过这个线程,我想出了一个lazy-model指令,它的工作原理完全一样,但只有在提交表单ng-model时才保存更改。

用法:

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

请注意将其包装到<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);
            });
         });
        };  
     }
  };
});

GitHub上的实际源代码

于 2013-12-17T19:29:54.583 回答
7

你好像多虑了。没有插件,因为过程非常简单。如果您想要模型的原始副本,请制作一个并将其保存在控制器中。如果用户取消,请将模型重置为您的副本并使用 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 控制器。

于 2013-05-30T14:59:50.690 回答
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。在取消时,调用取消函数,它会将原始值复制回来。

希望对某人有所帮助!

于 2015-08-02T13:00:39.370 回答
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"
};
})

视图模板.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

于 2014-05-05T14:29:52.950 回答