django-rest pagination with angularjs - json

I have an angularjs app working nicely with django-rest but have hit an issue by introducing pagination. I have a restservice and controller as per the below
// restservices.js
// API call for all images in an event
services.factory('ImageEvent', function ($resource) {
return $resource(rest_api + '/rest/image/?event_id=:eventId&format=json', {}, {
query: { method:'GET', params:{eventId:''}, isArray:true}
})
});
// controllers.js
// all Images in an event
.controller('ImageEventCtrl', ['$scope', '$stateParams', 'ImageEvent', function($scope, $stateParams, ImageEvent) {
$scope.images = ImageEvent.query({eventId: $stateParams.eventId}, function(images) {
});
}])
this returns the following json
[
{
"id": 13,
"title": "01-IMG_4953.JPG",
},
{
"id": 14,
"title": "02-IMG_4975.JPG",
},
{
"id": 15,
"title": "03-IMG_4997.JPG",
}
]
However if I turn on django-rest pagination it returns the following json:
{
"count": 3,
"next": "/rest/image/?event_id=1&page=2",
"previous": null,
"results":
[
{
"id": 13,
"title": "01-IMG_4953.JPG",
},
{
"id": 14,
"title": "02-IMG_4975.JPG",
}
]
}
This change has caused the following console error and everything fails to work:
Error: [$resource:badcfg] Error in resource configuration. Expected response to contain an array but got an object
Changing the restservice to isArray:false has made no difference. Can my controller be re-written to cope with this and in a perfect world also expose the count, next and previous links?
Thanks

Angular-ui has a pagination directive that I've used with Django Rest Framework before.
http://angular-ui.github.io/bootstrap/#/pagination
To load only X amount of items at a time I have done the following below. Note that I'm using the pagination to recreate the django admin feature in angular.
if request.GET.get('page'):
# Get the page number
page = request.GET.get('page')
# How many items per page to display
per_page = data['admin_attrs']['list_per_page']
begin = (int(page) - 1) * per_page
end = begin + per_page
objects = MODEL.objects.all()[begin:end]
# Serializer for your corresponding itmes. This will grab the particular modelserializer
serializer = serializer_classes[MODEL._meta.object_name](
objects, fields=admin_attrs['list_display']
)
data['objects'] = serializer.data
return Response(data)
My angular code to keep track of page and also allow back button functionality and also update the URL:
modelDetails Factory gets generates the url with the correct page number from pagination
app.factory('modelDetails', function($http, $q){
data = {content: null}
var dataFactory = {}
dataFactory.getObjects = function (app, model, page){
var deferred = $q.defer()
$http.get('api/admin/' + app + '/' + model + '/?page=' + page)
.success(function(result) {
deferred.resolve(result);
});
return deferred.promise
};
return dataFactory
});
$scope.loadObjects = function () {
modelDetails.getObjects(app, model, $scope.currentPage)
.then(function (data){
$scope.headers = data.headers;
$scope.admin_attrs = data.admin_attrs;
blank = new Array()
list_display = data.admin_attrs.list_display
$scope.objects = convertObjects(data.objects, list_display)
$scope.numPerPage = data.admin_attrs.list_per_page
$scope.currentPage = $stateParams.p
$scope.maxSize = 20;
$scope.bigTotalItems = data.object_count;
$scope.numPages = Math.ceil(data.object_count / $scope.admin_attrs.list_per_page);
})
.then( function (data) {
$scope.$watch('currentPage + numPerPage', function(oldVal, newVal) {
var begin = (($scope.currentPage - 1) * $scope.numPerPage)
, end = begin + $scope.numPerPage;
if(oldVal != newVal){
$location.search('p', $scope.currentPage)
}
$rootScope.$on('$locationChangeSuccess', function(event) {
$scope.currentPage = $location.search().p
modelDetails.getObjects(app, model, $scope.currentPage)
.then( function (data) {
// convertObjects just reorders my data in a way I want
$scope.objects = convertObjects(data.objects, list_display)
});
});
});
});
}

Related

Passing drop down list option to controller

