Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Renderer multiple selectRootElement Issue

Tags:

angular

I am trying to use Renderer.selectRootElement to get some elements from my Component, as described here.

Everything works fine, unless I select only one element (plnkr).

As you can see, I have created a component:

export class ExampleComponent implements OnInit{
    @Input() start: any;
    @Input() end: any;

  constructor(public _renderer:Renderer){

  };

    ngOnChanges(){

    }
    ngOnInit(){
        console.log("NG ON CHAN START DATE",this.start);
        console.log("NG ON INIT END DATE",this.end);
        var container =  this._renderer.selectRootElement('.container');
        console.log(container);   
        var inner1 =  this._renderer.selectRootElement('.inner1');
        console.log(inner1);   
        var inner2 =  this._renderer.selectRootElement('.inner2');
        console.log(inner2);   
    }

}

When I try to run this, I have an error of :

EXCEPTION: The selector ".inner1" did not match any elements in [{{exampleData.end}} in MainViewComponent@3:65]

(however, in my app, when only the first container is found, then none others are found).

Any ideas where does this come from?

UPDATE

I found out that the directive is not invoked fully - only div with class container gets added to the HTML.

enter image description here

like image 411
uksz Avatar asked Mar 17 '16 08:03

uksz


People also ask

Is Renderer2 deprecated?

The Renderer class has been marked as deprecated since Angular version 4. This section provides guidance on migrating from this deprecated API to the newer Renderer2 API and what it means for your app.

When should I use Renderer2?

The Renderer2 allows us to manipulate the DOM elements, without accessing the DOM directly. It provides a layer of abstraction between the DOM element and the component code. Using Renderer2 we can create an element, add a text node to it, append child element using the appendchild method., etc.

Why would you use renderer methods instead of using native element methods?

Using the Renderer for manipulating the DOM doesn't break server-side rendering or Web Workers (where direct access to the DOM would break). ElementRef is a class that can hold a reference to a DOM element. This is again an abstraction to not break in environments where the browsers DOM isn't actually available.

What is Renderer2?

The Renderer2 class is an abstraction provided by Angular in the form of a service that allows to manipulate elements of your app without having to touch the DOM directly.


1 Answers

DO NOT USE selectRootElement

Its purpose is not to select random elements by selector in your components view.

Simply see its implementation in DomRootRenderer

selectRootElement(selector: string): Element {
    var el = DOM.querySelector(this._rootRenderer.document, selector);
    if (isBlank(el)) {
      throw new BaseException(`The selector "${selector}" did not match any elements`);
    }
    DOM.clearNodes(el);
    return el;
 }

Do you see something interesting there? It's removing the nodes inside the element! Why would it do that? Because its purpose it's to grab the root element! So which one is the root element? Does this sound familiar?

<my-app>
    Loading...
</my-app>

Yes! That's the root element. Okay then, but what's wrong with using selectRootElement if I only want to grab the element? It returns the element without its children and nothing changes in the view! Well, you can still use it of course, but you will be defeating its purpose and misusing it just like people do with DynamicComponentLoader#loadAsRoot and subscribing manually to EventEmitter.

Well, after all its name, selectRootElement, says pretty much what it does, doesn't it?

You have two options to grab elements inside your view, and two correct options.

  • Using a local variable and @ViewChild
<div #myElement>...</div>

@ViewChild('myElement') element: ElementRef;

ngAfterViewInit() {
   // Do something with this.element
}
  • Create a directive to grab the element you want
@Directive({
    selector : '.inner1,inner2' // Specify all children
    // or make one generic
    // selector : '.inner'
})
class Children {}

template : `
    <div class="container">
        <div class="inner1"></div>
        <div class="inner2"></div>
        
        <!-- or one generic
            <div class="inner"></div>
            <div class="inner"></div>
        -->
    </div>
`
class Parent (
    @ViewChildren(Children) children: QueryList<Children>;
    ngAfterViewInit() {
        // Do something with this.children
    }
)
like image 125
Eric Martinez Avatar answered Jan 03 '23 14:01

Eric Martinez