Как использовать ng-switch внутри ng-repeat для редактирования данных JSON API

Я знаю, что это повторяющийся вопрос, но, к сожалению, я не смог найти правильный ответ на мой случай.

В основном я получаю данные от конечной точки API JSON, которая отображается в таблице с помощью ng-repeat, Теперь я хочу ng-switch представление полей ввода для внесения поправок в данные (и последующей отправки их обратно на сервер).

На мой взгляд, мои решения зависят от наличия в данных свойства, которое мне не очень нравится. Я уверен, что есть более разумный способ, чем введение этого свойства после получения данных - какие-либо предложения?

HTML:

<tbody>
  <tr ng-repeat="item in data" ng-switch on="item.edit" >
    <td ng-switch-default ng-bind="item.color"></td>
    <td ng-switch-when='true'>
      <input type="text" ng-model="item.color" />
    </td>
    <td ng-switch-default><button ng-click="switch(item)">edit</button></td>
    <td ng-switch-when='true'><button ng-click="send(item)">send</button></td>
  </tr>
</tbody>

JS:

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

app.controller('MyCtrl', function($scope) {

  $scope.switch = function (item) {
    if (item.edit) {
      item.edit = false;
    } else {
      item.edit = true;
    }
  };

  $scope.send = function (item) {
    if (item.edit) {
      // data is sent...
      item.edit = false;
    } else {
      item.edit = true;
    }
  };

  $scope.data = [
      {color: 'blue', edit: false},
      {color: 'green', edit: false},
      {color: 'orange', edit: false}];
});

заранее спасибо!

вот плункер: http://plnkr.co/edit/h8ar4S43JUvjHurzLgT0?p=preview

3 ответа

Решение

Если вы не хотите помещать свои флаги в свои объекты данных, вам нужно будет использовать отдельный объект для их хранения. С помощью WeakMaps вы можете легко связать объект данных или сам элемент с объектом flags. Если вы ориентируетесь на старые браузеры, вам нужно будет найти похожий способ связать объект данных / или элемент с объектом flags

JS

let map = new WeakMap();
$scope.editing = function(item){
    return map.get(item).edit;
}
$scope.switch = function (item) {
    let flags = map.get(item);
    if (flags.edit) {
        flags.edit = false;
    } else {
        flags.edit = true;
    }
};
//Note you could combine switch and send into a single toggle function
$scope.send = function (item) {
    let flags = map.get(item);
    if (flags.edit) {
        flags.edit = false;
    } else {
        flags.edit = true;
    }
};
$scope.data = [
  {color: 'blue'},
  {color: 'green'},
  {color: 'orange'}
];
//Create an empty flags object for each data item
for(let item of $scope.data){
   map.set(item,{});
}

HTML

<tr ng-repeat="item in data" ng-switch on="editing(item)" >
    <td ng-switch-default ng-bind="item.color"></td>
    <td ng-switch-when='true'>
        <input type="text" ng-model="item.color" />
    </td>
    <td ng-switch-default><button ng-click="switch(item)">edit</button></td>
    <td ng-switch-when='true'><button ng-click="send(item)">send</button></td>
</tr>

демонстрация

// Code goes here
var app = angular.module('myApp', []);

app.controller('MyCtrl', function($scope) {
  var map = new WeakMap();
  
  //Using fat arrow less code to write
  $scope.editing = item=>map.get(item).edit;
  
  
  //Since "switch" and "send" had similar 
  //toggling code just combined them
  //Also no need to use if statement, just use the NOT operator
  //to toggle the edit flag
  $scope.toggle = item=>{
    let flags = map.get(item);
    flags.edit = !flags.edit;
  };

  $scope.switch = item=>{
    $scope.toggle(item);
    //Do some switching? 
    //if not doing anything else just 
    //call toggle in the ng-click
  };
  $scope.send = item=>{
    $scope.toggle(item);
    //Do some sending
  };

  $scope.data = [
      {color: 'blue'},
      {color: 'green'},
      {color: 'orange'}];
      
  for(let item of $scope.data){
    map.set(item,{});
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
  <table>
    <thead>
      <tr>
        <th width="180">Column</th>
        <th>Edit</th>
      </tr>
    </thead>
    <tbody>
      <tr ng-repeat="item in data" ng-switch on="editing(item)" >
        <td ng-switch-default ng-bind="item.color"></td>
        <td ng-switch-when='true'>
          <input type="text" ng-model="item.color" />
        </td>
        <td ng-switch-default><button ng-click="switch(item)">edit</button></td>
        <td ng-switch-when='true'><button ng-click="send(item)">send</button></td>
      </tr>
    </tbody>
  </table><br>
  "$scope.data" should never change after hitting edit/send since the flag is no longer on the data item object:
  <code><pre>{{data}}</pre></code>
</div>

Для подобных случаев я всегда инкапсулирую состояние представления в директиву. Здесь это означает создать директиву для каждой строки и переместить item.edit флаг в этой директиве.

Очень наивная реализация выглядит следующим образом:

HTML:

<tbody>
    <tr ng-repeat="item in data" inplace-edit="item" send-callback="send(item)"></tr>
</tbody>

JS:

app.directive('inplaceEdit', function() {
  return {
    restrict: 'A',
    template:
      '<td ng-if="!inEditMode" ng-bind="item.color"></td>' +
      '<td ng-if="inEditMode">' +
        '<input type="text" ng-model="item.color" />' +
      '</td>' +
      '<td ng-if="!inEditMode"><button ng-click="toEditMode()">edit</button></td>' +
      '<td ng-if="inEditMode"><button ng-click="send()">send</button></td>',
    scope: {
      item: '=inplaceEdit',
      sendCallback: '&'
    },
    link: function(scope) {
      scope.inEditMode = false;

      scope.toEditMode = function() {
        scope.inEditMode = true;
      };

      scope.send = function() {
        scope.sendCallback({item: scope.item});
        scope.inEditMode = false;
      };
    }
  };
});

Смотрите раздвоенный план: http://plnkr.co/edit/BS6a866aiy3BA9MX0Flx?p=preview

Что бы я добавил, чтобы модернизировать это:

  1. controllerAs, bindToController
  2. Некоторый код для отката / отмены изменений (например, кнопка "отмена" в режиме редактирования)
  3. Используйте Angular 1.5.x и одностороннюю привязку: item: '>inplaceEdit' или интегрировать inplace-edit директива с нг-моделью

Вместо этого я использую ng-show, но, надеюсь, это демонстрирует лучший подход:

http://plnkr.co/edit/63Io7k1mJcfppxBQUVef?p=preview

я использую ng-show вместо этого я просто неявно добавляю "edit" к объекту, когда это необходимо, поскольку вам не нужно сразу устанавливать его в true. Отсутствие свойства будет означать, что оно возвращает false.

Разметка:

<tbody> <tr ng-repeat="item in data"> <td ng-show="!item.edit" ng-bind="item.color"></td> <td ng-show='item.edit'> <input type="text" ng-model="item.color" /> </td> <td><button ng-click="edit(item)">{{item.edit ? "Send" : "Edit"}}</button></td> </tr> </tbody>

Другие вопросы по тегам