Angular ресурсные объекты даты

Я использую $resource для управления моими данными. Я хотел бы преобразовать даты, помещенные в объекты даты, когда я их вывожу. Это просто упрощает работу с datepicker и т.д.

Мой factory:

AppFactories.factory("Books", ['$resource' ,function($resource){

  return $resource(
        "/books/:id",
        {id: "@id" },
        {
            "update": {method: "PUT", isArray: true },
            "save": {method: "POST", isArray: true },

        }
    );

}]);

Можно ли создать функцию в factory для преобразования дат из базы данных в объект даты и наоборот, когда я отправляю/обновляю?

Было бы очень удобно интегрировать его прямо в factory, чтобы я мог его повторно использовать.

Ответы

Ответ 1

Объект action $resource может иметь свойство с именем interceptor, которое позволяет вам определить объект interceptor для этого конкретного действия (как с $http-перехватчиками). Вы можете определить свойства response и responseError для этого перехватчика. Таким образом, вы можете определить некоторые функции синтаксического анализа даты для действий, которые предлагает ресурс:

function parseResponseDates(response) {
  var data = response.data, key, value;
  for (key in data) {
    if (!data.hasOwnProperty(key) && // don't parse prototype or non-string props
        toString.call(data[key]) !== '[object String]') continue;
    value = Date.parse(data[key]); // try to parse to date
    if (value !== NaN) data[key] = value;
  }
  return response;
}

return $resource('/books/:id', {id: '@id'},
  {
    'update': {
      method: 'PUT', 
      isArray: true, 
      interceptor: {response: parseResponseDates}
    },
    ....
  }
);

Надеюсь, это поможет!

Ответ 2

Вместо того, чтобы делать это на каждом ресурсе, вы можете применить его к базовому $http, как описано более подробно здесь.

app.config(["$httpProvider", function ($httpProvider) {
     $httpProvider.defaults.transformResponse.push(function(responseData){
        convertDateStringsToDates(responseData);
        return responseData;
    });
}]);

Ответ 3

Я хотел использовать Angular.js ngResource ($resource), но я также хотел, чтобы мой синтаксический анализ не мешал чему-либо еще в $http, а действие transformResponse представляется более подходящим, чем опция interceptor. @jakee parseResponseDates хорош, потому что это общая цель, но это более грубая сила, чем я хотел; Я точно знаю, какие поля могут быть датами в моих моделях, и я только хочу анализировать эти поля как даты.

В документации указано, что значение по умолчанию transformResponse равно angular.fromJson, но это не совсем так; он также удаляет префикс защиты JSON-защиты, )]}',\n, перед запуском angular.fromJson (http.js ссылка источника). Так как $resource не может легко отобразить каждый массив действий transformResponse, вам необходимо вручную обрабатывать по умолчанию transformResponse, введя $http, а затем вызывая $http.defaults.transformResponse.concat(your_transformer). Решение, к которому я пришел, в контексте вашего примера будет выглядеть примерно так:

AppFactories.factory("Books", function($resource, $http) {
  var raiseBook = function(book) {
    // "created_at" is declared as NOT NULL in the database
    book.created_at = new Date(book.created_at);
    // "completed_at" is a NULL-able field
    if (book.completed_at) {
      book.completed_at = new Date(book.completed_at);
    }
    return book;
  };

  var raiseBooks = function(books) {
    return books.map(raiseBook);
  };

  return $resource("/books/:id", {
    id: "@id"
  }, {
    update: {
      method: "PUT",
      isArray: true,
      // some_array.concat(...) creates and returns a new Array,
      //   without modifying the original some_array Array
      transformResponse: $http.defaults.transformResponse.concat(raiseBooks),
    },
    save: {
      method: "POST",
      isArray: true,
      transformResponse: $http.defaults.transformResponse.concat(raiseBooks),
    },
  });
});

Обратите внимание, что Date.parse(...) возвращает Number (миллисекунды с эпохи UNIX), поэтому если это вам нужно, вам придется изменить вызовы new Date(...). Но поскольку исходный вопрос требует объектов даты, new Date(...), вероятно, вы хотите.

Ответ 4

Я принял @chbrown ответ и сделаю службу angular для использования в любой ситуации, когда нам нужно прозрачное преобразование дат в запросе/ответе ng-ресурса на лету.

Вот gist.

Пример использования:

var Session = $resource('/api/sessions/:id', {id: '@id'}, {
  query: {
    method: 'GET',
    isArray: true,
    transformResponse: dateTransformer.transformResponse.toDate(['start', 'end'])
  },
  update: {
    method: 'PUT',
    transformRequest: dateTransformer.transformRequest.toLocalIsoString(['start', 'end'])
  }
});

После того, как поля Session.query() "start" и "end" в возвращаемых объектах ресурсов будут объектами JS Date.  После сеанса сервер $update() получит JSON, где объекты JS Date в полях "start" и "end"  будут просто строками в формате ISO без часовой пояс.

Поддерживаемые трансформаторы: toDate, toLocalIsoString, toZonedIsoString.

Эта служба использует time.js для синтаксического анализа и форматирования даты, поскольку родная дата JS не поддерживает  даты без часовой пояс (они всегда анализируются как UTC + 0).

Также Lodash используется для манипуляций с объектами.