So I have a list of news articles, and I need them to be filtered but server side not client side.
The way I'm filtering is by having a dropdown list that users click on to filter the articles like so:
<ul>
<li ng-repeat="category in categories">
<span href="" ng-click="getFilteredArticles(category.id)">{{category.title}}</span>
</li>
</ul>
and the articles are populated via an ng-repeat like so
<ul infinite-scroll="addPosts()" infinite-scroll-distance="0" infinite-scroll-disabled="stopScrolling" class="c-news">
<li ng-repeat="post in posts" class="c-news__item" ng-click="selectPost(post)">
<!-- Omitted some code for the sake of brevity -->
</li>
</ul>
The getFilteredArticles method looks like this
$scope.getFilteredArticles = function (categoryId) {
var posts = $scope.posts;
posts.forEach( (object) => {
if ( object[Categories.Id] === categoryId ) {
$scope.filteredArticles.push(object);
}
});
console.log($scope.filteredArticles);
}
A typical JSON object that I'm pulling through looks like this
{
"Title":"Test Title",
"Summary":"",
"PublishedDate":"2016-10-17T09:42:00",
"Author":{
"Id":"480586a5-2169-e611-9426-00155d502902",
"FirstName":"TestFirst",
"LastName":"TestSecond",
"Email":"test#test.com"
},
"Id":99,
"StatusName":"Published",
"Status":2,
"Categories":[
{
"Id":1,
"Name":"Category 1",
"ArticleCount":31,
"UnpublishedArticleCount":1
},
{
"Id":2,
"Name":"Category 2",
"ArticleCount":19,
"UnpublishedArticleCount":0
}
],
"AttachmentCount":0,
"FilesAwaitingCheckIn":0
}
What I'd like to happen is when the user clicks one of the filter choices, for the list to then filter to the clicked choice. I've got this far but then I'm getting a ReferenceError: Categories is not defined.
I based my getFilteredArticles() code from another Stack question which can be found here.
I'm aware of being able to just filter the ng-repeat however my manager does not want to go that route and would rather filter server side due to the amount of posts that we may have.
Any ideas?
EDIT
Here's what's inside $scope.posts array
As Categories is an array you need to loop through that array to get Id's
Your loop should be similar to this
$scope.getFilteredArticles = function (categoryId) {
for(var i=0;i<$scope.posts.length;i++)
{
for(var j=0;j<$scope.posts[i]["Categories"].length;j++)
{
if($scope.posts[i]["Categories"][j]["Id"] == categoryId)
$scope.filteredArticles.push($scope.posts[i])
}
}
console.log(JSON.stringify($scope.filteredArticles));
};
Related
I'm currently working on an AngularJS project and I got stuck in this specific requirement.
We have a service that has all the data, DataFactoryService. Then, I have a controller called DataFactoryController that is making the magic and then plot it in the view.
<div ng-repeat = "list in collection">
{{list.name}}
...
</div>
Now, we have a requirement that pass multiple data into one element. I thought an "ng-repeat" would do, but we need to have it inside an element attribute.
The scenarios are:
At one of the pages, we have multiple lists with multiple data.
Each data has a unique code or ID that should be passed when we do an execution or button click.
There are instances that we're passing multiple data.
Something like this (if we have 3 items in a list or lists, so we're passing the 3 item codes of the list):
<a href = "#" class = "btn btn-primary" data-factory = "code1;code2;code3;">
Submit
</a>
<a href = "#" class = "btn btn-default" data-factory = "code1;code2;code3;">
Cancel
</a>
In the example above, code1,code2,code3 came from the list data. I tried several approach like "ng-repeat", "angular.each", array, "ng-model" but I got no success.
From all I've tried, I knew that "ng-model" is the most possible way to resolve my problem but I didn't know where to start. the code below didn't work though.
<span ng-model = "dataFactorySet.code">{{list.code}}</span>
{{dataFactorySet.code}}
The data is coming from the service, then being called in the controller, and being plot on the HTML page.
// Controller
$scope.list = dataFactoryService.getAllServices();
The data on the list are being loaded upon initialization and hoping to have the data tags initialized as well together with the list data.
The unique code(s) is/are part of the $scope.list.
// Sample JSON structure
[
{ // list level
name: 'My Docs',
debug: false,
contents: [ // list contents level
{
code: 'AHDV3128',
text: 'Directory of documents',
...
},
{
code: 'AHDV3155',
text: 'Directory of pictures',
...
},
],
....
},
{ // list level
name: 'My Features',
debug: false,
contents: [ // list contents level
{
code: 'AHGE5161',
text: 'Directory of documents',
...
},
{
code: 'AHGE1727',
text: 'Directory of pictures',
...
},
],
....
}
]
How can I do this?
PLUNKER -> http://plnkr.co/edit/Hb6bNi7hHbcFa9RtoaMU?p=preview
The solution for this particular problem could be writing 2 functions which will return the baseId and code with respect to the list in loop.
I would suggest to do it like below
Submit
Cancel
//inside your controller write the methods -
$scope.getDataFactory = function(list){
var factory = list.map( (a) => a.code );
factory = factory.join(";");
return factory;
}
$scope.getDataBase= function(list){
var base= list.map( (a) => a.baseId);
base= base.join(";");
return base;
}
Let me know if you see any issue in doing this. This will definitely solve your problem.
You don't really have to pass multiple data from UI if you are using Angular.
Two-way data binding is like blessing which is provided by Angular.
check your updated plunker here [http://plnkr.co/edit/mTzAIiMmiVzQfSkHGgoU?p=preview]1
What I have done here :
I assumed that there must be some unique id (I added Id in the list) in the list.
Pass that Id on click (ng-click) of Submit button.
You already have list in your controller and got the Id which item has been clicked, so you can easily fetch all the data of that Id from the list.
Hope this will help you... cheers.
So basing from Ashvin777's post. I came up with this solution in the Controller.
$scope.getFactoryData = function(list) {
var listData = list.contents;
listData = listData.map(function(i,j) {
return i.code;
});
return listData.join(';');
}
I have some json data that I output with ng-repeat and I have added some form controls to filter data output from another json.
Simplified sample
First JSON data:
var technologies = [
{"id":"5", "slug":"mysql", "label":"MySQL", "category":"database"},
{"id":"4", "slug":"html", "label":"HTML", "category":"markup"}
]
and the output:
<ul>
<li ng-repeat="tech in technologies">
<span>{{tech.label}}</span>
<span><label>required expertise
<select>
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select></label>
</span>
</li>
</ul>
(actually, the technologies are at this point already filtered from another choice that the user makes on a different page, but I assume that makes no difference)
The 2nd json contains the expertise property that I want to use as a filter
var people = [
{
'label': 'MySQL',
'slug': 'mysql',
'talents':[
{
'name': 'Stanislav Lem',
'expertise': 5,
'desire': 0,
'last_used': '2009-01'
},
{
'name': 'Ijon Tichy',
'expertise': 1,
'desire': 5,
'last_used': '2011-06'
}
]
}, ...
]
...and that is for now being plain used as
<ul>
<li ng-repeat="item in people">
{{item.label}}<br>
<ul>
<li ng-repeat="person in item.talents">{{person.name}} · Expertise: {{person.expertise}}</li>
</ul>
</li>
</ul>
What I need is a filter that uses the options from the first output to filter the second output. For example when MySQL required expertise is set to '2', only Stanislav Lem will be displayed. Actually I will also need a 'no results found' message if the noone matches the expertise, but I guess I can figure that part out myself.
Plunker sample is here: http://plnkr.co/edit/B0aQp9aCJ2g4OM6fZ3qe?p=preview
Working Plunker: http://plnkr.co/edit/jcb6SfKVPkwt7FFCZFHX?p=preview
The first thing you'll need to do is add an ng-model attribute to the select. I've added this as a property of the technology so you can easily find it.
<select ng-model="tech.required">
Next, I added the following two functions to your controller to aid in filtering the second list:
$scope.getFilterForSlug = function(slug) {
var technology = getTechBySlug(slug);
return function(person) {
return person.expertise >= (technology.required || 0);
}
}
function getTechBySlug(slug) {
return _.find($scope.technologies, {
slug: slug
});
}
I'm using lodash in getTechBySlug, as the name states, to get the correct technology object based on the slug. Using that, getFilterForSlug returns a function which compares the person's expertise level with the desired level from the select. If an option wasn't selected, then the desired level is set to 0.
Lastly, in the HTML, I added the following:
<ul>
<li ng-repeat="person in filteredPeople = (item.talents | filter: getFilterForSlug(item.slug))">{{person.name}} · Expertise: {{person.expertise}}</li>
</ul>
<span ng-if='!filteredPeople.length'>No Results!</span>
This code gets a filter based on the current slug, and then filters the people using that function. It stores the results in a variable filteredPeople, which is used in the span to display a message when there are no results.
Currently I have a json file that populates information to my chart. An example of this chart can be found here: http://marmelab.com/ArchitectureTree/. I also have a panel to the right which is meant to display the information of one of the charts node's when clicked. However I don't know how to do this.
Say if I had in my json array:
{
"name": "Blogs",
"url": "blogs.my-media-website.com/*",
"dependsOn": ["Wordpress MU"],
"technos": ["PHP", "Wordpress"],
"host": { "Amazon": ["?"] }
},
I would click on 'Blogs' in the chart and the 'url', 'dependsOn' etc would then be displayed in the panel. I'm sure this function uses AngularJS to do this. Can someone point me in the right direction on how to do this?
On click of Blogs call below function :
In your controller, try adding this function.
$scope.onClickOfBlogs = function(){
$http.get('Your_JSON_file_Path_here').success(function(response){
$scope.jsonObject= response;
}).error(function() {
console.log('error occured while getting JSON file');
});
};
in your Panel HTML : -
<div id ="info">
<span>URL : {{jsonObject.url}} </span>
<span>Name : {{jsonObject.name}} </span>
<ul>
Depends on :
<li ng-repeat = "depends in jsonObject.dependsOn"> <!-- to iterate on array you need ng-repeat -->
{{depends}}
</li>
</ul>
</div>
you will get the data from json file into your html panel.Hope this helps.
**
** : http://plnkr.co/edit/dbJ0bhHhvASffJU9liY6?p=preview
I'm trying to use ng-repeat to repeat the rest of data if the attribute is the same. This may not sound clear so here is an example.
JSON sample, which is an array of books
[{"Genre":Sci-fic,"Name":"Bookname1"},
{"Genre":Sci-fic,"Name":"Bookname2"},
{"Genre":Non sci-fic,"Name":"Bookname3"},
{"Genre":Non sci-fic,"Name":"Bookname4"}]
So if the book belongs in the same genre, then we would have genre type as the heading of the accordion and list the rest of the attributes for the ones whose genre is that without repeating the heading. The problem I've been encountered is that since json array of objects comes from a data model, and I can't really filter the type without repeating the genre type.
here is what I'm having where I'm trying to have heading as the Book.Genre and inside that accordion-group will have the list of all the books whose genre is the heading.
http://plnkr.co/edit/PwIrOAIT3RRaE2ijzGZb?p=preview
I haven't been able to tackle this problem yet either... The best solution I can think of is to simply strip out the duplicates before adding that model to your scope...
Using your jsFiddle example...
http://jsfiddle.net/mcpDESIGNS/Uz5tM/1/
var books = [
{"Genre":"Sci-fic", "Name":"Bookname1"},
{"Genre":"Sci-fic", "Name":"Bookname2"},
{"Genre":"Non sci-fic", "Name":"Bookname3"},
{"Genre":"Non sci-fic", "Name":"Bookname4"}
];
var unique = [],
blocked = [];
unique.push(books[0]);
blocked.push(books[0].Genre);
for (var i = 1; i < books.length; i++) {
if (blocked.indexOf(books[i].Genre) <= -1) {
unique.push(books[i]);
blocked.push(books[i].Genre);
}
}
$scope.Books = books;
// [{Genre: "Sci-fic", Name: "Bookname1"},
// { Genre: "Non sci-fic", Name: "Bookname3" }];
it happens because you 've got duplicate value in generes use track by
<div ng-repeat="genere in generes track by $index"></div>
or if you want group you model by Genre you can use underscore.js please see that fiddle
View :
<div ng-app="app">
<div ng-controller="ParentCtrl">
<ul class="about-tab-titles" close-others="oneAtATime">
<li ng-repeat="(key ,book) in model">
{{key}}
<ul>
<li ng-repeat="b in book ">{{b.Name}}</li>
</ul>
</li>
</ul>
</div>
</div>
JS:
angular.module('app', [])
function ParentCtrl($scope) {
var books = [{
"Genre": "Sci-fic",
"Name": "Bookname1"
}, {
"Genre": "Sci-fic",
"Name": "Bookname2"
}, {
"Genre": "Non sci-fic",
"Name": "Bookname3"
}, {
"Genre": "Non sci-fic",
"Name": "Bookname4"
}];
$scope.model = _.groupBy(books, 'Genre');
}
So I found a solution which works for me. I followed this AngularJs filter on nested ng-repeat. I made a couple changes for my data model, but it's the same idea.
I'm looking through the Angular docs and I can't better documented stuff.
My problem is as follows:
I have a filter:
.filter('filteringService', function () {
return function (photos, categsList) {
if (photos !== undefined) {
var filteredPosts = [];
console.log('Categories or Selected Categories:');
console.log(categsList);
//DEVEL Only, assign a SEARCH value, can't pass the data from the list now.
categsList = 'people';
if (categsList === undefined) {
for(i=0; i < photos.length; i++) {
filteredPosts.push(photos[i]);
}
} else {
console.log('Trying to push a category slug to search for.');
//TASK: Convert in multiple possible selections.
filteredPosts = [];
}
console.log('Filter returns ' + filteredPosts.length + ' posts');
return filteredPosts;
}
};
});
And I have the template
<div class="photos">
<div class="filters">
<ul>
<li><a>ALL</a></li>
<li ng-repeat="category in categsList">
<a ng-checked="category[0]" ng-model="category[0]">{{ category[1] }}</a>
</li>
</ul>
</div>
<ul class="photos-list">
<li ng-repeat="photo in photos|filteringService:category">
<h1>{{ photo.title }} click LINK</h1>
<ul class="categories">
<li ng-repeat="category in photo.categories">
{{ category.title }}
</li>
</ul>
</li>
</ul>
</div>
There's a huge object with posts called photos and then there's a category list called categsList.
The photos object has the items from the categs list in it. I WANT to be able to filter with the CLICKED element through that list, and maybe multiple filter, but first to pass on the actual filter value to the filter service, I can't seem to do that.
How should I do that?
Apparently I managed to pass the filter value, in a dirty way (I GUESS), like this
<a ng-bind="category.slug" ng-click="returnFilter(category.slug);" ng-model="category.slug">{{ category.title }}</a>
it goes here
$scope.returnFilter = function(theSlug) {
$scope.filterBy = theSlug;
};
and it comes out here
<li ng-repeat="photo in photos|filteringService:filterBy">
It's working, but Is this correct?
EDIT: Also with this way in mind, I could pass an array as theSlug so I can do multiple filtering, and when clicking two times on the same item it would get it out of the array. hmmm
EDIT 2:
Let's say the resulting array is under 15 items, could do I run some action in the controller knowing this?
Actually the other way around, could I query from the controller the resulting array that the filter outputs?
I can't $watch the first array, I guess the filter creates a new array and puts those results in page. How could I watch the resulting array for changes and do stuff in the controller?