Home > database >  Dynamically generated elements don't get styles
Dynamically generated elements don't get styles

Time:03-25

I am running a project in Angular 9.1.13, on Centos 7.

I have a component with static HTML code, as follows:

<ul id="myUL">
   <li><span >Top 1</span>
      <ul >
         <li>Sub 1</li>
         <li>Sub 2</li>
      </ul>
   </li>
</ul>

My CSS looks as follows:

ul, #myUL {
  list-style-type: none;
}

#myUL {
  margin: 0;
  padding: 0;
}

.caret {
  cursor: pointer;
  user-select: none;
}

.caret::before {
  content: "\25B6";
  display: inline-block;
  margin-right: 6px;
}

.caret-down::before {
  transform: rotate(90deg);
}

.nested {
  display: none;
}

.active {
  display: block;
}

And my component executes this code in the ngOnInit() method:

...
const toggler = document.getElementsByClassName('caret');
let i;
     
for (i = 0; i < toggler.length; i  ) {
   toggler[i].addEventListener('click', function() {
      this.parentElement.querySelector('.nested').classList.toggle('active');
      this.classList.toggle('caret-down');      
   });
}
...

With things as described, everything works just fine. I can click that red icon, and the sublist can be shown or hidden:

enter image description here

The problem comes however if I have to generate the content of the list dynamically.

In order to do so, the HTML looks as follows:

<ul id="myUL"></ul> <!-- Deliberately empty unordered list -->

On the ngOnInit() method above, and before the code I had already mentioned, I prepend this other chunk of code:

...
const myUL = document.getElementById('myUL');

myUL.innerHTML = 
   '<li><span >Top 1</span><ul ><li>Sub 1</li><li>Sub 2</li></ul></li>';

// The rest of the code mentioned above
...

With that code, things don't look the same. The sublist is not collapsible anymore, styles are not applied... although surprisingly, click events are actually attached to the caret elements:

enter image description here

So, the question is... how can I achieve the same result as in the first screenshot, but dynamically generating the list content.

CodePudding user response:

You should use ng-deep and disable scope styles for elements, because you are trying to create these elemenets by yourself

#myUL ::ng-deep {
  .caret {
    cursor: pointer;
    user-select: none;
  }

  .caret::before {
    content: "\25B6";
    display: inline-block;
    margin-right: 6px;
  }

  .caret-down::before {
    transform: rotate(90deg);
  }

  .nested {
    display: none;
  }

  .active {
    display: block;
  } 

}

CodePudding user response:

You can generate the list dynamically in the html using angular. I would setup my a caret object / list in my component and then take advantage of *ngFor and [ngClass] to achieve what you're trying to do. Something like:

component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {
  carets = [
    {
      name: 'Top 1',
      children: [{ name: 'Sub 1' }, { name: 'Sub 2' }],
      toggle: false,
    },
    {
      name: 'Top 2',
      children: [{ name: 'Sub 1' }, { name: 'Sub 2' }],
      toggle: false,
    },
  ];
}

component.html

<ul id="myUL">
  <li *ngFor="let caret of carets" [ngClass]="caret.toggle ? 'active' : ''" >
    <span
      
      [ngClass]="caret.toggle ? 'caret-down' : ''"
      (click)="caret.toggle = !caret.toggle"
      >{{ caret.name }}</span
    >
    <ul [ngClass]="caret.toggle ? '' : 'nested'">
      <li *ngFor="let child of caret.children">{{ child.name }}</li>
    </ul>
  </li>
</ul>
  • Related