【问题标题】:How to create $scope functions that never reads $scope?如何创建从不读取 $scope 的 $scope 函数?
【发布时间】:2016-03-08 07:32:21
【问题描述】:

我想创建一个 $scope 函数,它只会操纵它接收到的变量。

我制作了一个 working Plunker 来测试一下。

我有一个 ng-repeat,它只是列出小猫的名字和 id。我还有一个输入表单来接收新小猫的名字。然后,我有 3 个按钮,每个按钮都访问一个不同的 $scope 函数,该函数将以不同的方式操作 $scope。

每个函数的目标从输入表单中读取小猫的名字读取小猫上使用的最后一个id数组,将该 id+1 分配给新小猫,将新小猫推送到小猫数组并删除表单数据。。 p>

  • 第一个函数$scope.addFullScope(), 将不接收任何参数,并将读取和操作来自$scope 的所有内容。

  • 第二个函数$scope.addJustDelete(kitty, kitties), 将接收新的kittykitties 数组作为参数。但是,最后,要清理表单,它会做$scope.kitty = undefined

  • 第三个函数$scope.addNoScope(kitty, kitties), 将接收新的kittykitties 数组作为参数。但是,最后,要清理表单,它将执行kitty = undefined但表单不会清理!而且所有内容都会出现问题。

我怎样才能使第三个函数或类似的函数工作,以便我有一个完全独立的函数可以使用?

附录:

HTML:

  <body ng-app='app' ng-controller="ctrl">

    <h3 ng-repeat="kitty in kitties">
      {{kitty.name}}: {{kitty.id}} //Kitties list
    </h3>

    <input placeholder='Kitty name to add' class='form form-control' 
           type="text" ng-model="kitty.name" />

    <h3> $scope use on adding kitty:</h3>
    <button ng-click="addFullScope()">Full Scope.</button>
    <button ng-click="addJustDelete(kitty, kitties)">Just delete.</button>
    <button ng-click="addNoScope(kitty, kitties)">None. Buggy</button>
  </body>

控制器:

.controller('ctrl', function($scope) {
  $scope.kitties = [
    //Let's imagine kitties in here.
    {name: 'Purple kitty', id:35},
    //Kittie 36 died in a car accident. :(
    {name: 'Rodmentou cat', id: 37},
    {name: 'Fatty kitty', id: 38}

    ];

  $scope.addFullScope = function () {
    var size = $scope.kitties.length;
    var id = $scope.kitties[size-1].id + 1;

    $scope.kitty.id = id;
    $scope.kitties.push($scope.kitty);
    $scope.kitty = undefined;
  };

  $scope.addJustDelete = function (kitty, kitties) {
    var size = kitties.length;
    var id = kitties[size-1].id + 1;

    kitty.id = id;
    kitties.push(kitty);
    $scope.kitty= undefined;

  };

  $scope.addNoScope = function (kitty, kitties) {
    var size = kitties.length;
    var id = kitties[size-1].id + 1;

    kitty.id = id;
    kitties.push(kitty);
    kitty = undefined; //Don't work
  };



});

【问题讨论】:

  • 这与var a=99;(function(a){a=0;})(a);console.log(a); 打印99 的原因相同。对象是通过引用传递的,addNoScope只是让局部变量引用undefined而不是原来的对象,但是并没有被删除。
  • 你不能有一个使用$scope作为其模型的表单,并期望一个函数在不接触$scope的情况下操作该表单
  • 您应该使用服务而不是操纵 $scope。 kittenService 可以公开一个 kittens 列表和一个方法来推送一个新的列表来处理 id 生成。
  • 我唯一关心的是如何在不触及范围的情况下删除表单的答案。

标签: javascript html arrays angularjs angularjs-scope


【解决方案1】:

这无法做到,因为 JS 遵循 Java 的 pass-by-ref-ish 参数传递方法。以下是一些与您的案例相当相关的示例

(pen)

    function double(x) {
      x.val = 2 * x.val;
    }

    function doubleToo(x) {
      x = 2 * x;
    }

    var data = {
      val: 2
    };

    document.write("Initial: " + data.val + "<br />");

    double(data); //data.val now = 4

    document.write("Double: " + data.val + "<br />");

    doubleToo(data.val); //data.val is still 4

    document.write("DoubleToo:" + data.val);

对参数的赋值操作对传入的实际变量没有影响。这就是为什么第一个例子,它更新了一个对象的内容,而第二个例子(它试图重新赋值)没有影响.

因此,在您的情况下,如果该函数绝对必须更新$scope 中的变量,则它必须接收$scope 作为参数(或包含对$scope 的引用的某个对象)。就像在那个例子中一样,它必须接收data 才能更新data.val

【讨论】:

  • 有趣。你的解释有帮助。谢谢!
【解决方案2】:

要使用一个独立的函数来操作kitties,并使用一个单独的函数来重置表单,试试这个:

$scope.addNoScope = function (kitty, kitties) { 操纵小猫(小猫,小猫); 重置表格(); }; 功能操作小猫(小猫,小猫){ var 大小 = kitties.length; var id = kitties[size-1].id + 1; kitty.id = id; kitties.push(小猫); } 函数resetForm() { $scope.kitty=未定义; }

【讨论】:

  • 非常开箱即用的方法。不错。
【解决方案3】:

