How do you loop through different rows (bootstrap rows) in Angular? - javascript

I am asking myself: How would you loop through different rows using *ngFor?
Lets say an array is given
let arr = [1,2,3,4] // arr can have a arbitrary number of elements
Now I want to loop trough that array and display the values. I want two elements to be in a row (using bootstrap 4.0). My approach:
<div class="row">
<div class="col-6" *ngFor="let el of arr"> {{el}} </div>
</div>
Now I would have this html-code
<div class="row">
<div class="col-6"> 1 </div>
<div class="col-6"> 2 </div>
<div class="col-6"> 3 </div>
<div class="col-6"> 4 </div>
</div>
but I want it this way:
<div class="row">
<div class="col-6"> 1 </div>
<div class="col-6"> 2 </div>
</div>
<div class="row">
<div class="col-6"> 3 </div>
<div class="col-6"> 4 </div>
</div>
because this would be correct regarding the elements in a row.
How can I achieve this?

You can achieve it without second loop provided col-6 is fixed.
<div>
<ng-container *ngFor="let el of arr; index as i">
<div class="row" *ngIf="i%2 === 0">
<div class="col-6" > {{arr[i]}} </div>
<div class="col-6" > {{arr[i+1]}} </div>
</div>
</ng-container>
</div>
This will do as you are expecting.

Option 1
The quickest option would be to use the slice pipe. But it might not be suitable for large data sets.
<div class="row">
<div class="col-6" *ngFor="let el of arr | slice:0:2"> {{el}} </div>
</div>
<div class="row">
<div class="col-6" *ngFor="let el of arr | slice:2:4"> {{el}} </div>
</div>
Option 2
Split the array into chunks of arrays and loop through them.
Controller
export class AppComponent {
arr = [1,2,3,4];
finalArr = []; // <-- [[1,2], [3,4]]
constructor(private http: HttpClient){
for (let i = 0, j = this.arr.length; i < j; i += 2) {
this.finalArr.push(this.arr.slice(i, i + 2));
}
}
}
Template
<div class="row" *ngFor="let arr of finalArr">
<div class="col-6" *ngFor="let el of arr"> {{el}} </div>
</div>

From the pattern it looks like a outer and a inner loop will solve the problem.
outer loop will control the "row" - div
inner loop will control the "col-6" - div
Kindly give it a try.

Related

Get a specific text value of an element when there is multiple elements with the same class names and attribute names

<div class="my-attributes">
<div class="row">
<div class="SameClass"><strong>Condition</strong></div>
<div class="SameClass-value" itemprop="condition">New</div>
</div>
<div class="row">
<div class="SameClass"><strong>Color</strong></div>
<div class="SameClass-value" itemprop="color">Black</div>
</div>
<div class="row">
<div class="SameClass"><strong>Availability</strong></div>
<div class="SameClass-value" itemprop="avail">In Stock</div>
</div>
<div class="row">
<div class="SameClass"><strong>Quantity</strong></div>
**<div class="SameClass-value" itemprop="qty"> 5 </div>**
</div>
<div class="row">
<div class="SameClass"><strong>Price</strong></div>
<div class="SameClass-value" itemprop="price">250</div>
</div>
I have multiple divs that has the same class and 'itemprop' attribute and I need to be able to create a function that will return the Quantity (which in the example above is '5')
the catch here is that the structure you see above changes in other pages. In the example above, the Condition and the Color shows but on other pages those do not appear which means the structure changes except the class name and the attribute value of the 'itemprop'. See below:
<div class="my-attributes">
<div class="row">
<div class="SameClass"><strong>Availability</strong></div>
<div class="SameClass-value" itemprop="avail">In Stock</div>
</div>
<div class="row">
<div class="SameClass"><strong>Quantity</strong></div>
**<div class="SameClass-value" itemprop="qty"> 5 </div>**
</div>
<div class="row">
<div class="SameClass"><strong>Price</strong></div>
<div class="SameClass-value" itemprop="price">250</div>
</div>
How do I get the Quantity into a JavaScript function and return the value of 5? Without having to edit the code of the site itself?
I have been playing with this but I am sure this is completely and utterly incorrect.
function () {
var x = document.getElementsByClassName("SameClass").getAttribute("itemprop");
if (x = 'price')
{
var a = document.getElementsByClassName("SameClass").getAttribute("itemprop").innerText;
return a;
}
}
The following might work for you:
function getQty() {
var arr=document.getElementsByClassName("SameClass-value");
for(var i=0;i<arr.length;i++)
if (arr[i].getAttribute("itemprop") == 'qty') return arr[i].innerHTML;
}
Or, even shorter, you can do
function getQty() {
return document.getElementsByClassName("SameClass-value")
.find(d=>d.getAttribute("itemprop") == 'qty').innerHTML;
}
Sorry for the repeated edits. It's been a long day for me today and I was trying to quickly put something together on my little smartphone.
If the classes don't change, you can select all the relevant elements then get the value from the one you need.
function getQty() {
var qty;
document.querySelectorAll("div.SameClass-value").forEach(function(element) {
if (element.getAttribute("itemprop") === "qty") {
qty = element.innerText;
}
});
return qty;
}
console.log(getQty());
<div class="my-attributes">
<div class="row">
<div class="SameClass"><strong>Condition</strong></div>
<div class="SameClass-value" itemprop="condition">New</div>
</div>
<div class="row">
<div class="SameClass"><strong>Color</strong></div>
<div class="SameClass-value" itemprop="color">Black</div>
</div>
<div class="row">
<div class="SameClass"><strong>Availability</strong></div>
<div class="SameClass-value" itemprop="avail">In Stock</div>
</div>
<div class="row">
<div class="SameClass"><strong>Quantity</strong></div>
<div class="SameClass-value" itemprop="qty"> 5 </div>
</div>
<div class="row">
<div class="SameClass"><strong>Price</strong></div>
<div class="SameClass-value" itemprop="price">250</div>
</div>
</div>