I have a drop down list in the form of the select tag as shown below:
<select id = "1">
<option>Amy</option>
<option>Gi-Anne</option>
</select>
I want to pass the selected option - either Amy or Gi Anne to this method of the controller.
public String name (string nameSelected)
{
var query = new NameQuery();
if(nameSelected.Equals('Amy'))
{run a specific query}
else if(nameSelected.Equals('Gi-Anne'))
{run a specific query}
}
How do I pass the parameter of the selected drop down list value to the controller? Appreciate your help and thanks in advance.
This is 'fairly' easy using AngularJS, see this Plunk for a (simulated) example.
The HTML changes to this:
<body ng-app="myApp">
<div ng-controller="myController">
State: {{onChangeText}}
<br/>
<select ng-model="selectedItemId" id="itemList" ng-change="onChange()">
<option value="{{item.id}}" ng-selected="{{item.id == selectedItemId}}" ng-repeat="item in items">{{item.name}}</option>
</select>
<br/>
{{selectedQuery}}
</div>
</body>
With a controller like this:
app.controller("myController", [
"$scope",
"$http",
function($scope, $http){
var self = {};
self.simulatedGetQuery = function() {
console.log($scope.selectedItemId);
$scope.selectedQuery = "";
switch($scope.selectedItemId) {
case "1":
$scope.selectedQuery = "Query Amy";
break;
case "2":
$scope.selectedQuery = "Query Gi-Anne";
break;
}
};
self.httpGetQuery = function() {
$http({
method: 'GET',
url: 'http://somehostname/action/' + $scope.selectedItemId
}).then(function successCallback(response) {
$scope.selectedQuery = response;
}, function errorCallback(response) {
});
};
// -- SCOPED -- //
$scope.selectedItemId = 0;
$scope.items = [
{
"id": 1,
"name": "Amy"
},
{
"id": 2,
"name": "Gi-Anne"
}
];
$scope.onChange = function() {
$scope.onChangeText = "simulated GET triggered.";
self.simulatedGetQuery();
// Use this for actual GET
// self.httpGetQuery
};
// --- //
$scope.onChangeText = "waiting for user input";
$scope.selectedQuery = "no query selected. Chose a person for a valid query.";
}]);
It would need to be fleshed out in a real environment, but I think it will do for a simulated test. Check the scripts in the Plunk for a more detailed perspective on how to do this. All of this is clientside.
The URL of the $http call would be to your backend (MVC or Web API) controller.

best way to parse nested object in AngularJS?