这是因为函数$scope.addNoScope(kitty, kitties) 只接收到对象kitty 的“引用的副本”,因此更改引用只会影响这个副本而不影响原始引用。 Check this thread.

因此,在您的情况下,$scope.kitty 仍然指向具有姓氏和 id 的同一个对象。当您再次按下addNoScope 时,angular 会再次尝试推动同一个对象,这会抛出this error,因为angular 不允许在ng-repeat 中重复,除非您添加track by

解决方案,如果你需要在不使用作用域的情况下这样做,是传递一个包含kitty的对象作为属性,这样当你改变传递对象的属性时,它会反映在原始对象上,如本例所示

var obj = {id:1};

function changeReference (obj){ 
    //changing the reference to an entirely new object
    obj = {id:2}
}

function changeProperty (obj){ 
    //changing a property of the current object
    obj.id = 2
}

changeReference(obj);
console.log(obj.id);
//1 as original obj is not affected

changeProperty(obj);
console.log(obj.id);
//2

所以你的代码变成了

$scope.addNoScope = function (fields, kitties) {
    var size = kitties.length;
    var id = kitties[size-1].id + 1;

    fields.kitty.id = id;
    kitties.push(fields.kitty);
    fields.kitty = undefined; //Don't work
  };

和html

<input placeholder='Kitty name to add' class='form form-control' type="text" ng-model="fields.kitty.name" />
<h3> $scope use on adding kitty:</h3>
<button class='btn btn-danger' ng-click="addFullScope()">Full Scope.</button>
<button class='btn btn-warning' ng-click="addJustDelete(fields.kitty, kitties)">Just delete.</button>
<button class='btn btn-success' ng-click="addNoScope(fields, kitties)">None. Buggy</button>

不要忘记在其他函数中将$scope.kitty 更改为$scope.fields.kitty,否则它们会中断。

【讨论】:

    【解决方案4】:

    在底层,Angular 需要一种方法来观察 ngModel 是否被更改,并使用 $scope.$watch 来完成此操作。 $watch 被视为 Equality Watcher,它检查一个特定属性是否已更改,而不是它底层的对象是否已更改,这将是一个 Reference Watcher。

    我在Tero Parviainen's blog 上找到了很好的描述和图片

    因此,您的代码的问题在于您正在设置 kitty = undefined,但 ngModel 和扩展名 $scope.$watch 正在寻找 kitty.name 的更改。为了证明情况就是这样,我在kitty.name 上添加了一个手表,当你设置kitty = undefined 时,手表永远不会被触发。将 kitty 设置为 undefined 会改变底层对象,但不会改变它的属性(相当混乱)!我创建了一个plunker example with updated code

    HTML

    为了清楚起见,我创建了我们正在更新的模型 kitten,因此它不会与我们正在迭代的 kitties 混淆。

    <h3 ng-repeat="kitty in kitties">
      {{kitty.name}}: {{kitty.id}}
    </h3>
    <input placeholder='Kitty name to add' class='form form-control' type="text" ng-model="kitten.name" />
    <h3> $scope use on adding kitty:</h3>
    <button class='btn btn-success' ng-click="addNoScope(kitten, kitties)">None. Buggy</button>
    <button class='btn btn-danger' ng-click="noWatch(kitten)">Full Scope.</button>
    

    Javascript

    最大的更新是从模型的小猫中创建一个克隆小猫,这样它就不会在数组中被引用。此外,当我们想要重置模型的小猫时,我们直接设置 kitten.namekitten.id 属性。

    angular.module('app', [])
    .controller('ctrl', function($scope) {
    
      $scope.kitten = {
        name: undefined, id: undefined
      };
    
      $scope.$watch('kitten.name', function() { console.log("changed"); }, true);
    
      $scope.kitties = [
        //Let's imagine kitties in here.
        {name: 'Purple kitty', id:35},
        //Kittie 36 died in a car accident. :(
        {name: 'Rodmentou cat', id: 37},
        {name: 'Fatty kitty', id: 38}
    
      ];
    
      $scope.addNoScope = function (kitten, kitties) {
        if (!$scope.kitten || !$scope.kitten.name) return;
        var size = kitties.length;
        var id = kitties[size-1].id + 1;
        var newKitten = {
          name: kitten.name, id: id
        }
        kitties.push(newKitten);
        kitten.name = undefined;
        kitten.id = undefined;
        // kitten = undefined;
      };
    
      $scope.noWatch = function (kitten) {
        kitten = undefined;
        console.log('not watching');
      };
    
    });
    

    【讨论】:

    • 如果你不喜欢手动克隆你可以使用的对象:var newKitten = JSON.parse(JSON.stringify(kitten))
    【解决方案5】:

    你只需要通过以下方式重构你的函数:

      $scope.addNoScope = function (kitty, kitties) {
        var size = kitties.length;
    
        kitties.push({ //you need to create a new object
          id : kitties[size-1].id + 1,
          name : kitty.name
        });
        kitty.name = undefined;   
    };
    

    需要记住的重要一点是,JavaScript 通过值传递变量,这意味着参数 kitty 实际上是作用域 $scope.kitty 中的变量。因此,您将实际范围变量推入数组,然后使其等于未定义,这就是错误结果的原因。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-11-01
      • 1970-01-01
      • 2017-11-29
      • 1970-01-01
      • 2013-05-06
      • 1970-01-01
      • 1970-01-01
      • 2021-07-03
      相关资源
      最近更新 更多