Double ng-repeat to flatten out nested object - html

I have a use in case which I had solved up 'til now with ng-repeat-start. It currently looks like this:
Controller
$scope.providers = [
'Site Hosting',
'Best Hosting',
'Fantastic Hosting'
];
$scope.providerColumns = {
price: {
text: 'Price',
exposed: true
},
site: {
text: 'Website',
exposed: true
},
phone: {
text: 'Phone Number',
exposed: false
}
};
Template
<div class='table-header-row'>
<div class='first-header-cell' ng-repeat='obj in firstCellsHeaders'>
{{obj.text}}
</div>
<div class='middle-header-cell' ng-repeat-start='prov in providers' ng-if='providerColumns.price.exposed'>
{{prov}} {{providerColumns.price.text}}
</div>
<div class='middle-header-cell' ng-if='providerColumns.site.exposed'>
{{prov}} {{providerColumns.site.text}}
</div>
<div class='middle-header-cell' ng-repeat-end ng-if='providerColumns.phone.exposed'>
{{prov}} {{providerColumns.phone.text}}
</div>
<div class='last-header-cell' ng-repeat='obj in lastCellsHeaders'>
{{obj.text}}
</div>
</div>
<div class='table-row'>
<div class='first-cells' ng-repeat='obj in firstCells'>
{{obj.text}}
</div>
<div class='middle-cells' ng-repeat-start='prov in providers' ng-if='providerColumns.price.exposed'>
{{data[prov].price}}
</div>
<div class='middle-cells' ng-if='providerColumns.site.exposed'>
{{data[prov].site}}
</div>
<div class='middle-cells' ng-repeat-end ng-if='providerColumns.phone.exposed'>
{{data[prov].phone}}
</div>
<div class='last-cells' ng-repeat='obj in lastCells'>
{{obj.texLine2}}
</div>
</div>
It's essentially a table built out of div's in order to facilitate reordering the columns after angular finishes rendering.
Now I've realized the object represented in ng-repeat-start is more dynamic than I previously thought, and I want to be able to have a squared ng-repeat, i.e. nested repeat without nested elements, much like the oft-applied solution of using ng-repeat on a tbody element, and then an additional ng-repeat on the tr elements within it, which essentially is a flattened nesting effect. I need a solution for horizontal elements, preferably divs. I can't have them tied together in any way, however, so no nesting of divs or anything like that. Ideally it would look something like this:
<div ng-repeat='prov in providers' ng-repeat='col in providerColumns' ng-if='col.exposed'>
{{data[prov][col]}}
</div>
Any ideas?

Instead going with:
<div ng-repeat='obj1 in parent' ng-repeat='obj2 in obj1'>
{{content[obj1][obj2]}}
</div>
Why not use a nested ngRepeat, like this:
<div ng-repeat='obj1 in parent'>
<div ng-repeat='obj2 in obj1'>
{{content[obj1][obj2]}}
</div>
</div>

Related

Thymeleaf: Delete the div that contains iterating attribute (th:each) after looping through or conditional attribute (th:if) after evaluating