ngFor and ngIf on bootstrap columns

I want to hide and show bootstrap columns inside a row based on search value.
Here is a simple template:
<div class="container py-5">
<p class="text-center"><input type="text" placeholder="search" [(ngModel)]="searchVal"></p>
<div class="row">
<div *ngFor="let val of values" class="col-4 my-2">
<ng-container *ngIf="val.toLowerCase().indexOf(searchVal.toLowerCase())!=-1">
{{val}}
</ng-container>
</div>
</div>
</div>
And the component:
export class AppComponent {
searchVal : string = "";
values = ['Manager', 'Model', 'Teacher', 'Student'];
}
The ideal solution is to use ngFor and ngIf on column:
<div class="row">
<div *ngFor="let val of values" class="col-4 my-2" *ngIf="val.toLowerCase().indexOf(searchVal.toLowerCase())!=-1">
{{val}}
</div>
</div>
But we all know that its not possible to use both on same element.
In the code I provide here, I used ng-container to solve this issue, however, since the ng-container is not inside the column, if a value does not match, the column remains displayed and its content is hidden which looks ugly. Try to write the letter "t" in the search field to see what I mean. The solution that I am looking for is to remove the entire column if its value does not match.
Here is a running code: https://angular-rgc8f7.stackblitz.io
Swap your div and ng-container:
ng-container has the for loop and div has the if condition
<div class="container py-5">
<p class="text-center"><input type="text" placeholder="search" [(ngModel)]="searchVal"></p>
<div class="row">
<ng-container *ngFor="let val of values">
<div *ngIf="val.toLowerCase().indexOf(searchVal.toLowerCase())!=-1" class="col-4 my-2">
{{val}}
</div>
</ng-container>
</div>
</div>
Try the following code.
<ng-container *ngFor="let val of values">
<div class="col-4 my-2"
*ngIf="val.toLowerCase().indexOf(searchVal.toLowerCase())!=-1">
{{val }}
</div>
</ng-container>
Couldn't you just add a function in your component to dynamically filter the values before iteration. e.g
<div class="container py-5">
<p class="text-center"><input type="text" placeholder="search" [(ngModel)]="searchVal"></p>
<div class="row">
<div *ngFor="let val of filterValues(values, searchVal)" class="col-4 my-2">
{{val}}
</div>
</div>
</div>
In your component;
filterValues(values: string[], searchVal: string) {
return values.filter((val: string) => {
return val.toLowerCase().indexOf(searchVal.toLowerCase())!=-1;
});
}
Change detection on your search value will cause a refilter, filtering on the current search value, and value list, and updating your table.
This way, you don't need the ng-container at all, and, if you ever need to filter based on other criteria too, you can do it all in one place :)
StackBlitz Example: https://stackblitz.com/edit/angular-miow53

Wrap every nth piece of content with 2 divs in javascript

