Pregunta

As a follow up to $resource to return array of [OO] objects how to properly wrap new domain object instances from $resource

app.factory('NoteResource', ['$resource', function($resource) {

  var res = $resource('http://okigan.apiary.io/notes/:id', null, ...);

  res.newInstance = function() {
    return angular.extend(new Note(), res);
  };
}

NoteResource.newInstance() shall return object with standard $save/$update methods.

Best to modify the plunker and watch traffic then "New note" button shall work correctly.

¿Fue útil?

Solución

UPDATED

Here is a way you can continue incorporate your Note constructor into your $resource service...

Add a new instance method to the object returned from the factory:

res.getNew = function(){
  var newNote = new Note();
  newNote.id = undefined; // new note doesn't have id set
  newNote.checked = undefined; // don't need this data passed to server?
  angular.extend(newNote, res);
  return newNote;
}

This method modifies an instantiated Note object (to remove ID and other data that doesn't need to be passed to the server when creating a new note) before merging it with the $resource service and returning that object to the caller.

Call that method from your controller assigning its return value to a local variable. You can also modify its properties from the controller:

var note = NoteResource.getNew();
note.title = 'Eat a lot of junk food';

Call the save method on the note object, passing itself as the sole parameter:

note.save(note).$promise.then(function() {
  $scope.notes = NoteResource.query();
  $scope.newNotes = [];
});

Watch in dev tools and notice that this does result in a JSON payload being sent with the POST containing your note properties (which your previous solution did not).

Hopefully this solution ticks off your box for enforcing a model without having to using a factory in an unintended manner.

Plunker Demo

Otros consejos

Posting my solution for reference:

Latest demo: http://plnkr.co/edit/AVLQItPIfoLwsgDzoBdK?p=preview

Key item to the solution is to wrap returned json objects into javascript objects with correctly set prototype.

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

//
function Note() {
  this.id = '<new id>';
  this.title = '<new title>';
  this.checked = false;
  this.spellCheck = function() {
    // imagine spellchecking logic here
    this.checked = true;
  };
}

app.factory('NoteResource', function($resource) {

  var wrap = function(_) {
    // forward declaration -- function redefined below
  };

  function extend(item) {
    return angular.extend(new Note(), item);
  }

  var url = 'http://okigan.apiary.io/notes/:id';
  var res = $resource(url, null, {
    create: {
      method: 'POST',
      transformResponse: function(data, headersGetter) {
        var item = angular.fromJson(data);
        var headers = headersGetter();

        // also handle HTTP 201 response
        var extra = {
          location: headers.location
        };
        var model = wrap(item);
        angular.extend(model, extra);
        return model;
      }
    },
    query: {
      method: 'GET',
      isArray: true,
      transformResponse: function(data, headersGetter) {
        var items = angular.fromJson(data);
        var models = [];
        angular.forEach(items, function(item) {
          models.push(wrap(item));
        });
        return models;
      }
    },
    get: {
      method: 'GET',
      params: {
        id: '@id'
      },
      transformResponse: function(data, headersGetter) {
        var item = angular.fromJson(data);
        var model = wrap(item);
        return model;
      }
    }
  });

  res.url = url;

  wrap = function(data) {
    var T = Note;
    // read up on http://javascript.crockford.com/prototypal.html
    T.prototype = res.prototype;
    var instance = new T();
    angular.extend(instance, data);
    return instance;
  };

  res.newModel = function() {
    return wrap({});
  };

  return res;
});

app.controller('MainCtrl', function($scope, NoteResource) {
  $scope.name = 'World';
  $scope.notes = NoteResource.query();
  $scope.newNotes = [];

  $scope.spellCheckAllNotes = function() {
    angular.forEach($scope.notes, function(note) {
      note.spellCheck();
    });
  };

  $scope.newNote = function() {
    var note = NoteResource.newModel();
    note.title = 'Buy cheese and bread for breakfast.';
    $scope.newNotes.push(note);

    note.$save().then(function() {
      $scope.notes = NoteResource.query();
      $scope.newNotes = [];
    });

  };
});
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top