Using ng-repeat to fill remaining elements for "x - available elements" - javascript

I am trying to have 4 boxes always show up. The boxes are filled by an image,but if there is no image available I want the boxes to be gray. The images variable holds an unknown amount of images, it could be 2 it could be 30.
<div class="container">
<div class="picture" ng-repeat="image in images | limitTo: 4"></div>
// PSEUDO CODE BELOW
<div class="empty" ng-repeat="i in 4 - images.length"></div>
// PSEUDO CODE ABOVE
</div>
"4 - images.length" is pseudo code, This is what I want to achieve, thinking if I only have 3 images available the result will be 1 gray box. But this syntax obviously does not work since ng-repeat require a collection.
Which made me try to provide it said collection through a function:
$scope.getNumber = function(num) {
return new Array(num);
}
and use in the following way:
<div class="empty" ng-repeat="n in getNumber(4 - cookbook.images.length)"></div>
But with no success.

<div class="picture" ng-repeat="image in images | limitTo: 4">hi</div>
<div class="empty" ng-repeat="n in [] | range:images.length:4">hey</div>
I've created a custom filter for it:
app.filter('range', function() {
return function(input, min, max) {
min = parseInt(min);
max = parseInt(max);
for (var i=min; i<max; i++) {
input.push(i);
}
return input;
};
});
DEMO

Possible repeat question
By using this updated syntax you can iterate with a specific index defined
HTML
<div ng-app="myapp">
<div ng-controller="ctrlParent">
<ul>
<li ng-repeat="i in getNumber(4) track by $index"><span>{{$index+1}}</span></li>
</ul>
</div>
Controllers
var app = angular.module('myapp',[]);
app.controller('ctrlParent',function($scope){
$scope.getNumber = function(num) {
return new Array(num);
}
});
Way to ng-repeat defined number of times instead of repeating over array?

I would probably handle this in whatever populates $scope.images. For example, something like:
$http.get('api/some/images?skip=10&take=4')
.success(function(response){
$scope.images = response;
for(var i = $scope.images.length; i<4; i++){
$scope.images.push('empty');
}
});
Then my template would just handle that:
<div class="container>
<div ng-repeat="image in images" ng-class="{empty:image=='empty',picture:image!='empty'}"></div>
</div>

wouldnt be easier to make operation modulo in controller?
function equal_number_of_images(){
for(var i = 0, add = 4 - $scope.some_files%4; i < add; i++ ){
$scope.some_files.push('');
}}

Related

AngularJS get first item in a repeat that has a certain value

