Angular 2 : #ViewChildren for recursive component - recursion

I have a recursive component which displays des tag a
First , there is the parent component
- html
<div class=" suggestions" style="display : inline-block;" tabindex=-1 #autocompleteDiv>
<span style="width: auto;cursor: pointer;position: absolue">
<div *ngIf="hideInputField" style="border: 1px solid #9e9898">
{{query}}
<a style="color: #0012ff;" (click)="unselectData()">x </a>
</div>
<div *ngIf="!hideInputField">
<input type="text" [(ngModel)]="query" (keyup)="handleKeyPress($event)" (keypress)="handleEnterPress($event)"
style="padding: 4px;" (click)="displayDropdown();" #inputquery (selectValueEvent)="select($event)">
<div #suggestion *ngIf="divMustBeShow===true" tabindex="-1">
<div #recursivediv class="liclass" >
<recursive-tree-view [treeNodeDisplay]="listToBuild"
[selectedItem]="selectedItem"
(selectValueEvent)="select($event)" #recursive></recursive-tree-view>
</div>
</div>
</div>
</span>
</div>
parent code (a part) :
displayDropdown() {
if (this.divMustBeShow) {
if ((this.query && this.query.length < 1) || !this.query) {
this.divMustBeShow = false;
}
} else {
this.divMustBeShow = true;
}
}
handleEnterPress(key: KeyboardEvent) {
if (key.keyCode === 13 || key.keyCode === 20) {
//when press enter (13) or tab (20)
this.query = this.recursive.selecItemWithEnterPress();
}
}
handleKeyPress(key: KeyboardEvent) {
if (key.key === "ArrowDown") {
// if the pressed key is down arrow
this.recursive.setDownListFocus()
} else if (key.key === "ArrowUp") {
// if the pressed key is up arrow
this.recursive.setUpListFocus();
} else if (key.keyCode === 13 || key.keyCode === 20) {
//when press enter (13) or tab (20)
this.query = this.recursive.selecItemWithEnterPress();
} else if ((key.keyCode > 64 && key.keyCode < 91) // key a -> z
|| key.keyCode === 32 // space
|| key.keyCode === 8 // backspace
|| key.keyCode === 46 // delete
|| (key.keyCode > 47 && key.keyCode < 57) // 1-> 9
|| (key.keyCode > 95 && key.keyCode < 106))// 1-9 (numpad)
if (this.query && this.query.length > 1) {
this.divMustBeShow = true;
this.listToBuild = this.filterFunction(this.query, this.originalMap, this.userLanguage);
}
else {
this.divMustBeShow = true;
this.listToBuild = this.filterFunction("", this.originalMap, this.userLanguage);
}
}
html of component recursive-tree-view :
<li *ngFor="let treeNode of treeNodeDisplay.getChildren(); let x = index;" style="cursor: default;">
<div *ngIf="!treeNode.canBeClickable()">
{{treeNode.getLabel()}}
</div>
<div *ngIf="treeNode.canBeClickable()">
<a #item (click)="select(treeNode.getId())" >
{{treeNode.getLabel()}} </a>
</div>
<div *ngIf="nodeHasChildren(treeNode)">
<recursive-tree-view [treeNodeDisplay]="treeNode" (selectValueEvent)="select($event)" > </recursive-tree-view>
</div>
</li>
</ul>
in the component, i want to have of list of all (thus a list of #item)
#ViewChildren(forwardRef(() => "item"))
itemsList: QueryList<ElementRef>;
setDownListFocus() {
if (this.lineCounter < this.itemsList.toArray().length) {
if (this.lineCounter > 0) { // one item is already selected
//unselected previous element
this._renderer.setElementStyle(this.itemsList.toArray()[this.lineCounter - 1].nativeElement, 'font-weight', 'normal');
}
this._renderer.setElementStyle(this.itemsList.toArray()[this.lineCounter].nativeElement, 'font-weight', 'bold');
this.lineCounter++;
}
}
setUpListFocus() {
if (this.lineCounter > 0) {
this.lineCounter--;
this._renderer.setElementStyle(this.itemsList.toArray()[this.lineCounter - 1].nativeElement, 'font-weight', 'bold');
this._renderer.setElementStyle(this.itemsList.toArray()[this.lineCounter].nativeElement, 'font-weight', 'normal');
}
}
lineCounter is set to 1 in ngOnInit of recursive component
the display result is :
..... (input text)
node 1
result1 => the first element must be in bold
result2
node2
result3
result4
result5
when the user press arrowDown key , the first element(thus result1) must have font normal and the second (thus result2) must be in bold
Next if the user press again arrowDown key , the second element (result2) must be normal and the hirs element (result 3) must be in bold
next if the user press upDown key , the third element (result3) must be normal and the second element must be in bold.
The element node cannot be in bold

#ViewChildren() only works for direct children.
A workaround would be a shared service where each treeNode registers itself when it is created and removes itself in ngOnDestroy.
The service can't be registered on a treeNode otherwise each treeNode would get its own instance. It has to be a parent of the root node or the root module.

Related

Full calendar wrap fc-heading and fc-item into single div

Full calendar how to wrap fc-list-heading and fc-list-item into single div?
Need output like this
<div>
<tr>fc-list-heading</tr>
</div>
<div>
<tr>fc-list-item</tr>
<tr>fc-list-item</tr>
</div>
I have done this in following way:
eventAfterAllRender: function (view) {
if (view.name == 'listUpcoming' || view.name == 'listPast') {
let tableSubHeaders = $(".fc-list-heading");
tableSubHeaders.each(function () {
jQuery(this).nextUntil(".fc-list-heading").wrapAll("<tr class='fc-list-item-parent'></tr>");
});
}
}

changing bootstrap progress bar color based on percentage

I created a progress bar using Bootstrap and want the color to change dynamically based on percentage. So far this is what my code looks like:
<div class="progress" style="height: {{c.options.bar_height}}px;">
<div class="progress-bar progress-bar-striped active {{c.progress_bar_color}}" role="progressbar" aria-valuenow={{c.data.percent_complete}} aria-valuemin="0" aria-valuemax="100" style="width:{{c.data.percent_complete}}; font-size: {{c.options.font_size}};">
{{c.data.percent_complete}}
</div>
</div>
In my client controller, I have set the colors as such:
if(c.data.percent_complete<=50) {
c.progress_bar_color = 'progress-bar-danger';
}
if(c.data.percent_complete>50 && c.data.percent_complete<=75) {
c.progress_bar_color = 'progress-bar-warning';
}
if(c.data.percent_complete>75 && c.data.percent_complete<=99.99) {
c.progress_bar_color = 'progress-bar-primary';
}
if(c.data.percent_complete==100) {
c.progress_bar_color = 'progress-bar-success';
}
So far, the progress bar doesn't change color at all. What am I missing to get this to work?
EDIT
I got rid of my client controller and added a ng-class:
ng-class="{'progress-bar-danger': c.data.percent_complete<'33', 'progress-bar-warning': c.data.percent_complete>='33' && c.data.percent_complete<'66', 'progress-bar-success': c.data.percent_complete>='66'}"
This works for every scenario, EXCEPT when it is 100%. When the bar reaches 100%, for whatever reason, it changes to progress-bar-danger instead of progress-bar-success. Any suggestions how to fix this? Thanks!
Could try using ng-class="c.progress_bar_color" instead
Maybe this working JSFiddle helps :)
I fixed this by using uib-progressbar instead:
My client controller:
var value = c.data.percent_complete;
var type;
if (value <= c.options.danger_threshold) {
type = 'danger';
} else if (value > c.options.danger_threshold && value <= c.options.warning_threshold) {
type = 'warning';
} else if (value > c.options.warning_threshold && value <= c.options.primary_threshold) {
type = 'primary';
} else {
type = 'success';
}
c.percentage_label = value+'%';
c.type = type;
My revised HTML:
<uib-progressbar style="height: {{c.options.bar_height}}px;" class="progress-striped active" value="c.data.percent_complete" type="{{c.type}}">
<div>
<span style="font-size: {{c.options.font_size}};">{{c.percentage_label}}</span>
<i ng-if="c.data.empty.length>0" class="fa fa-info-circle info m-l-xs" popover-placement="{{c.dynamicPopover.placement}}" popover-trigger="mouseenter" uib-popover-template="c.dynamicPopover.templateUrl" popover-title="{{c.data.empty.length}} Required Fields Missing"></i>
</div>
</uib-progressbar>

