How to Display Data Inside Nested ng-repeat Directives - angularjs

I have two objects, programme and section, each programme has some sections
I have two services, the first service shows all programmes, and the second service shows all sections for one programme, the problem is : I can't show each sections for one programme using the code of programme as a parameter, this is my code :
var app=angular.module("myApp",[]);
app.controller("myController",function($scope,$http){
$scope.programme=null;
$scope.section=null;
$http.get("/programmes").success(function(response) {
$scope.programme = response;
});
//getting sections for one programme using code
$scope.getSectionByCode = function(code)
{
$http.get("/listsections/"+code).success(function(data){
$scope.section = data;
});
};
});
HTML
<div class="padd" ng-app="myApp"
ng-controller="myController">
<ul>
<li ng-repeat="x in programme"
ng-init="getSectionByProgCode(x.code)" >
{{x.titre}}
<ul>
<li ng-repeat="s in section">{{s.title }}</li>
</ul>
</li>
</ul>
</div>
the result of this code is :
programme 1
Section title 4
Section title 5
programme 2
Section title 4
Section title 5
but im looking for this result :
programme 1
Section title 1
Section title 2
Section title 3
programme 2
Section title 4
Section title 5
Note : when I check my browser log, I find that the service of section get the results properly for each programme.

First of all I would advise you to read about how to structure your app properly, e.g. place http calls in services, store data in services, etc.
Now - your problem is that you do 2 asynchronous calls and place both results on the same variable: $scope.section, so obviously both ng-repeat divs will show the same data.
One way to fix that is you could make the $scope.section variable an array and store by index:
ng-init="getSectionByProgCode(x.code, $index)"
And in your controller:
$scope.section = [];
$scope.getSectionByCode = function(code, index)
{
$http.get("/listsections/"+code).success(function(data){
$scope.section[index] = data;
});
};
HTML function name and controller function name don't match, I'm guessing it's a typo of yours.
A different way to solve it (preferable by me and fellow commentators), is to store your section data under the programme itself:
<li ng-repeat="x in programme"
ng-init="getSectionByProgCode(x)" >
{{x.titre}}
<ul>
<li ng-repeat="s in x.section">{{s.title}}</li>
</ul>
</li>
And in your controller:
$scope.getSectionByProgCode = function(programme)
{
$http.get("/listsections/" + programme.code).success(function(data){
programme.section = data;
});
};

Related

ngRepeat doesn't refresh rendered value

I'm having an issue with ngRepeat :
I want to display a list of students in two different ways. In the first one they are filtered by group, and in the second they are not filtered.
The whole display being quite complex, I use a ngInclude with a template to display each student. I can switch between view by changing bClasseVue, each switch being followed by a $scope.$apply().
<div ng-if="currentCours.classesOfGroup !== undefined"
ng-show="bClassesVue">
<div ng-repeat="group in currentCours.classesOfGroup">
<br>
<h2>Classe : [[group.name]]</h2>
<div class="list-view">
<div class="twelve cell"
ng-repeat="eleve in group.eleves | orderBy:'lastName'"
ng-include="'liste_eleves.html'">
</div>
</div>
</div>
</div>
<div class="list-view" ng-show="!bClassesVue">
<div class="twelve cell"
ng-repeat="eleve in currentCours.eleves.all"
ng-include="'liste_eleves.html'">
</div>
</div>
My problem happens when my list of students change (currentCours here). Instead of refreshing the ngRepeat, both lists concatenate, but only in the unfiltered view.
I tried adding some $scope.$apply in strategic places (and I synchronize my list for example) but it doesn't help.
EDIT : the function used to refresh currentCours in the controller. It's called when a "cours" is selected inside a menu.
$scope.selectCours = function (cours) {
$scope.bClassesVue = false;
$scope.currentCours = cours;
$scope.currentCours.eleves.sync().then(() => {
if ($scope.currentCours.classe.type_groupe === 1) {
let _elevesByGroup = _.groupBy($scope.currentCours.eleves.all, function (oEleve) {
return oEleve.className;
});
$scope.currentCours.classesOfGroup = [];
for(let group in _elevesByGroup) {
$scope.currentCours.classesOfGroup.push({
name: group,
eleves: _elevesByGroup[group]
});
}
$scope.bClassesVue = true;
}
});
utils.safeApply($scope);
};
Well, I found a workaround, but I still don't know why it didn't work, so if someone could write an explanation, I would be very thankful.
My solution was simply to open and close the template each time I switch between views.

AngularJS - how to highlight/add class to a max value

Let say I have a very simple data that Im looping through with ng-repeat.
Now, how can I highlight (add a css class) to a highest value in the data.
Data:
$scope.marks = [
{point:'11'},
{point:'2'},
{point:'23'}
];
html:
<ul>
<li ng-repeat="value in marks" class={{here add class for value 23}}> //how to add class to li with max value, in this case it is 23
{{ value.point}}
</li>
</ul>
Many thanks for your help
PLUNKR
You can use calculate the max value in a watch:
$scope.$watchCollection('marks', function(items) {
$scope.maxPoint = -1; // assuming we won't have negative values
angular.forEach(items, function(item) {
if (parseInt(item.point) > $scope.maxPoint) {
$scope.maxPoint = item.point;
}
});
});
Then use ngClass directive in your markup:
<li ng-repeat="value in marks" ng-class="{max : value.point == maxPoint}">
{{ value.point}}
</li>
Like Petr pointed out this won't perform well. Calculating the max value on changes is a better way. Here's a working plunk : http://plnkr.co/edit/F8yjoWR0ZWVF4tcck1rH?p=preview

