How to remove extra wrapping elements in the rendered HTML?

I started learning angular 5 3 days ago so I’m quite new at it. I also use angularJS and React to develop applications and I think I don’t understand how angular 5 components fully work. If I create for example a custom button that has a custom text inside (I’m not saying this should be done this way but it’s a simple example that shows my point) like this:

<app-button>
  <app-text>
    My Text
  </app-text>
</app-button>

The rendered DOM results in:

<app-button>
  <button>
    <app-text>
      <span>
        My Text
      </span>
    </app-text>
  </button>
</app-button>

which is unreadable, I wanted to know if there’s a way to remove this wrapping elements and just place the components layout replacing the tags resulting in the following structure:

<button>
  <span>
    My Text
  </span>
</button>

If there’s no way of removing them what are your suggestions? thanks!

4 thoughts on “How to remove extra wrapping elements in the rendered HTML?”

  1. Angular components are directives with templates. According to this:

    Directive configuration @Directive({ property1: value1, … })

    selector: ‘.cool-button:not(a)’ Specifies a CSS selector that
    identifies this directive within a template. Supported selectors
    include element, [attribute], .class, and :not().

    So component selectors can be also attribute selectors. For your example, instead of writing this:

    parent.component.html:

    <app-button>
      <app-text>
        My Text
      </app-text>
    </app-button>
    

    write this:

    parent.component.html:

    <button app-button>
        <span app-text>My Text</span>
    </button>
    

    where :

    app-button.component.ts

    ...  
      selector: '[app-button]',
      template: `<ng-content></ng-content>
    ...
    

    app-text.component.ts

    ...
      selector: '[app-text]',
      template: `<ng-content></ng-content>`
    ...
    

    this would be rendered as you expected:

    enter image description here

    Update after your comment about styling those buttons:

    To style the buttons from inside the button component, and set class in parent component, use :host-context pseudo-class. It is not depricated and works well

    button.component.css

      :host-context(.button-1)  {
        background: red;
      }
      :host-context(.button-2)  {
          background: blue;
      }
    

    app.component.html

    <button app-button class="button-1">
        <span app-text>My Text</span>
    </button>
    
    <button app-button class="button-2">
        <span app-text>My Text</span>
    </button>
    

    Here is the DEMO

    Reply
  2. I had a similar issue. I’ll provide my solution in case someone else has the same problem.

    My component should be able to be used either within other components or as a route from <router-outlet></router-outlet>. When I used the selector as an attribute [my-component] things worked perfectly provided it was used within other components. But when created by <router-outlet></router-outlet> a <div> were created automatically.

    To avoid that, we can simply use multiple selectors, and consider that the selectors can be combined.

    Consider this: I want my component to use the attribute my-component and if it ever should be created by the <router-outlet></router-outlet> it should be wrapped in a <section></section>. To achieve this simply use:

    @Component(
        selector: 'section[my-component], my-component',
        ...
    )
    

    The result will be, if used inside another tag:

    <whatevertag my-component>
         ... component content ...
    </whatertag>
    

    If used as a route:

    <section my-component>
         ... component content ...
    </section>
    
    Reply

Leave a Comment