<div ng-repeat="widget in widgets"
ng-class="">
<div>{{widget.row}}</div>
</div>
I'm trying to apply a class inside the repeat based on a particular value in the repeat, for example if widget.row = 0 and it is the first widget with that value displayed then give it a class and all the other widgets that have row as 0 do not get the class. This will need to be the case if it equals 1 or 2 and so on so I can't just use $first as there will be multiple row values and multiple widgets for example it may output something like:
0 0 0 0 1 1 2 2 2 2
So the easiest way for me to achieve this was using the Adjacent sibling selector rather than do it with angular as each item is not really aware of the others:
<div ng-repeat="widget in widgets"
class="widget-row-{{widget.row}}">
<div>{{widget}}</div>
</div>
and then use CSS for:
.widget-row-0:first-child {}
.widget-row-0 + .widget-row-1 {}
.widget-row-1 + .widget-row-2 {}
.widget-row-2 + .widget-row-3 {}
Best practise is to prepare your data in a init function in your controller. It's nice and KISS! It's the best way to prepare your data in control function instead of misapply the E2E binding of AngularJS. It solve your problem so no class is written when there is no need for (as you asked for). Its proceeded once instead of calling a function again, again and again by E2E binding like ng-class="shouldIAddAClass()".
View
<div ng-repeat="widget in widgets"
ng-class="{ 'first' : widget.first }">
<div>{{widget.row}}</div>
</div>
Controller
$scope.widgets = [{
row: 0
}, {
row: 2
},{
row: 0
},{
row: 1
},{
row: 1
},{
row: 2
},{
row: 0
}];
//self calling init function
(function init () {
var widgetRowFound = {};
angular.forEach($scope.widgets, function (widget, key) {
if (angular.isDefined(widgetRowFound[widget.row])) {
$scope.widgets[key].first = false;
} else {
$scope.widgets[key].first = true;
widgetRowFound[widget.row] = true;
}
});
})();
Not the cleanest one but will work
<div ng-repeat="widget in widgets">
<div ng-class="{'myClass': applyClass(0, widget.row)}"></div>
</div>
----------
$scope.widgetsRows = {};
function applyClass(number, row){
if(!$scope.widgetsRows[row]){
$scope.widgetsRows[row] = true
}
return row == number && $scope.widgetsRows[row];
}
You can add the class you want to use to the widget objects in the controller first:
var tempRow = "";
for(var i = 0;i < $scope.widgets.length;i++) {
if($scope.widgets[i].row != tempRow) {
$scope.widgets[i].class = "myClass";
tempRow = $scope.widgets[i].row;
}
}
Then you can use that class:
<div id="widgets" ng-repeat="widget in widgets"
class="{{widget.class}}">
<div>{{widget.row}}</div>
</div>
Hope this helps
You can create a method that will be called from ng-class to achieve your goal. The method should return the class to be used.
$scope.firstHitFound = false;
$scope.isFirstZeroValue = function(value){
if($scope.firstHitFound == false && value == 0){
$scope.firstHitFound = true;
return class1;
}else{
return class2;
}
}
The HTML / Angular shoudl look as:
<div ng-class="isFirstZeroValue(widget.row)">
If you want to style it, add the class to all the widget that match your criteria, and use css to perform it only on the first of them.
Html:
<div id="widgets" ng-repeat="widget in widgets"
ng-class="{'widget-first': widget.row == 0}">
<div>{{widget.row}}</div>
</div>
Css:
#widgets.widget-first:first-of-type {
background: #ff0000;
}
You can use ng-class in addition of your ng-repeat:
Example
<div ng-repeat="widget in widgets" ng-class="{'test': widget.value === 0}">
<div>{{widget.row}}</div>
</div>
You need to call a method that will check if the row result is not same with previous value. If it not same , it will return true value and will be assigned ng-class, and if not return false. Filter this out using ng-if.
Html
<div ng-repeat="widget in widgets"
ng-class="">
<div ng-if="calculate(widget.row)">
<div ng-class="test">{{widget.row}}</div>
</div>
<div ng-if="!calculate(widget.row)">
<div>{{widget.row}}</div>
</div>
</div>
Controller
var arr = [];
$scope.calculate = function (row) {
arr.push(row);
var breakLoop = false;
angular.forEach(arr, function (oldVal, newVal) {
breakLoop = false;
if (oldVal != newVal) {
breakLoop = true;
}
)};
return breakLoop;
}

Stop loop using ng-if condition in angularJS

I have this code. $scope.counter = '1';
<div ng-repeat="cat in catadata">
<div ng-if="cat.TopCategoryID = 2" >
<div ng-if="counter = 1" >
<div class="feedsDisplay" ng-init="counter = counter + 1">
Creater Img:{{cat.CreatedBy_Profile.Picture.URI}}"
</div>
</div>
</div>
</div>
I want to stop loop when counter = 2, I want to display just 1 result.
ng-init="counter = counter + 1
{{counter}}
displaying 11111 it should display 12345
you are comparing with = instead of ==
Try like this
ng-if="counter == 1"
value of counter is string . add + sign before counter
try like this
ng-init="counter = +counter + 1"
You cant stop ng-repeat conditionally.
To print only one result you can use
<div ng-repeat="cat in catadata | limitTo : 1">
OR
<div>{{catadata[0]}}</div>
Rather than misusing ng-repeat, ng-init and ng-if like this, just try to extract the first matching cat before you render it, e.g. in a controller, directive or service:
for (var i = 0, maxI = catadata.length; i < maxI; ++i) {
if (cat.TopCategoryID === 2) {
$scope.firstMatchingCat = cat;
break;
}
}
And then in the template:
<div class="feedsDisplay">
Creater Img:{{firstMatchingCat.CreatedBy_Profile.Picture.URI}}"
</div>

How can I dynamically set css color in angular?

