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);
Related
I'm building a rest api that uses Sequelize to interact with the database. A query looks like this:
function read_category(req, res) {
Category.findById(req.params.categoryId, {rejectOnEmpty: true}).then(category => {
res.json(category);
}).catch(Sequelize.EmptyResultError, function () {
res.status(404).json({message: 'No category found'});
}
).catch(function (err) {
res.send(err);
}
);
}
Now I want the category object that is returned from Sequelize and then returned to the user to include the linkto the ressource. I could do:
category.dataValues.link = config.base_url + 'categories/' + category.dataValues.id;
Which would result in:
{
"id": 1,
"name": "TestCategory 1",
"position": 1,
"createdAt": "2018-08-19T11:42:09.000Z",
"updatedAt": "2018-08-19T11:42:09.000Z",
"link": "http://localhost:3000/categories/1"
}
Since I have more routes than this one I'm wondering if there's a dynamic way to add the link property to every category. I don't want to save it in the database because the base-url might differ.
Thanks!
Better way to do it is , create a getter method :
const Category = sequelize.define( 'category' , {
....
your_fields
....
},
{
getterMethods:{
link() {
return config.base_url + 'categories/' + this.id;
}
}
});
module.exports = Category;
Then
Category.findAll(...).then(categories => {
// Now there is no need to append data manually , it will added each time when you query
console.log(categories); // <-- Check the output
})
I have a model City which hasMany CitiesDescriptions.
I would like to update City, but whenever the data contains CitiesDescriptions I would like them to be updated as well.
This is what my data looks like:
{
"id": 4263,
"name": "Accra",
"country_id": 9,
"slug": "accra-5",
"code": "ACD",
"hdo_code": "",
"tourico_code": null,
"active": true,
"CitiesDescriptions": [
{
"id": 1,
"lang": "es",
"description": "text text"
}
]
}
So, in this case I want the data within CitiesDescriptions to be updated.
In the case that one of the objects inside CitiesDescriptions is a new record (doesn't have ID) I want it to be created.
Anyway, I haven't found a way to do this, whatever I found fails in some way. The closest I found that almost works is this:
var city = models.City.build(params, {
isNewRecord : false,
include : [models.CitiesDescription]
});
Promise.all([
city.save(),
city.CitiesDescriptions.map(function (description) {
if (description.getDataValue('id')) {
return description.save();
} else {
description.city_id = city.id;
return models.CitiesDescription.create(description.dataValues);
}
})
]).then(function(results) {
res.send(results);
});
This works except with the data doesn't have a CitiesDescriptions key.
Still, looks way to complicated to do, specially if tomorrow I have more associated models.
Isn't there any way to do this without so much hassle?
EDIT:
Couldnt find a solution, I made this and it works as intended.
var params = req.body;
var promises = [];
promises.push(models.City.update(params, {
where: {
id: parseInt(req.params.id)
}
}));
if(params.CitiesDescriptions) {
promises.push(
params.CitiesDescriptions.map(function (description) {
return models.CitiesDescription.upsert(description, {individualHooks: true});
})
);
}
Promise.all(promises).then(function(results) {
res.send(results);
});
I am working on a single page app using dynamic routing. I get my bookid from the URL as below
myApp.controller('BookCtrl', function($scope,$routeParams) {
$scope.mybookid=$routeParams.bookurl;
});
Here is my data
var bookdata = {
"records": [
{
"seriesid": "SpectrumSeries",
"name": "White Curse",
"bookid": "WhiteCurse",
"image": "book1",
}
...
]};
In the above controller, how to I filter out an object that has bookid that equals to $scope.mybookid? I know I can do this by ng-repeat and filter but is there a more efficient way?
Use filter in javascript:
var filteredBooks = bookdata.records.filter(function(book){
return book.bookid == $scope.mybookid;
});
The filteredBooks array will contains all the book with your bookId.
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)
});
});
});
});
}
difficult title for a simple issue :)
say we have a list of books
they are in different categories, these categories are an array property on the book
we want to transform this json, into a list of unique categories, with the books under the category
first off: the json:
[
{
"description":"book 1 description",
"title":"book 1 title",
"id":"4",
"logo":"",
"image":"",
"categories":[
{
"id":"1",
"title":"Logistiek"
},{
"id":"2",
"title":"Finances"
}
]
},
{
"description":"book 2 description",
"title":"book 2 title",
"id":"1",
"logo":"",
"image":"",
"categories":[
{
"id":"3",
"title":"Telecom"
}
]
},
{
"description":"book 3 description",
"title":"book 3 title",
"id":"2",
"logo":"",
"image":"",
"categories":[
{
"id":"3",
"title":"Telecom"
}
]
},
{
"description":"book 4 description",
"title":"book 4 title",
"id":"3",
"logo":"",
"image":"",
"categories":[
{
"id":"2",
"title":"Finances"
}
]
}
]
now what i managed myself:
i started by mapping off all the categories:
var data = {} // lets say all json is inhere...
var res = _(data).map(function(m){
return m.categories;
});
this I flatten into 1 array of categories (because now its an array per book.
res = _(res).flatten();
this gives me an array of all category items, though this has doubles in it.
now i'm not getting much further than this yet.
i tried using the union method before flattening but that didnt help out
i tried the uniq on the bigger array but i think i have to break em down into separate arrays for the uniq to work
i'm kind of stuck getting the unique values out of that array of categories.
after that I can manage to add the books under the categories
If anyone got some ideas, or maybe tell me that i'm doing this completely wrong :) go ahead tell me, if i can do it shorter or with better performance by going another direction feel free to throw it at me.
update1
ok, now i got a little further but i'm pretty sure it's not ideal, (too many steps, i get a feeling getting a unique list could go quicker than these steps)
// get all categorie arrays
var res = _(data).map(function(m){
return m.categories;
});
// flatten them
res = _(res).flatten();
// reduce to unique ID array
var catIds = _(res).pluck('id');
catIds = _(catIds).uniq();
// from here on create an array with unique categories
var cats = [];
_(catIds).each(function(cId){
var s = _(res).filter(function(c){
return c.id === cId;
});
cats.push(_(s).first());
});
can I do this quicker?
see jsFiddle in action...
http://jsfiddle.net/saelfaer/JVxGm/
update 2
ok, i got further, thanks to the help from you guys below,
but i still feel like using 2 eaches is not the best way to get to the end.
var json = [] // lets say the above json is in this variable.
var books = _(json).map(function(book) {
var cats = book.categories;
delete book.categories;
return _(cats).map(function(cat) {
return _({}).extend(book, { category: cat });
});
});
books = _(books).flatten();
var booksPerCategory = [];
_(books).each(function(book){
if(!_(booksPerCategory).any(function(cat){
return cat.id === book.category.id;
}))
{ booksPerCategory.push(book.category); }
});
_(booksPerCategory).each(function(cat){
var mods = _(books).filter(function(book){
return book.category.id === cat.id;
});
cat['modules'] = mods;
});
you can see what i wanted to recieve: the booksByCategory array
and what i got via the help from below: the books object
both in this jsfiddle: http://jsfiddle.net/saelfaer/yWCgt/
Here's some help, it does not solve your entire problem, but I think you should be able to take it from there ;)
The key is in using groupBy. Something like the following should give you a good start!
_.groupBy(data, function (book) {
return _.map(book.categories, function (category) {
return category.id;
});
});
You could do something like this:
var books = _(json).map(function(book) {
var cats = book.categories;
delete book.categories;
return _(cats).map(function(cat) {
return _({}).extend(book, { category: cat.id });
});
});
books = _(books).flatten();
books = _(books).groupBy(function(book) { return book.category });
Demo: http://jsfiddle.net/ambiguous/jaL8n/
If you only want a slice of each book's attributes then you can adjust this:
return _({}).extend(book, { category: cat.id });
accordingly; for example, if you just want the titles and category IDs:
return _(cats).map(function(cat) {
return {
category: cat.id,
title: book.title
};
});
Demo: http://jsfiddle.net/ambiguous/EFLDR/
You don't need to explicitly generate a set of unique category IDs, you just have to arrange your data appropriately and everything falls out on its own (as is common with functional approaches).
UPDATE: Based on the comments, I think you just need to add a reduce after the flatten instead of a groupBy:
var books = _(json).map(function(book) {
var cats = book.categories;
delete book.categories;
return _(cats).map(function(category) {
return {
category: category,
book: book
};
});
});
books = _(books).flatten();
books = _(books).reduce(function(h, b) {
if(!h[b.category.id])
h[b.category.id] = _(b.category).extend({ modules: [ ] });
h[b.category.id].modules.push(b.book);
return h;
}, { });
Demo: http://jsfiddle.net/ambiguous/vJqTz/
You could also use chain and some named functions to make it easier to read:
function rearrange(book) {
var cats = book.categories;
delete book.categories;
return _(cats).map(function(category) {
return {
category: category,
book: book
};
});
}
function collect_into(h, b) {
if(!h[b.category.id])
h[b.category.id] = _(b.category).extend({ modules: [ ] });
h[b.category.id].modules.push(b.book);
return h;
}
var books = _.chain(json).
map(rearrange).
flatten().
reduce(collect_into, { }).
value();
Demo: http://jsfiddle.net/ambiguous/kSU7v/