I want to modify one object via a REST API using Restangular on the client side. I do the following:
Restangular.setBaseUrl('/api/v1');
return Restangular.one('lists', item.listId).one('items',item.order).get().then(function(elem) {
elem.text = item.text;
elem.put();
});
But the put method gets the wrong URL missing the ID for the last member (item.order).
The expected url is /lists/2/items/22 but i get /lists/2/items and the PUT fails.
What could I be doing wrong?
Ok, let me try a theory :)
My comment above was a bit lame because I was focusing on the first part of the code and not the put().
The first part is perfect, you tell Restangular to build the URL with one() methods and the GET request must be very fine.
Then, base on a new Restangular-aware object, you try to persist the change in your API.
However, I believe that Restangular does not actually know what field to use as id in order to build the right query for your elem object.
Did you try to configure, in your Restangular Entity Provider the fields property?
RestangularProvider.setRestangularFields({
id: "orderId",
});
I believe that specifying the right field as id would suffice.
As a bonus, it seems that .save() will use PUT or POST accordingly if it's a new object or not.
Hope this helps
Related
I'm trying to PUT the data and my model doesn't have an id.
Is it possible to explicitly tell the Save() method to PUT the data irrespective of ID.
The save method has an options parameter that can override anything on the XHR:
model.save(newVals, { type: 'PUT' })
You can also override the isNew method. PUT vs POST is determined by the result of that method. You'll also want to make sure the URL is being created correctly for new and non-new objects.
Also consider setting the idAttribute correctly so that your model does have an id field that can be used to generate a correct url. Using POST and PUT correctly (POST new items, PUT updates to items) makes your api more intuitive.
So I have the following angular 1.3 code
Restangular.one('user',270).get().then(function(existingUser){
existingUser.password = "foo";
existingUser.put();
});
Which grabs the user at http://api.dev/user/270 fine, however, the existingUser.put(); makes a PUT request to http://api.dev/user, ignoring the ID.
Changing to
Restangular.one('user/270').get().then(function(existingUser){
existingUser.password = "foo";
existingUser.put();
});
works fine, however looking at the examples on the Restangular homepage, it appears my original code should also work fine. Any pointers to whats going wrong?
Solved, issue was related to the existingUser being returned having a existingUser.userID attribute rather than a existingUser.id attribute.
Its this attribute that restangular seems to use for future post/put requests, rather than the id passed into the one() command.
I'm trying to use this attribute on methods in the web API for a custom module:
[DnnModuleAuthorize(AccessLevel = DotNetNuke.Security.SecurityAccessLevel.Edit)]
but no matter what SecurityAccessLevel I set, I always get a 401 unauthorized response.
I was able to make the code work by adding:
[AllowAnonymous]
on the method, and adding:
if (!ModulePermissionController.CanEditModuleContent(this.ActiveModule))
return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "You do not have permission to access this content.");
to the beginning of my method, but it seems like this is a workaround that I really shouldn't need because it's exactly what that attribute is there for. I'm running DNN 7.2.1.
Anyone have any idea where I'm going wrong with the attribute?
Turns out it was actually related to the anti-forgery token. I'm using Angular so I'm setting my headers manually in my Angular service rather than using the built-in ServicesFramework setModuleHeaders method and was only setting the TabId and ModuleId. I didn't think the [AllowAnonymous] attribute would override the anti-forgery stuff but it looks like it definitely does (which is good to know).
Full solution for those doing the same:
var baseUrl = sf.getServiceRoot('[yourmodulename]') + '[controller]';
var config = {
headers: {
'ModuleId': sf.getModuleId(),
'TabId': sf.getTabId(),
'RequestVerificationToken': sf.getAntiForgeryValue()
}
};
Do you have the SupportedModules attribute applied to your controller (or action method)? If so, I'd guess there's a mismatch between the name you're passing in there and the real name in DNN (you should be passing in the desktop module name). Try removing that attribute and seeing if it helps.
The same process that sets ActiveModule and the current user (and would thus make your check in the action method work) should be responsible for implementing the DnnModuleAuthorize attribute's check. So, that's definitely perplexing. Maybe that changed, and if you just pass ModuleId but not TabId in the headers, then it sets ActiveModule, but won't authenticate?
Have you looked at the traffic in Fiddler and made sure that the ModuleId and TabId headers are being sent correctly? Does being logged in as a super-user (i.e. host-level user) affect any of the auth checks (if so, perhaps the URL isn't being constructed properly, and DNN is identifying the wrong portal)?
When you initialize the ServicesFramework, make sure you do it inside a document.ready function.
var self = {};
jQuery(document).ready(function ($) {
self.sf = $.ServicesFramework(<%=ModuleID %>);
});
More info: www.dnnsoftware.com/forums/threadid/507753/scope/posts/services-framework-problems
I am trying to make a PUT request using RESTAngular. I am fairly new to Angular as well as RESTAngular.
Following is code snippet which works.
$scope.itemToUpdate = Restangular.all($scope.slug);
$scope.itemToUpdate.getList().then(function(items){
var item = items.one($routeParams.id);
item.name = $scope.singular.name;
item.description = $scope.singular.description;
item.put();
});
This doesn't work.
$scope.itemToUpdate = Restangular.all($scope.slug);
$scope.itemToUpdate.getList().then(function(items){
var item = items.one($routeParams.id);
item = $scope.singular;
item.put();
});
Don't know what am I doing wrong.
$scope.singular gets it data initially as following. Restangular.one('roles', $routeParams.id).getList().$object.
Basically idea is to update this model from form and also prepopulate the form with relevant data when slug matches the id. I can change the way things are wired up if required. So feel free to suggest best practices.
Edit 2
This official demo is very helpful in solving the issue.
http://plnkr.co/edit/d6yDka?p=preview
When Restangular returns resouce array\object it adds some methods on the object such as put which has been wired up to update the object on put call to server.
In second case you are assigning item=$scope.singular. $scope.singular may not be a Restangular object and hence does not work.
This official demo is very helpful in solving the issue. http://plnkr.co/edit/d6yDka?p=preview
Using Restangular for AngularJS, keep getting an object object from Mongolab.
I'm sure it has to do with Promise but not sure how to use/implement this coming from old Java OO experience.
(Side note, would something like Eloquent Javascript, some book or resource help me understand the 'new' Javascript style?)
The small web app is for Disabled Students and is to input/edit the students, update the time they spend after school and then output reports for their parents/caregivers every week.
Here's the code that returns undefined when popping up a new form (AngularJS Boostrap UI modal)
I personally think Restangular & the documentation is a great addition so hope it doesn't dissuade others - this is just me not knowing enough.
Thanks in advance
app.js
...
$scope.editStudent = function(id) {
$scope.myStudent = Restangular.one("students", id);
console.log($scope.myStudent);
}
I'm the creator of Restangular :). Maybe I can help you a bit with this.
So, first thing you need to do is to configure the baseUrl for Restangular. For MongoLab you usually do have a base url that's similar to all of them.
Once you got that working, you need to check the format of the response:
If your response is wrapped in another object or envelope, you need to "unwrap" it in your responseExtractor. For that, check out https://github.com/mgonto/restangular#my-response-is-actually-wrapped-with-some-metadata-how-do-i-get-the-data-in-that-case
Once you got that OK, you can start doing requests.
All Restangular requests return a Promise. Angular's templates are able to handle Promises and they're able to show the promise result in the HTML. So, if the promise isn't yet solved, it shows nothing and once you get the data from the server, it's shown in the template.
If what you want to do is to edit the object you get and then do a put, in that case, you cannot work with the promise, as you need to change values.
If that's the case, you need to assign the result of the promise to a $scope variable.
For that, you can do:
Restangular.one("students", id).get().then(function(serverStudent) {
$scope.myStudent = serverStudent;
});
This way, once the server returns the student, you'll assign this to the scope variable.
Hope this helps! Otherwise comment me here!
Also check out this example with MongoLab maybe it'll help you :)
http://plnkr.co/edit/d6yDka?p=preview