ng-repeat doesn't seem to be working

I am trying to pull all items from my array called 'collections'. When I input the call in my html I see only the complete code for the first item in the array on my page. I am trying to only pull in the 'edm.preview' which is the image of the item. I am using Angular Firebase and an api called Europeana. My app allows the user to search and pick which images they like and save them to that specific user.
here is the js:
$scope.users = fbutil.syncArray('users');
$scope.users.currentUser.collections = fbutil.syncArray('collections');
$scope.addSomething = function (something) {
var ref = fbutil.ref('users');
ref.child($rootScope.currentUser.uid).child('collections').push(something);
}
$scope.addSomething = function(item) {
if( newColItem ) {
// push a message to the end of the array
$scope.collections.$add(newColItem)
// display any errors
.catch(alert);
}
};
and the html:
<ul id="collections" ng-repeat="item in collections">
<li ng-repeat="item in collections">{{item.edmPreview}}</li>
</ul>
First, remove the outer ng-repeat. You want to only add the ng-repeat directive to the element which is being repeated, in this case <li>.
Second, from your AngularJS code, it looks like you want to loop over users.currentUser.collections and not just collections:
<ul id="collections">
<li ng-repeat="item in users.currentUser.collections">{{item.edmPreview}}</li>
</ul>
And third, you're defining the function $scope.addSomething twice in your JavaScript code. Right now, the second function definition (which, incidentally, should be changed to update $scope.users.currentUser.collections as well) will completely replace the first.

AngularJS: Filter Too Lax - incorrect display

I have an object of attachments returned from a SharePoint custom list with each named as ProjectID | RequestID | TaskID | Filename.
I am trying to filter according to my needs (only 1 ID will be present with the filename and the rest are usually 0 unless there is sharing going on) but when I filter where PID (Project ID) == 1, it displays where PID is 1 AND 17. How do I tighten up the filter to match EXACT?
Here is a plunkr.
Here is my HTML:
<body ng-controller="MainCtrl">
<h3>All Attachments:</h3>
<ul>
<li ng-repeat="att in attachments">{{att.Title}}</li>
</ul>
<hr />
<h3>Attachments for Project #1</h3>
<ul>
<li ng-repeat="pat in fstProj = (attachments | filter:{PID:'1'})">{{pat.Title}}</li>
</ul>
</body>
and here is the Javascript:
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope, catFactory) {
$scope.attachments = catFactory.getAttachments();
});
The filter filter uses string-matching. sinc eyou want to compare exact numbers, you can filter by a user-defined function:
<li ng-repeat="pat in attachments | filter:byPID">{{pat.Title}}</li>
$scope.byPID = function (item) {
return item.PID === 1;
}
See, also, this short demo.
UPDATE:
Of course, if you are willing to use a more recent version of Angular (not from the 1.0.* branch) (which you absolutely should), there is a third paramter to be passed to filter, in order to do strict matching (instead of substring):
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js" ...>
...
<li ng-repeat="pat in attachments | filter:{PID:1}:true">{{pat.Title}}</li>
In this case, nothing else needs to be changed.
See, also, this other demo.

Angular Scope and ng-repeat

My question in short
how do i manipulate the isolated scope created by ng-repeat inside of my controller?
To read the full problem i am having continue reading below:
I have a collection on timelines and each of them further has a collection of captures (essentially images).
HTML
<ul>
<li ng-repeat="timeline in timelines | filter:search | orderBy:'event_date':sort">
<ul ng-if="timeline.captures">
<li ng-repeat="captures in timeline.captures | limitTo:imageLimit">
<img ng-src="<% captures.capture_location %>">
</li>
</ul>
<a ng-click="(imageLimit = imageLimit + 1)">Load more</a>
</li>
</ul>
I want to limit the number of images being loaded on page load.. which i am able to do using limitTo
I want to load more images when the user clicks on 'Load more'. I am also able to do this by ng-click="(imageLimit = imageLimit + 1)
What i want to do:
I want to extract this out as a function
so that the following
Load more
becomes
Load more
To achieve this, i tried the following:
inside my controller
$scope.imageLimit = 1; // number of images to show initially on page load
$scope.loadMore = function() {
$scope.imageLimit += 1;
};
Now what is happening is that this this not create the imageLimit model inside the repeated scope (if you understand what i mean) but instead creates the imageLimit model at the controller scope. This is also the expected behaviour but i want to know how do i manipulate the scope within the repeated timeline (isolated scope for that timeline) inside my controller?
If I understand correctly, you want to be able to loadMore() for each timeline, independantly of the other ones. The loadMore() function should thus take the timeline as argument, and the limit should be a property of the timeline:
angular.forEach($scope.timelines, function(timeline) {
timeline.imageLimit = 10; // default value
});
$scope.loadMore(timeline) {
timeline.imageLimit++;
}
And in your template:
<li ng-repeat="captures in timeline.captures | limitTo:timeline.imageLimit">
...
<a ng-click="loadMore(timeline)">Load more</a>

Resources