Aplying style to a list item dynamically in view

I have got a list and I want to apply a different style to each item depending on it's Id. Below is my attempt to do that but unfortunately something is wrong. Any help will be very appreciated.
#foreach (var item in Model)
{
#if (#item.Id==0)
{
var css = "background-color:Aqua";
}
else if (#item.Id==1)
{
var css = "background-color:Red";
}
else
{
var css = "background-color:Green";
}
<div style="#css" class="box">
#item.Info
</div>
<div>
#item.Name
</div>
}
Your if condition is already in a code block(foreach code..). So no need of # . Also define the css varibale outside of your if-else blocks
#foreach (var item in Model)
{
var css="background-color:Green";
#if (#item.Id==0)
{
css = "background-color:Aqua";
}
else if (#item.Id==1)
{
css = "background-color:Red";
}
<div style="#css" class="box"> #item.Info </div>
<div> #item.Name </div>
}
Another solution is creating a css class name string inside your loop using your item.Id or item.Code(if that exists) and using that in your markup. With this approach, you may completely eliminate the if condition checking in your razor code, thus making this a much cleaner solution than the previous one.
#foreach (var item in Model)
{
<div class="box myClass-#item.Id">
#item.Name
</div>
<div> #item.Name </div>
}
And in your css class add the css classes as needed
.myClass-0
{
background-color:aqua;
}
.myClass-1
{
background-color:red;
}
.myClass-2
{
background-color:green;
}
Id's might change, so i recommend using some other properties like Code/Name etc.
First of all try setting a default style and bring var css out of the if block, then set the value according to your needs.
But you can also try a helper for that:
#helper styler(int id, String value){
var bg = "background-color:white" // as default
if(id == 1){
bg = "Another color";
}
else if (id == 2) {
// continue setting values
}
<span style="#bg">#value</span>
}
However, this is not really necessary as you can set the style inside of the for loop, but this approach reduce some code duplication.

Odd,Even in ng-repeat Angular

I want to detect odd or oven in ng-repeat . now i have a fiddle that display some dots show/hide randomly , now i want to change background to not only red , for example odd = green and even = red .
function MyCtrl($scope, $timeout) {
$scope.lights = [0,1,2,3,4,5,6,8,9,10,11,12,13,14,15,16,17,18,19,20];
$scope.currentLight = 0;
function light(index){
if($scope.lights.length < index) {
light(0);
} else {
$scope.currentLight = index;
index = Math.floor((Math.random() * 20) + 1);
$timeout(function(){
light(index);
}, 1000);
}
}
light(0);
}
Any advice ?
you can use ng-class-odd and ng-class-even
from https://docs.angularjs.org/api/ng/directive/ngClassOdd
<ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
<li ng-repeat="name in names">
<span ng-class-odd="'odd'" ng-class-even="'even'">
{{name}}
</span>
</li>
</ol>

Apply style to first element in a row of similar elements

I have the following list (the numbers are just for reference)
<div class="A">alpha1</div>
<div class="B">alpha2</div>
<div class="A">alpha3</div>
<div class="A">alpha4</div>
<div class="A">alpha5</div>
<div class="B">alpha6</div>
<div class="A">alpha7</div>
I want to apply one style to DIVS 1, 3 and 7, because they are the first of their class (A) in a row of elements of the same class. Is there a pseudo element / magic I can use for that? Something like (inventing)
not(.A) & .A {color:red} -> if class is A and it is not preceded by an A
Thanks!
You use the :not() pseudo-class with an adjacent sibling combinator + to match an .A that is not immediately preceded by an .A:
:not(.A) + .A
You'll also need to use :first-child to select the very first .A element since it's not preceded by anything:
.A:first-child
Combine them, and you have:
:not(.A) + .A, .A:first-child { color: red; }
jsFiddle demo
Here is a cross browser solution using JavaScript:
http://jsfiddle.net/YhvGw/
function applyStyleToFirstDiv(className, styleAttr, val,baseSelector) {
//Allow to specify a base element to search in
if(baseSelector == null){
baseSelector = document
}
var divElements = baseSelector.getElementsByTagName("div"),
len = divElements.length;
var prevWas = false,currentIs;
// Go through all the divs
for (var i = 0; i < len; i++) {
var cur = divElements[i];
var classes = cur.className.split(" ");
currentIs = false;
for (var j = 0; j < classes.length; j++) {
//If you find a matching class
if (classes[j] === className) {
currentIs = true;
break;
}
}
//If the current one matches, and the last one didn't, apply the style, otherwise don't
if(currentIs && !prevWas){
cur.style[styleAttr] = val;
}
prevWas = currentIs;
}
}
//usage sample
applyStyleToFirstDiv("A","color","yellow");
Here is an example:
div:not(.A) + .A, .A:first-of-type{
color:red;
}

Resources