I'm setting up a project using django-tastypie REST API and AngularJS. I'm fine with reading things from the json file through angular, but I cannot find a decent tutorial that would show me how to make even a simple CRUD application that isn't saving all the information in an object or whatever, but is manipulating the database through the tastypie api. Can any of you show me a tutorial of such sort or maybe just show me some sample code for this?
Thank you.
Use $resource - A factory which creates a resource object that lets you interact with RESTful server-side data sources.
Let's say you have Django model Book, and tastypie resource named BookResource. It's URL is /api/v1/book/. As you know, this URL actually is a resource, that means you can manipulate data in your Book model with GET, POST, DELETE, etc. requests.
You can "map" the Angular $resource to this API resource in a way:
someModule.factory('bookResource', ['$resource', function($resource) {
var apiResourceUrl = "/api/v1/book/:bookId/";
// id - your model instance's id or pk, that is represented in API resource objects.
var resource = $resource(apiResourceUrl, {bookId: '#id'}, {
all: {
method: 'GET', params: {}, // GET params that will included in request.
isArray: true, // Returned object for this action is an array (miltiple instances).
},
get: {
method: 'GET',
},
// [Define custom save method to use PUT instead of POST.][2]
save: {
/* But, the PUT request requires the all fields in object.
Missing fields may cause errors, or be filled in by default values.
It's like a Django form save.
*/
method: 'PUT',
},
// [Tastypie use POST for create new instances][3]
create: {
method: 'POST',
},
delete: {
method: 'DELETE',
},
// Some custom increment action. (/api/v1/books/1/?updateViews)
updateViews: {
method: 'GET',
params: {"updateViews": true},
isArray: false,
},
});
}]);
someModule.controller('bookCtrl', ['$scope', '$routeParams', 'bookResource',
function ($scope, $routeParams, bookResource) {
if ("bookId" in $routeParams) {
// Here is single instance (API's detail request)
var currentBook = bookResource.get({bookId: $routeParams.bookId}, function () {
// When request finished and `currentBook` has data.
// Update scope ($apply is important)
$scope.$apply(function(){
$scope.currentBook = currentBook;
});
// And you can change it in REST way.
currentBook.title = "New title";
currentBook.$save(); // Send PUT request to API that updates the instance
currentBook.$updateViews();
});
}
// Show all books collection on page.
var allBooks = bookResource.all(function () {
$scope.$apply(function(){
$scope.allBooks = allBooks;
});
});
// Create new
var newBook = new bookResource({
title: "AngularJS-Learning",
price: 0,
});
newBook.$save();
}]);
Angular's docs provide more information how to make usage of resource really incredibly.
Here is the problem with urls. As I remember, Angular will send request to /api/v1/books/1 (without slash in the end) and you'll get 404 from tastypie. Let me check this.
[2] http://django-tastypie.readthedocs.org/en/latest/interacting.html#updating-an-existing-resource-put
[3] http://django-tastypie.readthedocs.org/en/latest/interacting.html#creating-a-new-resource-post
Related
Hi I am developing web application in angularjs. I am making lot of http calls to interact with web api. As a result i have to send lot of data in headers too. I have created factory for get data as below. These data should should be passed in headers. Below is my factory.
var myapp = angular.module('RoslpApp');
myapp.factory("ScrollFunction", ['$location', '$anchorScroll', '$cookieStore', function ($location, $anchorScroll, $cookieStore) {
return {
getheaders: function () {
var cookiePreferredLanguage = $cookieStore.get('PreferredLanguage');
var headers={
Logintoken: $cookieStore.get('LoginToken'),
LoginId: $cookieStore.get('LoginID'),
RequestedPlatform: "Web",
RequestedLanguage: cookiePreferredLanguage
};
return headers;
}
}
}
]);
Below is my sample http call.
function getpermissiondetails() {
var headersdata = ScrollFunction.getheaders();
$scope.Create = "Create";
var getsavedvechilceUrl = baseurl + "api/RolesPermission/getgroups";
var request = {
url: getsavedvechilceUrl,
method: 'GET',
headers: {
RequestedPlatform: headersdata.RequestedPlatform,
RequestedLanguage: headersdata.RequestedLanguage,
Logintoken: headersdata.LoginToken,
LoginId: headersdata.LoginID
}
};
$http(request).then(function (response) {
$scope.groups = response.data.data;
}, function (error) {
})
}
What i want in header is i already have data in var headersdata = ScrollFunction.getheaders(); Is it possible to pass something like headers: { headersdata } because i can make add headers in factory. No need to expand it in each http call. I tried headers: { headersdata } but this is not sending any data to api. May i know is there any better solution? Any help would be appreciated. Thank you.
I guess this is a code refractoring question.
Q: Can I avoid doing the messy property assignment by doing something shortcut like headers={ factory data }?
A: Ok. I probably did a bad job paraphrasing your question. I'm just trying to make it more understandable.
Short answer: Yes. You can further shorten your code by doing this.
var request = {
url: getsavedvechilceUrl,
method: 'GET',
headers: ScrollFunction.getheaders()
};
Why?
Your function getheaders() is already returning a javascript object which is exactly what request.headers is looking. An object {}.
Since getHeaders() is already returning an object you can just plug it straight into request.headers. That is, you no need to re-create an object and reassign the property back in one by one.
Hope this helps.
I have this $http request interceptor
app.config(function($httpProvider) {
$httpProvider.interceptors.push(function() {
return {
request: function(req) {
// Set the `Authorization` header for every outgoing HTTP request
req.headers['cdt_app_header'] = 'tamales';
return req;
}
};
});
});
Is there any way we can add a header or cookie to every $http request, but keep the header value secure / not visible with JavaScript?
We can add an obfuscation layer with this header to prevent easy access to our API endpoints, but I am wondering about a more truly secure solution.
Cookies are used for secure sessions, and these are more secure because they cannot be accessed with JavaScript. Say we have a user who can do this request with front-end code:
GET /api/users
we don't really want them to be able to make a simple request with cURL or a browser without an extra piece of information. The cookie we give them will give them the ability to use the browser address bar to make a GET request to /api/users, but if we add the requirement to have another cookie or header in place, then we can prevent them from accessing endpoints that are authorized for, in a format that we don't really want them to use.
In other words, we want to do our best to give them access, but only in the context of a front-end Angular app.
I can't add a comment because of my rep but what are you doing on the back-end to authorize users? If the cookie is signed and contains user permissions it shouldn't matter that the header is visible in the client as it will also be verified on the back-end API call.
in this sample i used HttpRestService to get RESTful API, read this article
at first we create a service to get our configs in this sample is getConfigs
we use getConfigs in the app.run when application is started, after get the configs we set them all in the header as sample.
after that we can get userProfile with new header and also secure by call it from our controller as you see.
in this sample you need to define apiUrl, it's your api host url, remember after logout you can remove the header, also you can define your configs dynamically to make more secure for your application.
HttpRestService.js github link
app.js
var app = angular.module("app", ["HttpRestApp"]);
app.service
app.service("service", ["$http", "$q", "RestService", function (http, q, restService) {
this.getConfigs = function () {
var deferred = q.defer();
http({
method: "GET",
async: true,
headers: {
"Content-Type": "application/json"
},
url: "you url to get configs"
}).then(function (response) {
deferred.resolve(response.data);
}, function (error) {
deferred.resolve(error);
});
return deferred.promise;
}
var api = {
user: "User" //this mean UserController
}
//get user with new header
//this hint to your api with this model "public Get(int id){ return data; }"
//http://localhost:3000/api/users/123456
this.getUserProfile= function(params, then) {
restService.get(params, api.user, true).then(then);
}
}]);
app.run
app.run(["RestService", "service", function (restService, service) {
var header = {
"Content-Type": "application/json"
}
//get your configs and set all in the header
service.getConfigs().then(function (configs) {
header["systemId"] = configs.systemId;
});
var apiUrl = "http://localhost:3000/";
restService.setBaseUrl(apiUrl, header);
}]);
app.controller
app.controller("ctrl", ["$scope", "service", function ($scope, service) {
$scope.getUserProfile = function () {
//this is just sample
service.getUserProfile({ id: 123456 }, function (data) {
$scope.user = data;
});
}
$scope.getUserProfile();
}]);
Trying to take a url and in an AngularJS controller redirect that request to the best place to get the json data. The VideoSearchCtrl is bound to the search form. The url generated is correct for the template so I'm using the controller to redirect it to the place for the json data.
GuideControllers.controller('VideoSearchCtrl', ['$scope', 'VideoSearch',
function($scope, VideoSearch) {
var pattern = new RegExp(".*/search\\?=(.*)");
var params = pattern.exec( document.URL )[1];//redirect to videos to execute the search for the data
$scope.videos = VideoSearch.query({ resource: "videos", action: "search", q: params });
}
]);
This sends /videos/search?q=xyz in to the query. The factory creates the resource:
var VideoSearchServices = angular.module('VideoSearchServices', ['ngResource']);
VideoSearchServices.factory('VideoSearch', ['$resource',
function($resource){
return $resource("/:resource/:action:params", {resource: "#resource", action: "#action", params: "#params"}, {
query: {
isArray: true,
method: "GET",
headers: {
"Accept": "application/json",
"X-Requested-With": "XMLHttpRequest"
}
}
});
}
]);
But the server gets the url as /videos/search%fq=xyz, not /videos/search?q=xyz and therefore the "show" method is being invoked instead of a custom "search" action. Obviously there is some escaping somewhere? Or maybe the "?" is also a special pattern the resource factory looks for? Probably obvious to someone used to AngularJS or javascript for that matter.
I have a template for search and the json is retrieved from a different location. Both work but I can't ask for the json with the above code.
First, do:
return $resource("/:resource/:action", {resource: "#resource", action: "#action"}, {
Then:
$scope.videos = VideoSearch.query({ resource: "videos", action: "search", q: params });
The point is params are not a part of the url you have to declare to the resource, you just declare resource and action then you add params which is natural for all routes
Actually there is a better way to do this with $location from Angular and jQuery's extend that should work with any future params that are added. Will only have to add the new params to the query factory.
GuideControllers.controller('VideoSearchCtrl', ['$scope', '$location', 'VideoSearch',
function($scope, $location, VideoSearch) {
var route = jQuery.extend(
{ resource: "videos", action: 'search' },
$location.search()
);
$scope.videos = VideoSearch.query(route);
}
]);
Maybe I'm going about this the wrong way, i'm not sure, but I have 2 services, one is a user service which gets a bunch of details about the user from the server, the other being one that relies on some user details from the user service and then makes some more calls to the server to get other information.
Anyway, because of the async stuff that goes on when the 2nd service makes the calls the information required from the user server has not yet been populated.
I know Angular services can depend on one another, but not in this context it would appear?
factory('User', ['$resource', function ($resource) {
return $resource(usersUrl, {}, {
//The data model is loaded via a GET request to the app
query: {method: 'GET', params: {}, isArray: false},
putupdate: {method: 'PUT', params:{}}
});
}])
.factory('UserData', function() {
var data = {}
data.userinfo = {};
if(data = {}){
}
return {
updateinfo: function(newdata) {
data.userinfo = newdata;
// alert(data.userinfo.user)
},
userinfo: data
}
})
.factory('PlansData', ['UserData', 'User', '$rootScope', function(userData, user, $rootScope) {
var data = {}
data.plansinfo = {};
//alert(userData.data.userinfo.user.email)
if(data = {}){
}
return {
updateinfo: function(newdata) {
alert(user.query())
data.plansinfo = newdata;
},
plansinfo: data
}
}])
So I have a user service and a caching userdata service, but if I ever try and call anything from UserData in the PlansData service I get undefined.
How do I get plansData to wait for UserData to have some data?
Thanks
Tom
I'm not sure what you're trying to accomplish, but this line of code:
if(data = {}){
}
In both your services is wiping out your data object. You're setting the whole data object to be {}
I'm new to angularJS and have a question about caching etc.
I have a wizard with two steps, I want to be able to click back and next and have the forms still filled out as the user had them.
In my page1Partial i have this:
<li ng-repeat="pick in picks | orderBy:orderProperty">
<b><span ng-bind="pick.name"/></b>
<input type="checkbox" ng-model="pick.checked" ng-click="updateBasket(pick)">
</li>
When i go to the next page, then click back the checkboxs are cleared, and its because my RESful call to a java service is called again. How can I cache this response?
From my controller, this hits my REST web service every time.
$scope.picks = Pick.query();
My service
angular.module('picksService', ['ngResource']).
factory('Pick', function ($resource) {
return $resource('rest/picks/:id', {}, {
'save': {method: 'PUT'}
});
});
Since 1.1.2 (commit), all the $httpConfig options are directly exposed in $resource action objects:
return {
Things: $resource('url/to/:thing', {}, {
list : {
method : 'GET',
cache : true
}
})
};
if you replace $resource with $http then you can directly use below code
$http({
method: 'PUT',
url: 'url',
cache:true
});