2

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

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
Florence
  • 1,641
  • 3
  • 13
  • 23

1 Answers1

2

@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.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • I use this list to put some elements in bolds (when some events are detected) this._renderer.setElementStyle(this.itemsList.toArray()[x].nativeElement, 'font-weight', 'bold'); – Florence Sep 14 '16 at 14:18
  • 1
    You can use the shared service to communicate back to the nodes. You can then use `[ngStyle]` or similar to set `bold`. – Günter Zöchbauer Sep 14 '16 at 14:24
  • Have you an example of service (i'm developper beginner) – Florence Sep 14 '16 at 14:28
  • - https://angular.io/docs/ts/latest/cookbook/component-communication.html – Günter Zöchbauer Sep 14 '16 at 14:32
  • You don't need `@ViewChildren()` for that. Just add a property `selectedItem` to the `node x` elements and add `[style.font-weight]="isSelected == myId ? 'bold' : 'normal'"` to the element you want to show bold, then all you need to do is update `selectedItem` to match `myId` of the `resultx` item. – Günter Zöchbauer Sep 14 '16 at 18:28
  • I want also select the "bold" element to put its value in input filed when user press enter. In fact , I want my component works in the same way that – Florence Sep 14 '16 at 18:53
  • Sorry, I don't get what the problem is. Have you tried to solve the problem with a single node first? – Günter Zöchbauer Sep 14 '16 at 18:55
  • yes , with a signle Node , the ViewChildren works fine !, When the child component is initialized , a lineCounter is set to 1. When user press arrowDown key , linecounter + 1, when user press enter , an event is emit which selects the n item (n is lineCounter) in the list given by viewChildren. When I have several levels of nodes, the viewchildren does not work and i cannot known which element is selected – Florence Sep 14 '16 at 19:00
  • I still don't understand why you need `@ViewChildren()` this should work with simple databinding. – Günter Zöchbauer Sep 14 '16 at 19:03
  • to use simple dataBinding , I must give the focus to the selected item. And when user press arrowdown key, focus must be given to next item, ... – Florence Sep 14 '16 at 19:10
  • Doesn't sound too complicated as well. For example with such a simple directive http://stackoverflow.com/a/34573219/217408 – Günter Zöchbauer Sep 14 '16 at 19:11