In Thymeleaf, is it possible to delete the wrapper div which contains the conditional loop? Suppose the following code in a SpringBoot application, I intend to erase the first and the second divs after evaluating the condition and looping through the errors list.
<div th:if="${errors != null}">
<div th:each="error : ${errors}">
<div th:utext="${error}"></div>
</div>
</div>
I fancy achieving this without using JavaScript.
You could also use a <th:block /> (which isn't rendered). Also, no need to separate the iteration and text. You should be able to generate the same code like this:
<th:block th:if="${errors != null}">
<div th:each="error : ${errors}" th:utext="${error}" />
</th:block>
(I prefer <th:block /> to th:remove="tag".)
Well it is possible to delete the wrapper div with just adding the th:remove="tag" attribute to the desired div.
<div th:if="${errors != null}" th:remove="tag">
<div th:each="error : ${errors}" th:remove="tag">
<div th:utext="${error}"></div>
</div>
</div>
The result of the above-mentioned code will be:
<div>Error 1</div>
<div>Error 2</div>
<div>Error 3</div>
<div>Error 4</div>
...

Setting the div in angular to different color on click

So though my question might sound familiar the case is a bit different. I have a screen with multiple tasks. To show the tasks I am iterating via the data and my code looks something like
<div *ngFor="let task of tasks" class="scheduleContainer inline-block shadow">
<div id="myHeader" #myHeader class="activeHeader">
{{task.title}}
</div>
<div class="detailsBox">
<div class="row">
<div class="offset-md-1 col-md-auto">
Last Date:
</div>
<div class="col-md-auto">
{{task.lastDate}}
</div>
</div>
<div class="row">
<div class="offset-md-1 col-md-auto">
Duration:
</div>
<div class="col-md-auto">
{{task.duration}}
</div>
</div>
<div class="row">
<div class="offset-md-1 col-md-auto">
Total Runs:
</div>
<div class="col-md-auto">
{{task.totalRun}}
</div>
</div>
</div>
<div class="footer">
<a [routerLink]="['edit-scheduled-tasks']">edit schedule</a>
<a [routerLink]="['view-history-scheduled-tasks']">view history</a>
<a (click)="onClick()">enable task</a>
run now
</div>
</div>
<router-outlet></router-outlet>
Now when I click on the enabled task, I would like the color of that particular div to be changed. In my component, I tried something like
onClick() {
this.myHeader.nativeElement.style.background = 'red';
}
So this did change the color but it did not change the current task but rather some other task. Suggestions?
you can access myHeader from template so you can change the color something like this
<div id="myHeader" #myHeader class="activeHeader">
Change the color by myHeader variable
</div>
<button (click)="myHeader.style.background='red'">click</button>
or you can use a property with ngStyle like this
<div [ngStyle]="{'background-color':color}" >
Another way by ngStyle
</div>
<button (click)="color='red'">click</button>
or you can use a property to toggle class with ngClass
<div [ngClass]="{'red':isClicked}" >
Set class
</div>
<button (click)="isClicked=!isClicked">Toggle class</button>
Example toggle color of taskList by useing ngClass
template
<div *ngFor="let task of taskList"
[ngClass]="{'red':selectedTasks[task.id]}"
(click)="selectedTasks[task.id]= !selectedTasks[task.id]" class="task">
{{task.name}}
</div>
or you can use button to toggle the state
<div *ngFor="let task of taskList"
[ngClass]="{'red':selectedTasks[task.id]}"
class="task">
{{task.name}}
<button (click)="selectedTasks[task.id]= !selectedTasks[task.id]">toggle {{task.name}}</button>
</div>
if you want to set the state without toggle on click event just set
the state to true like this selectedTasks[task.id] =true
component
taskList =[
{id:1 , name:'Task 01'},
{id:2 , name:'Task 02'},
{id:3 , name:'Task 03'},
{id:4 , name:'Task 04'},
{id:5 , name:'Task 05'},
];
selectedTasks = {};
stackblitz demo
Not a clean way to do, but it still works. Send an index of selected element to onClick(i) and add the color to selected div. So that you don't mess with template reference.
html
<div *ngFor="let task of tasks; let i=index;" class="scheduleContainer inline-block shadow">
<div id="myHeader" #myHeader class="activeHeader">
{{task}}
</div>
<div class="footer">
<a (click)="onClick(i)">enable task</a>
</div>
</div>
component.ts
onClick(index: number) {
document.querySelectorAll('#myHeader')[index]
.style.background = 'red';
}
DEMO
It's not a good practice to manipulate DOM directly.
Angular: Stop manipulating DOM with ElementRef!
As an alternate, It's easy to bind inline style in your Angular templates using style binding.
Since you would like the color of that particular div to be changed. Use A boolean array:
component:
export class AppComponent {
name = 'Angular';
public styleArray=new Array<boolean>(10);
onClick(i)
{
this.styleArray[i]=true;
}
}
While Iterating pass index to onClick(i) to set particular index of array true and apply style dynamically
[style.color]="styleArray[i] ? 'green':'black'"
<div *ngFor="let task of tasks; let i=index" class="scheduleContainer inline-block shadow">
<div id="myHeader" [style.color]="styleArray[i] ? 'green':'black'" class="activeHeader">
{{task.title}}
</div>
........rest of the code
<a (click)="onClick(i)">enable task</a>
Live Demo

Styling background-color for div When div height changes

I have the follow ASP.Net MVC razor code.
#{
foreach (var note in Model.Notes)
{
<div class="row data-area" style="margin-bottom:5px !important">
<div class="small-2 columns magmalabel" style="background-color:#01466d;">
#Html.DisplayFor(m => note.NoteTypeId)
</div>
<div class="small-10 columns">
#Html.DisplayFor(m => note.Notes)
</div>
</div>
}
}
This works, but occasionally, the second column word wraps due to the length, this is by design, but the first div that displays:
#Html.DisplayFor(m => note.NoteTypeId)
does not get the correct formatting as it does not color the second row.
I know that this is a height issue as so far the only way I have managed to do this is by setting the height property to twice the size in the Chrome console.
<div class="small-2 columns magmalabel" style="background-color:#01466d;height:50px;">
In Chrome, a single height equals 25px's
I have looked on SO and seen many solutions, but so far none has worked.
Had to resort to some jQuery to sort this out.
Added a placeholder class, rowHeading, to the markup as below:
<div class="row data-area" style="margin-bottom:5px !important">
<div class="small-2 columns magmalabel rowHeading" style="background-color:#01466d;">
#Html.DisplayFor(m => note.NoteTypeId)
</div>
<div class="small-10 columns">
#Html.DisplayFor(m => note.Notes)
</div>
</div>
Then simply added the following jQuery script:
$('.rowheading').each(function () {
var parentHeight = $(this).parent().height();
$(this).height(parentHeight);
});
This enabled the browser to set the row height for all divs on the page to correctly format the height.

