4

プランカーはこちら

一般的なシナリオとして、ng-repeat に表示されるアイテムのコレクションがあります。表示される行ごとに、プロセス (ファイルのアップロード) を開始するボタンとステータス フィールドがあります。プロセスのステータスが変わるたびに、UI に反映させたいと思います。これは、Angular の 2 ウェイ バインディングで簡単にできるはずですよね?

特にこのプロセスは非同期であり、ユーザーは多くのアイテムをアップロードする可能性があるため、アイテムのコレクションを処理するのではなく、独自のスコープでアイテムのステータスを簡単に更新できるように、ng-repeat に 2 番目の (子) コントローラーを作成しました。ファイルを同時に。

問題: Ang/JS の $scope の理解が不足しています - 笑。真剣に、UI のバインドされた {{xxx}} 値は、スコープ モデルの値が更新されても更新されません。いずれかのボタンをクリックして、アラートを監視します。UI を正しく更新するにはどうすればよいですか?

参考までに - 実際には、そのボタンは外部ライブラリの API を呼び出してファイルをアップロードし、アップロードのステータスを確認するための URL を返します。次に、setInterval() ループで URL をポーリングして、完了またはエラーになるまでステータスを ping します。この複雑さ自体は問題ではないため、Plunkr でその部分を単純化しました。 プランカー

    <!DOCTYPE html>
<html ng-app="myapp">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="angular.js@1.2.x" src="http://code.angularjs.org/1.2.7/angular.js" data-semver="1.2.7"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
  <table>
      <th></th>
      <th>Id</th>
      <th>Name</th>
      <th>Status</th>
    <tr ng-repeat="item in items" ng-controller="ChildCtrl">
        <td><button ng-click="updateStatus(item)">click</button></td>
        <td>{{item.id}}</td>
        <td>{{item.name}}</td>
        <td>{{item.status}}</td>
    </tr>

  </table>
  </body>

</html>

JS

var app = angular.module('myapp', []);

app.controller('MainCtrl', function($scope) {
  $scope.items = [ {id: 1, name: "Moe", status: "init"}
  , {id: 3, name: "Larry", status: "init"}
  , {id: 2, name: "Curly", status: "init"}
  ];
});


app.controller('ChildCtrl', function($scope) {
  $scope.updateStatus = function(item){
    $scope.myItem = item;
    alert('child: ' + item.id + ' status: ' + item.status);
    item.status = 'clicked';
    alert('status just update in UI to: ' + item.status);

    callResult = fakeAjaxCall($scope);
    alert('callResult: ' + callResult);
  };

  var fakeAjaxCall = function(scope){
    setTimeout(function (item) {
        if (-1 == -1) {  //success
            result = "Wow, it worked!";
            alert('current status: ' + scope.myItem.status);
            alert('ajax result: ' + result);
            scope.myItem.status = result;
            alert('new status: ' + scope.myItem.status);
            alert("but the status in the UI didn't update");
        }
    }, 2000);
  };

});
4

3 に答える 3

6

モーダルを更新する angular 内からダイジェスト サイクルを呼び出す代わりに$timeoutを使用するsetTimeoutか、自分でダイジェスト サイクルを呼び出す必要があります (コードをラップする などscope.$apply()scope.evalAsyncて...)。setTimeoutangular内では発生しないため、完了しました。物事を行うための角度のある方法があり、それを自由に使用できる場合は、できるだけ手動でダイジェスト サイクルを呼び出さないようにする必要があります。この場合、次のように置き換えることができますsetTimeout$timeoutモデルの変更は、完了時に angular がダイジェスト サイクルを呼び出すため、ビューに自動的に反映さ$timeoutれます。もう1つの利点は、$timeoutそれはあなたが連鎖することができるプロミスも返しますsetTimeout.

app.controller('ChildCtrl', function($scope, $timeout) {
  $scope.updateStatus = function(item){
    $scope.myItem = item;
    console.log('child: ' + item.id + ' status: ' + item.status);
    item.status = 'clicked';
    console.log('status now updates in UI to: ' + item.status);

    callResult = fakeAjaxCall($scope);
    console.log('callResult: ' + callResult);
  };

  var fakeAjaxCall = function(scope){
    $timeout(function (item) {
        if (-1 == -1) {  //success
            result = "Wow, it worked!";
            console.log('current status: ' + scope.myItem.status);
            console.log('ajax result: ' + result);
            scope.myItem.status = result;
            console.log('new status: ' + scope.myItem.status);
            console.log("but the status in the UI doesn't update");
        }
    }, 2000);
  };

プランク

$log非同期操作のデバッグ中は、デバッグ用のアラートの代わりにconsole.log (または ) を使用することをお勧めします。

于 2014-08-10T23:56:30.577 に答える
1

タイムアウト コールバックのように angular の外でスコープ変数を変更する場合、$scope.$apply を使用してダイジェスト サイクルを更新するよう angular に指示できます。

var fakeAjaxCall = function(scope){
setTimeout(function (item) {
    if (-1 == -1) {  //success
        result = "Wow, it worked!";
        alert('current status: ' + scope.myItem.status);
        alert('ajax result: ' + result);
        scope.$apply(scope.myItem.status = result); // <---- Changed
        alert('new status: ' + scope.myItem.status);
        alert("but the status in the UI doesn't update");
    }
}, 2000);
};
于 2014-08-10T23:34:50.487 に答える
0

を使用する必要があります$timeout

setTimeoutangularjs スコープから値を削除します。を使用scope.$apply()してスコープを手動で更新できます。angular ビルド済みラッパーを使用することをお勧めします。

$timeout リファレンス

更新されたPlunkr

app.controller('ChildCtrl', function($scope,$timeout) {
  $scope.updateStatus = function(item){
    $scope.myItem = item;
    alert('child: ' + item.id + ' status: ' + item.status);
    item.status = 'clicked';
    alert('status now updates in UI to: ' + item.status);

    callResult = fakeAjaxCall($scope);
    alert('callResult: ' + callResult);
  };

  var fakeAjaxCall = function(scope){
    $timeout(function (item) {
        if (-1 == -1) {  //success
            result = "Wow, it worked!";
            alert('current status: ' + scope.myItem.status);
            alert('ajax result: ' + result);
            $scope.myItem.status = result;
            alert('new status: ' + scope.myItem.status);
            alert("but the status in the UI doesn't update");
        }
    }, 2000);
  };
});
于 2014-08-10T23:58:47.717 に答える