Reputation: 298
I am struggling with the following AngularJS problem.
I have a model in a .json file on the server:
{
feedback: []
}
My aim is to push user-generated data from a form to the feedback array, so every feedback comment is represented as a javascript object.
I tried to use a service that returns an angular $resource pointing to the feedback array in my .json. It is implemented as follows (baseURL just points to a port on localhost):
.service('feedbackFactory', ['$resource', 'baseURL', function($resource, baseURL) {
this.getFeedback = function() {
return $resource(baseURL + 'feedback', null, {'update': {method: 'PUT'}});
};
}])
And then I use a controller to handle the resource object in the following way($scope.feedback
is an object storing data generated by a user via a form):
.controller('FeedbackController', ['$scope', 'feedbackFactory', function($scope, feedbackFactory) {
$scope.sendFeedback = function() {
if ($scope.feedback.agree && ($scope.feedback.mychannel === "")) {
$scope.invalidChannelSelection = true;
console.log('incorrect');
}
else {
var feedbackData = feedbackFactory.getFeedback().query(
function(response) {
feedbackData = response;
}
);
feedbackData.push($scope.feedback);
feedbackData.$save();
}
}
}])
Unfortunately I constantly get the following error: feedbackData.$save is not a function
. According to everything I read so far the feedbackData object should have access to the $save method, but something must be wrong. I spent too many hours on this already, so I will be really thankful for any advice.
EDIT.
After some struggling I tried using feedback/:id
approach, so I changed the service like this:
.service('feedbackFactory', ['$resource', 'baseURL', function($resource, baseURL) {
this.getFeedback = function() {
return $resource(baseURL + 'feedback/:id', null, {'update': {method: 'PUT'}});
};
}])
And send data to the json on the server like this:
var feedback = feedbackFactory.getFeedback().query(
function(response) {
feedback = response;
$scope.feedback.id = feedback.length;
feedbackFactory.getFeedback().save({id: $scope.feedback.id}, $scope.feedback);
}
);
This time I of course get the error I actually expected - POST can not be done, since the array in the json in an empty array, so I can not address it and any of its non-existing elements by numerical indices. However, I feel that this approach is better anyway and that I am missing on something simple, but important.
Upvotes: 1
Views: 807
Reputation: 331
The solution is to POST instead of PUT. POSTing will automatically assign an id to the new data as well. I used a service but a factory is fine too.
.service('feedbackFactory', ['$resource', 'baseURL', function ($resource, baseURL) {
this.getFeedback = function () {
return $resource(baseURL + 'feedback/:id', null, {
'save': {method: 'POST'}
});
};
}]);
and then the controller is as simple as this
...
else {
feedbackFactory.getFeedback().save($scope.feedback);
...
}
You don't need to include ":id" in the getFeedback function but including it will allow you to make specific id requests (if you wanted to) like those used in the IndexController in this course.
Upvotes: 0
Reputation: 298
OK, I finally found the solution for my problem that still using the $resource service, so I suppose it means that it conforms to the CRUD constraints (the solution is based on this awesome blog post: https://www.dunebook.com/introduction-to-resource-with-http-service-in-angularjs/#)
So everything is really pretty simple. First I set up my resource via a factory:
.factory('feedbackFactory', ['$resource', 'baseURL', function($resource, baseURL) {
return $resource(baseURL + 'feedback/:id', null, {'update': {method: 'PUT'}});
}])
Then I just create a new instance of the $resource which I fill with an object containing the feedback info that I take from the $scope in which a user generated it. And then I $save it putting it back to the place where it was originally fetched from ($save handles everything on my behalf):
var newFeedback = new feedbackFactory($scope.feedback);
newFeedback.$save();
Hope it will save someone a few hours of headache.
Upvotes: 0
Reputation: 1135
$resource
is meant to work with a RESTful API. In your case, a RESTful API could be like this :
/feedbacks
returns array of feedbacks/feedbacks/:id
returns one specific feedback/feedbacks/new
with POST allows to create a new feedbackquery()
is used to query all instances of the model (query on /feedbacks
), thus is expecting an array. get()
is used to query one instance (usually by its ID, query on /feedbacks/:id
).
In your case, you did not considered a single feedback to be the model object, but instead the whole array of feedback as the model object.
A few problems with your code :
query()
expects an array of instances, your API just returns an instance object. I'm not sure of the behaviour of query()
in this case (I guess it returns an empty array, given you call push
with no error message).$save()
on the returned variable, because $save()
is a method of an instance objet. query()
returns an array of instances. What you want is to use get()
.Now with get()
instead of query()
, it still does not work. Because the result of get()
is asynchronous, you should do everything in the callback. get()
returns a promise object, which is resolved latter (when the server replies to the AJAX request). See below your code snippet, commented on why it does not work.
var feedbackData = feedbackFactory.getFeedback().get(
function(response) {
/* This will be executed later. Note
that affecting response to feedbackData is not necessary,
since you already affected the result of .get(), which has
now resolved */
feedbackData = response;
}
);
/* feedbackData is just a promise, has not resolved into
instance object yet. $save() won't work */
feedbackData.push($scope.feedback);
feedbackData.$save();
Now the correct way :
var feedbackData = feedbackFactory.getFeedback().get(
function() {
/* Note that feedbackData is the object returned by your API,
not directly the array inside the object */
feedbackData.feedback.push($scope.feedback);
feedbackData.$save();
}
);
Finally, $resource
is designed for RESTful APIs. Yours is not really. I think you would better use $http directly.
Upvotes: 0
Reputation: 961
I think this may work
else {
var feedbackData = feedbackFactory.getFeedback().query(
function(response) {
feedbackData = response;
feedbackData.push($scope.feedback);
feedbackData.$save();
}
);
}
the query takes time till it fetch the data, so it is safe to keep your assignation of your variables to the response of your requests inside the success function
In your case I think it starts to read the next lines after your getFeedBack()
finish fetching the data
Upvotes: 1