Draw table, and put div with specific class to new row

I have some rendered data, and I want to display it "in table".
How can I implement this using CSS? (Maybe flex could help me)
<div class="test">1</div>
<div class="test">2</div>
<div class="newRow">3</div>
<div class="test">4</div>
<div class="newRow">5</div>
<div class="test">6</div>
Expected result, something like this:
1 3 5
2 4 6
*
Divs are rendered by angular2 loop (ngFor). There may be 2 or more. I need more dynamic solutions which would be depends on "class="newRow"" - end of row.
Perhaps try this:
<div>
<div class="test">1</div>
<div class="test">3</div>
<div class="test">5</div>
</div>
<div>
<div class="test">2</div>
<div class="test">4</div>
<div class="test">6</div>
</div>
CSS:
.test{
display:inline;
}
Fiddle here: https://jsfiddle.net/d0Ltnenp/2/

HTML element to contain id or name from ko.observable using foreach

Below I have a for-each loop using knockout.js.
<div data-bind="foreach:Stuff">
<div class="row">
<span data-bind="text: $data.name"></span>
</div>
</div>
I need to have the HTML Element with an id or name or something that reflects a unique value related to the $data.name value, as another method runs asynchronously, and needs to know which HTML element to update.
Ideally, it would look something like this, I guess:
<div data-bind="foreach:Stuff">
<div class="row">
<span id="data-bind='text: $data.name'" data-bind="text: $data.name"></span>
</div>
</div>
I have found a knockout syntax that applies values during runtime to specified attributes:
<div data-bind="foreach:Stuff">
<div class="row">
<span data-bind="attr: { id: $data.name}"></span>
</div>
</div>
Are you looking for this
<div data-bind="foreach:Stuff">
<div class="row">
<span data-bind="text: $data.name,attr:{id:$data.name}'"></span>
</div>
</div>
Here name is a observable i believe when ever there is change in name in stuff it will automatically updates its value & attr:{id}just to give a dynamic id to element using available bindings .