I am writing a list values from a json file to the DOM. I want to set each text object to be a different color. I found some resources for doing this, but I haven't been able to get anything to work.
my js:
.controller("Catalog", function($scope, $interval) {
$scope.search = "";
$scope.books = books;
$scope.reverse = false;
$scope.list = false;
for(var i=0; i<10;i++){
$scope.books[i] = {
'color': ('#'+Math.floor(Math.random()*16777215).toString(16))
};
}
});
and html:
<div class="container result">
<div ng-class="col s12 m6" class="books" ng-repeat="book in books | filter:search | orderBy:'book.doc.name':reverse" > <a href="https://address/{{book.doc.name}}" target="_blank">
<img ng-class="list ? 'col s1'" alt="Click to read {{book.doc.name}}" title="Click to read {{book.doc.name}}" class="img-thumbnail" /></a>
<h2 ng-class="list ? 'col s12 m6' ">
{{book.doc.name}}
{{book.doc.languages}}
</h2>
<p ng-class="list ? 'col-md-10' : 'col-md-12'">
{{book.doc.subjects}}{{book.doc.tags}}</p>
</div>
Your books variable that you are assigning to $scope.books is not set.
You can init like this:
$scope.books = [];
and than use it in ng-repeat like this:
{{book.color}} because you are only setting the color on the for loop.
If you have the $scope.books set than you can set the colors for all the books like this:
for (var i = 0; i < books.length; i++) {
books[i].color = ('#' + Math.floor(Math.random() * 16777215).toString(16));
}
Here is a JSFiddle Demo.

JavaScript reselect class personally in ListView

I am creating ListView using my template:
HTML:
<div id="ItemTemplate" data-win-control="WinJS.Binding.Template">
<div class="ItemTemplate">
<div class="back"></div>
<div data-win-bind="innerText:Info.shortName" class="shortName"></div>
<div data-win-bind="innerText:value Converters.BeginValue" class="value"></div>
<div data-win-bind="innerText:value Converters.EndValue" class="valueEnd"></div>
<div data-win-bind="innerText:Info.longName"></div>
<img data-win-bind="src:Info.flag" class="flag" />
<div data-win-bind="innerText:change Converters.BeginChange" class="change"></div>
<div data-win-bind="innerText:change Converters.EndValue" class="changeEnd"></div>
<div data-win-bind="innerText:changePercent Converters.BeginChangePercent" class="changePercent"></div>
<div data-win-bind="innerText:changePercent Converters.EndValue" class="changePercentEnd"></div>
</div>
</div>
The issue is when I meet the very long name I need to adjust font-size.
So I do (for each element in list):
JavaScript:
template = document.getElementById('ItemTemplate');
// Adjust font - size
var name = item.data.Info.longName;
// Split by words
var parts = name.split(' ');
// Count words
var count = parts.filter(function(value) {
return value !== undefined;
}).length;
var longNameDiv = $(template).children("div").children("div").eq(4);
if (count > 2) {
// Display very long names correctly
$(longNameDiv).removeClass();
$(longNameDiv).addClass("veryLongName");
}
var rootDiv = document.createElement('div');
template.winControl.render(item.data, rootDiv);
return rootDiv;
CSS:
.veryLongName {
font-size: 10pt;
}
But it doesn't effect selectivly. Moreover seems like it is check conditions for the first time and then just apply the same setting for remaining items. But it needs to change font-size to smaller only in case if the name is too long. So what am I doing wrong? Thanks.
Try by using following code instead, but u must include jquery for it.
jsfiddle : http://jsfiddle.net/vH6G8/
You can do this using jquery's filter
$(".ItemTemplate > div").filter(function(){
return ($(this).text().length > 5);
}).addClass('more_than5');
$(".ItemTemplate > div").filter(function(){
return ($(this).text().length > 10);
}).removeClass('more_than5').addClass('more_than10');
DEMO

Angular ng-repeat rating images

I have two pngs, one with an empty star one with a full star. Depending on the ratings from 1-5 I want to add or remove more star images. Here is my code that doesn't work:
<div>
<img ng-repeat="i in getNumberOfFullStars(company.AverageReview)" ng-src="{{starPhoto}}">
<img ng-repeat="i in getNumberOfEmptyStars(company.AverageReview)" ng-src="{{emptyPhoto}}">
</div>
JS
$scope.getNumberOfFullStars = function(num){
return new Array(num);
};
$scope.getNumberOfEmptyStars = function(num){
var emptyCount = 5-num;
return new Array(emptyCount);
};
Any thoughts? Thank you for the help
Instead of two seperate images, you can just use one and then check against $index. Modify to meet your needs
<div>
<img data-ng-repeat="i in ratings" data-ng-src="{{ ($index < company.AverageReview) && starPhoto || emptyPhoto }}" />
</div>
$scope.ratings=[];
$scope.ratings.length = 5;

Categories