I am trying to wrap two divs around every 3 pieces of content generated from an API call. It should look like this:
<div class="carousel-inner">
<div class="item carousel-item">
<div class="row">
<div class="col-sm-4">
<div class="thumb-wrapper">
Content Here (1)
</div>
</div>
<div class="col-sm-4">
<div class="thumb-wrapper">
Content Here (2)
</div>
</div>
<div class="col-sm-4">
<div class="thumb-wrapper">
Content Here (3)
</div>
</div>
</div>
</div>
<div class="item carousel-item">
<div class="row">
<div class="col-sm-4">
<div class="thumb-wrapper">
Content Here (4)
</div>
</div>
</div>
</div>
</div>
I have worked on this all day and I can't seem to get it right. Also, I may not be using the most efficient method. I am hoping that someone can tell me what I am doing wrong and how to fix it... also, I am open to any advice as far as streamlining my code. So, with this search 4 pieces of content are returned, and every set of 3 should be wrapped in two divs (item carousel-item and row), but in my attempt below, it seems to be wrapping correctly, but it surrounds 4 instead of three and then brings back a duplicate piece of content for the 4th, I also have an extra div at the end... yikes :)
What I have it doing so far:
<div class="carousel-inner">
<div class="item carousel-item">
<div class="row">
<div class="col-sm-4">
<div class="thumb-wrapper">
Content Here (1)
</div>
</div>
<div class="col-sm-4">
<div class="thumb-wrapper">
<div class="img-box">
Content Here (2)
</div>
</div>
</div>
<div class="col-sm-4">
<div class="thumb-wrapper">
<div class="img-box">
Content Here (3)
</div>
</div>
</div>
***this div should not be here, should have stopped at 3***
<div class="col-sm-4">
<div class="thumb-wrapper">
<div class="img-box">
Content Here (4)
</div>
</div>
</div>
</div>
</div>
<div class="item carousel-item">
<div class="row">
<div class="col-sm-4">
<div class="thumb-wrapper">
<div class="img-box">
Content Here (4)
</div>
</div>
</div>
</div>
</div>
</div>
***extra div shows up at end***
</div>
Here is the code I used:
jQuery.each(ws_ftr, function(index, ftr) {
if(index % 3 === 0){
jQuery('.carousel-inner').append('<div class="item carousel-item active"><div class="row">');
}
jQuery('.row').append('<div class="col-sm-4"><div class="thumb-wrapper"><div class="img-box">Content Here</div></div></div>');
if(index % 3 === 0){
jQuery('.row').append('</div></div>');
}
your code should be like
function createColumnsList(arr) {
var html = '<div class="row">';
//or another code to start your row like $.append or smth
$.each(arr, function(index, item) {
if (index % 3 == 0 && index != 0) {
html += '</div><div class="row">';
//end and start your row
}
html += '<div class="column">' + item + '</div>';
// output your content from array
});
html += "</div>";
//end row
return html;
}
Change it to fit your needs. Hope it will help you :)

Angularjs ng-repeat more data

js app
Now In html I user ng-repeat
<div class="row">
<div ng-repeat="item in Photos" class="col col-33"style="border:none">
<img ng-src="{{item.image}}" imageonload style="width:100px;height:120px;margin-left:auto;margin-right:auto;display:block">
</div>
</div>
My problem is that I have 5 images in list but only 3 of them is on dipslay. Another 2 is not shown on display. How can I solve problem ?
Thanks in advance
May be your images have same url value in the Photos array so you need to use ng-repeat with track by $index like this
<div class="row">
<div ng-repeat="item in Photos track by $index" class="col col-33"style="border:none">
<img ng-src="{{item.image}}" imageonload style="width:100px;height:120px;margin-left:auto;margin-right:auto;display:block">
</div>
</div>
$scope.imagearray = {
'images' : []
};
var i,j,temparray,chunk = 3;
for (i=0,j=$scope. Photos.length; i<j; i+=chunk) {
temparray = $scope. Photos.slice(i,i+chunk);
$scope.imagearray.images.push(temparray);
}
<div ng-repeat="img in imagearray.images">
<span ng-repeat="item in img" ></span>
</div>
Try col-xs-2 instead of col-33:
<div class="row">
<div ng-repeat="item in Photos" class="col col-xs-2">
<img ng-src="{{item.image}}">
</div>
</div>

Trouble with ng-repeat angular js

I have an angular ng-repeat like bellow,
<div class="row" >
<div class="col-md-6" ng-repeat="(index,data) in mydata">
<-- my content -->
</div>
</div>
This will create output like below,
<div class="row" >
<div class="col-md-6" ng-repeat="(index,data) in mydata">
<-- my content -->
</div>
<div class="col-md-6" ng-repeat="(index,data) in mydata">
<-- my content -->
</div>
<div class="col-md-6" ng-repeat="(index,data) in mydata">
<-- my content -->
</div>
-----------------------
----------------- Etc.
</div>
But i need to repeat <div class="row" > also which contain two <div class="col-md-6" in each row.
This output needs like
<div class="row" >
<div class="col-md-6" ng-repeat="(index,data) in mydata">
<-- my content -->
</div>
<div class="col-md-6" ng-repeat="(index,data) in mydata">
<-- my content -->
</div>
</div>
<div class="row" >
<div class="col-md-6" ng-repeat="(index,data) in mydata">
<-- my content -->
</div>
<div class="col-md-6" ng-repeat="(index,data) in mydata">
<-- my content -->
</div>
</div>
<div class="row" >
<div class="col-md-6" ng-repeat="(index,data) in mydata">
<-- my content -->
</div>
<div class="col-md-6" ng-repeat="(index,data) in mydata">
<-- my content -->
</div>
</div>
------- Etc
Is it possible to do with this usingng-repeat?
As mentioned in comment, if you want your row to repeat with each element in mydata, you would need to put ng-repeat on the <div> that contains row.
Its upto you to decide if you want to hardcode the inner <div> like this:
<div class="row" ng-repeat="data in mydata">
<div class="col-xs-6"> {{data.index}} </div>
<div class="col-xs-6"> {{data.value}} </div>
</div>
or use another ng-repeat on it.
<div class="row" ng-repeat="(index,data) in mydata">
<div class="col-xs-6" ng-repeat="i in data"> {{i}} </div>
</div>
Where mydata is an array of json with following structure:
$scope.mydata = [{index:0,value:'hello'},
{index:1,value:'hello1'},
{index:2,value:'hello2'}
]
here's the plunkr
UPDATE:
If you have data like following,
$scope.mydata = [{value:'hello0'},
{value:'hello1'},
{value:'hello2'},
{value:'hello3'}];
and you want to display it like
hello0 hello1
hello2 hello3
in the view, then you would need to make a check for elements occurring at even iterations and print elements at $index and $index+1. I used $even for the same. but you can also create a custom filter.
<div class="row" ng-repeat="data in mydata">
<div ng-if="$even">
<div class="col-xs-6" > {{mydata[$index].value}} </div>
<div class="col-xs-6" > {{mydata[$index + 1].value}} </div>
</div>
</div>
Heres the updated plunker
Create a directive:
angular.module(....)
.directive('myRows',function(){
return {
restrict : 'A',
template : '<div class="row"><div class="col-md-6" ng-repeat="(index,data) in mydata"><-- my content --></div></div>'
};
});
assumption here is that mydata is defined on the scope of the surrounding controller.
Use it in your html:
<div data-my-rows><div>
Yes ng-repeat is possible to do that.
Here is example code from my project
<tbody>
<tr ng-repeat="proj in projects>
<td>
{{proj.projectID}}
</td>
<td>
{{proj.projectName}}
</td>
<td>
{{proj.project.start date | date:'mediumDate'}}
</td>
<td>
{{proj.projectStatus}}
</td>
</tr>
</tbody>
I hope this would help
So, you now have two answers that correctly address your question and will produce the results you requested, but I wanted to give you some advice (albeit unsolicited) about your Bootstrap structure.
In Bootstrap, it is perfectly acceptable to not repeat your rows for every 12 grid units. In fact, if you want to use multiple responsive column classes together, such as using col-lg-4 and col-md-6 to produce 3 columns on large viewports and 2 on medium viewports, you cannot insert rows between your repeated content.
Since col classes use float and percentages, you don't have to worry about "terminating" the row. Elements will simply float next to one another up to 12 grid units and then start a new natural horizontal "row." That is how Bootstrap's responsive grid works.
You should use rows for two purposes:
To nest columns inside of columns. See my answer here for a
visual explanation of this topic. or
To create horizontal groups of columns. This has the purpose of grouping elements together and clearing column floats for subsequent columns.
What I'm saying is that your initial ng-repeat structure was better and will provide you with a more flexible outcome.

Categories

Resources