I'm trying to use angularjs for parsing a nested data structure returned from a remote server. I'm really stumped by this pattern because i'm trying to access the "events" data with the following function
$scope.generate_event = function(){
from_date = $scope.dts.from
to_date = $scope.dts.to
from = from_date.getFullYear()+'/'+(from_date.getMonth()+1)+'/'+from_date.getDate()
to = to_date.getFullYear()+'/'+(to_date.getMonth()+1)+'/'+to_date.getDate()
$http.get(server+'rawdata?vids='+$scope.selected_vehicle.id+'&evfields=lat,lon,f_event_time,speed&from='+from+'&to='+to)
.success(function(data){
$scope.report_data = data
$localStorage.report_data = data
$scope.generate()
})
}
Any advice or even a hint on the best approach would be great, i need this running for work and its been a month now. Thanks!
{
"rawData": {
"keys": {
"lat": ["number", "lat"],
"lon": ["number", "lon"],
"speed": ["number", "Speed [mph]"],
"code": ["number", "EVC"],
"vid": ["number", "Vehicle ID"]
},
"keys_order": ["lat", "lon", "speed", "code", "vid"],
"events": [{
"f_lon": -8.3315599999999996,
"code": 4,
"vid": 5,
"lon": -833156,
"f_lat": 51.90831,
"lat": 5190831,
"speed": 78.0
}, {
"f_lon": -8.3741599999999998,
"code": 4,
"vid": 5,
"lon": -837416,
"f_lat": 51.903979999999997,
"lat": 5190398,
"speed": 78.0
}]
}
}
UPDATE: I didnt explain the problem correctly. Here's the generate function
$scope.generate = function(){
$scope.event_config = {
title: 'Events', // chart title, legend etc
/*etc
*etc
*/
data = {}
data.series = [' Events']
data.data = []
this fucker ----->$scope.report_data.events.forEach(function(value, index, array){
o = {}
o.x = value.f_event_time
o.y = [value.lat+'/'+value.lon]
o.tooltip = value.speed
data.data.push(o)
})
$scope.event_data = data
I'm getting error 'forEach undefined'. This is supposed to generate a d3 chart but 'report_data' is intially used to store distance data from a different function for local storage. So do i need a second variable for localStorage? ie $scope.report_event = $localStorage.report_event? Can someone look at the source code if i send it?
$http.get(server+'rawdata?vids='+$scope.selected_vehicle.id+'&evfields=lat,lon,f_event_time,speed&from='+from+'&to='+to)
.success(function(data){
if (data) {
var events = data.rawData.events; // get the events json array
$scope.generate(events);
}
})
and in your controller have the declarative function like
$scope.generate = function(events) {
// your code
}
Solved it with the following
$scope.generate_report = function(){
from_date = $scope.dts.from
to_date = $scope.dts.to
from = from_date.getFullYear()+'/'+(from_date.getMonth()+1)+'/'+from_date.getDate()
to = to_date.getFullYear()+'/'+(to_date.getMonth()+1)+'/'+to_date.getDate()
$http.get(server+'vehicle/'+$scope.selected_vehicle.id+'/counters/deltas/day?from='+from+'&to='+to)
.success(function(data){
$scope.report_data = data
$localStorage.report_data = data
$scope.process()
})
$http.get(server+'rawdata?vids='+$scope.selected_vehicle.id+'&genevcodes=39,40&evfields=lat,lon,f_event_time,mph,speed,code&from='+from+'&to='+to)
.success(function(data){
if(data){
var events = data.rawData.events
}
$scope.report_event = events
$localStorage.report_event = events
$scope.generate()
})
}
just had to declare another localstorage variable for storing events from the JSON object Thanks Yannik

AngularJS Filter REST JSON in Controller

I need to output a count of items from JSON by category (using .length I believe), and would like to manage this in a controller so I can place it to scope anywhere I want. How can I filter REST JSON in a controller?
My sample JSON is as follows:
[
{
"id": "66D5069C-DC67-46FC-8A51-1F15A94216D4",
"articletitle": "artilce1",
"articlecategoryid": 1,
"articlesummary": "article 1 summary. "
},
{
"id": "66D5069C-DC67-46FC-8A51-1F15A94216D5",
"articletitle": "artilce2",
"articlecategoryid": 2,
"articlesummary": "article 2 summary. "
},
{
"id": "66D5069C-DC67-46FC-8A51-1F15A94216D6",
"articletitle": "artilce3",
"articlecategoryid": 3,
"articlesummary": "article 3 summary. "
},
{
"id": "66D5069C-DC67-46FC-8A51-1F15A94216D7",
"articletitle": "artilce4",
"articlecategoryid": 1,
"articlesummary": "article 3 summary. "
},
]
My Resource is as follows:
// Articles by ID
pfcServices.factory('pfcArticles', ['$resource', function ($resource) {
return $resource('https://myrestcall.net/tables/articles', {},
{
'update': { method:'PATCH'}
});
}]);
My Controller is as follows:
// Count number of total articles
pfcControllers.controller('pfcArticleCountCtrl', ['$scope', 'pfcArticles', function ($scope, pfcArticles) {
$scope.articlecount = pfcArticles.query();
I believe I would need to add something to the controller (ex. $scope.cat1 = filter(my logic to get my category here), but am a little lost on how to query this so I have a count of items per category.
Build a filter like this.
angular.module('FilterModule', [])
.filter( 'cat1', function() {
return function(yourJSON) {
var count = 0;
yourJSON.forEach(function(item) {
switch(item.articlecategoryid) {
case 1:
count++;
break;
default:
console.log('item is not of category 1');
}
});
return count;
};
});
Inject the $filter service and fetch the cat1 filter. Now you can use it as $scope.cat1 = $filter('cat1')(yourJSON);

Parsing doubly nested JSON object to MongoDB

Schema for my MongoDB model:
var resultsSchema = new mongoose.Schema({
start_date: String,
end_date: String,
matches:[{
id:Number,
match_date:String,
status:String,
timer:Number,
time:String,
hometeam_id:Number,
hometeam_name:String,
hometeam_score:Number,
awayteam_id:Number,
awayteam_name:String,
awayteam_score:Number,
ht_score:String,
ft_score:String,
et_score:String,
match_events:[{
id:Number,
type:String,
minute:Number,
team:String,
player_name:String,
player_id:Number,
result:String
}]
}]
});
Example of JSON data coming from the server:
"matches":
[
{
"match_id":"1234"
"match_date":"Aug 30"
...
...
"match_events":
[
{
"event_id":"234",
"event_minute":"38",
...,
...
},
{
"event_id":"2334",
"event_minute":"40",
...,
...
}
],
{
"match_id":"454222"
"match_date":"Aug 3"
...
...
"match_events":
[
{
"event_id":"234",
"event_minute":"38",
...,
...
},
....
My current implementation works for parsing just the matches (i.e the first array). But I can't seem to access the inner array properly.
async.waterfall([
function(callback) {
request.get('http://football-api.com/api/?Action=fixtures&APIKey=' + apiKey + '&comp_id=' + compId +
'&&from_date=' + lastWeek_string + '&&to_date=' + today_string, function(error, response, body) {
if (error) return next(error);
var parsedJSON = JSON.parse(body);
var matches = parsedJSON.matches;
var events = parsedJSON.matches.match_events;
var results = new Results({
start_date: lastWeek_string,
end_date: today_string,
matches:[]
});
_.each(matches, function(match) {
results.matches.push({
id: match.match_id,
match_date: match.match_formatted_date,
status:match.match_status,
timer:match.match_timer,
hometeam_id:match.match_localteam_id,
hometeam_name:match.match_localteam_name,
hometeam_score:match.match_localteam_score,
awayteam_id:match.match_visitorteam_id,
awayteam_name:match.match_visitorteam_name,
awayteam_score:match.match_visitorteam_score,
ht_score:match.match_ht_score,
ft_score:match.match_ft_score,
et_score:match.match_et_score,
match_events:[]
});
});
_.each(events, function(event) {
results.matches.match_events.push({
id:event.event_id,
type:event.event_type,
minute:event.event_minute,
team:event.event_team,
player_name:event.event_player,
player_id:event.event_player_id,
result:event.event_result
});
});
I understand that the second _.each loop should be iterating for every match, since very match has it's own events subarray. I'm just not sure how to structure this and have been struggling with it for a while.
I tried nesting that loop inside the _.each(matches, function(match) { loop but that didn't work.
Thank you.
Edit: How could I get this to work?
var results = new Results({
start_date: lastWeek_string,
end_date: today_string,
matches:[
match_events: []
]
});
Because then as #zangw says I could construct the match_events array first, append it to matches, and so on.

Using MongoJs and Node.JS to store data

I have successfully connected to MongoDb and used a form to send some data. I would now like to store the data in a json document with a collection. So my current output is:
{
"_id": "53be957007d2c838083046a6",
"subscriberinfo": "X",
"grouporpolicynumber": "X",
"title": "X",
"clientnumber": "X",
"xnumber": "X",
"postedOn": "2014-07-10T13:30:24.499Z"
}
I would like it to look like:
{
"_id": "53be957007d2c838083046a6",
"ReferenceInfo": {
"subscriberinfo": "00003",
"grouporpolicynumber": "Direct",
"title": "SNP",
"clientnumber": "VG00003M",
"HICnumber": "264134187C"
}
"postedOn": "2014-07-10T13:30:24.499Z"
}
Current code looks like this:
function postNewJob(req , res , next){
var job = {};
job.subscriberinfo = req.params.subscriberinfo;
job.grouporpolicynumber = req.params.grouporpolicynumber;
job.title = req.params.clientreportingcategory;
job.clientnumber = req.params.clientnumber;
job.HICnumber = req.params.hicnumber;
job.postedOn = new Date();
res.setHeader('Access-Control-Allow-Origin','*');
memberInfo.save(job , function(err , success){
console.log('Response success '+success);
console.log('Response error '+err);
if(success){
res.send(201 , job);
return next();
}else{
return next(err);
}
});
}
Have you tried something like this?
function postNewJob(req , res , next){
var job = {
referenceInfo: {
subscriberinfo : req.params.subscriberinfo,
grouporpolicynumber : req.params.grouporpolicynumber,
title : req.params.clientreportingcategory,
clientnumber : req.params.clientnumber,
HICnumber : req.params.hicnumber
},
postedOn: new Date();
};
res.setHeader('Access-Control-Allow-Origin','*');
memberInfo.save(job, function(err, job){
if(err) {
console.error('Response error ' + err);
next(err);
} else {
console.log('Response success ' + job);
res.send(201, job);
next(req, res);
}
});
}
On another note, I changed around your callback function that's passed in to save. Mongo will call the function, passing in error and data (data in this case being job) which allows you to conditionally log error or success depending on the status of the save. Also, because those are asynchronous functions, returning the call of next() will not actually impact the code. Also, when calling next(), it is advisable to pass along the req and res from before, so that more actions can be taken from that data.
Hope that helps!