20

I know it should be easy but angular 2.0 has no many examples yet..

In one of my components in some case I need to add class on my body tag. But my application is bootstrapped deeper than body, so I need something like

angular.element('body').addClass('fixed');

but in Angular 2.0..

BTW, I know I can somehow bootstrap my application to include body tag, but I think in some other cases I would need to select some elements anyway, so I need a solution how to do this simple task - "select element and add class to it"

Zameer Ansari
  • 28,977
  • 24
  • 140
  • 219
Arūnas Smaliukas
  • 3,231
  • 6
  • 27
  • 46

5 Answers5

13

Update

I'm not sure if DOM is actually still supported in RC. The related statements aren't very clear. Something like

DOM is only for internal use. Either access the DOM directly or use a custom renderer.

I haven't see how a custom renderer might be implemented or how to provide an implementation depending on the current platform (webworker, server, DOM thread).

Update This seems to be the Angular2 way

import { DOM } from 'angular2/src/platform/dom/dom_adapter';

DOM.addClass(DOM.query("body"), 'fixed');

Import from .../src/... at your own risk. .../src/... is considered private implementation and you can't expect any guarantees that the API won't change without notice.

I tried it in Dart and it works fine (not sure if the TS import above is correct though). In Dart DOM is exported by package:angular2/angular2.dart

Original

If you want to access a DOM element that's outside of your Angular application root, just use document.querySelector(), no need to involve Angular.

Mikeumus
  • 3,570
  • 9
  • 40
  • 65
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • But angular 1.x had jquery lite or something to help with adding/removing class. Does angular 2.0 has something like jqlite to help with adding/removing classes for elements outside application root? – Arūnas Smaliukas Dec 23 '15 at 10:44
  • 4
    I don't think so. Angular2 is all about avoiding direct DOM access to ensure everything is working with webworkers and server-side rendering. – Günter Zöchbauer Dec 23 '15 at 10:47
  • 1
    Thanks for this hint. I ended up doing the following: `ngOnInit() { var dom = new BrowserDomAdapter(); dom.addClass(dom.query("body"), "login-content"); } ngOnDestroy() { var dom = new BrowserDomAdapter(); dom.removeClass(dom.query("body"), "login-content"); }` – MIP1983 Jan 20 '16 at 15:05
  • 1
    In beta7 the import is now `import { BrowserDomAdapter } from 'angular2/platform/browser';` See available methods here: https://angular.io/docs/ts/latest/api/platform/browser/BrowserDomAdapter-class.html – Yoav Kadosh Mar 26 '16 at 00:07
9

As of Angular2 Version 2.0.0-beta.17.

Attribute Directives in Angular2 will do this for you nicely.

Please see this plunk written in TypeScript. This does what you want nicely.

The directive file my-highlight.ts has the line:

this.renderer.setElementClass(this.element, "red", true);

This sets the CSS class on the element.

While template.html has the actual element which is decorated with the directive [myHighlight]:

<p [myHighlight]>Mouseover to highlight me!</p>

This, to me, provides the least hack-ish answer to the question without any dependency on jqLite.

JDTLH9
  • 1,765
  • 1
  • 23
  • 34
  • I don't see how this is related to the question to add a class to elements outside Angulars root component. What this tutorial shows is actually bad practice. See also https://github.com/angular/angular.io/issues/1670. If you think you must use `ElementRef.nativeElement` then use it with `Renderer` like shown in http://stackoverflow.com/a/34573219/217408 instead of directly calling methods on it. – Günter Zöchbauer Jun 15 '16 at 17:14
  • Thank you for the link to the other post using renderer, this looks interesting. I am going by the latest Angular2 documentation, if that changes I will update my plunk. I still feel this is a good answer as the question states they want to "select element and add class to it". I will update my answer to edit the "class" attribute instead of "style". – JDTLH9 Jun 15 '16 at 18:18
  • In my opinion It still doesn't fit to the question because it doesn't allow to set the attribute on the `` element. This is not too obvious from the question though. – Günter Zöchbauer Jun 15 '16 at 18:22
  • Yes I see the body tag in the question now. I think my answer may help people who want to select any other DOM element though. – JDTLH9 Jun 15 '16 at 18:27
  • 1
    I have updated my answer to change the CSS class. I have added the renderer to do this also. I would very much like to leave this answer here, because it was this specific problem that lead me here, I believe others may find this useful. – JDTLH9 Jun 16 '16 at 09:32
  • Sure, no prob. . . . . – Günter Zöchbauer Jun 16 '16 at 09:35
6

As of angular 2.4 you should inject the DOCUMENT and don't interact with any adapter:

import { Component, Inject } from '@angular/core';
import { DOCUMENT } from '@angular/platform-browser';

@Component({})
export class MyClass {

    constructor (@Inject(DOCUMENT) private document) { }

    doSomething() {
        this.document.someMethodOfDocument();
    }
}

Further reading: https://github.com/angular/angular/issues/8509

Stefan Rein
  • 8,084
  • 3
  • 37
  • 37
3

DOM Manipulation in Angular 2 / 4 app

To manipulate the DOM in Angular 2/4 apps, we need to implement the method ngAfterViewInit() of AfterViewInit. The method ngAfterViewInit() is called when the bindings of the children directives have been checked for the first time. In other words, when the view is initially rendered.

The @ViewChild provides access to nativeElement. It is recommended to not access nativeElement inside the ngAfterViewInit() because it is not browser safe. Also, it's not supported by web workers. Web workers will never know when the DOM updates.

The right way is to use renderer. The renderer needs to be injected to the component constructor. We need to provide an id reference to the HTML element on the view something like this:

<p #p1></p>

It shall be accessed by the corresponding coponent .ts file, something like this:


export class SampleComponent implements AfterViewInit {

  @ViewChild("p1") p1;

  constructor(private renderer: Renderer2) //Renderer set to be depreciated soon
  { }

  ngAfterViewInit() {

    //recommended DOM manipulation approach
    this.renderer.setStyle(this.p1.nativeElement, //setElementStyle for soon to be depreciate Renderer
      'color',
      'red');

    //not recommended DOM manipulation approach
    //this.p1.nativeElement.style = "color:blue;";
  }

}
Zameer Ansari
  • 28,977
  • 24
  • 140
  • 219
1

I don't recommend direct DOM access from Angular, but you have a DOM hook via the ElementRef of your component. Once you have access to it you can use it directly or via jquery or any other DOM manipulation technique. I have included an example of how to use jquery to run general queries. If you are always going for the body tag you don't really need ElementRef, but it's useful for something that is relative to the root of the component.

import {Component, ElementRef, OnInit} from '@angular/core';

declare var jQuery:any;

@Component({
    selector: 'jquery-integration',
    templateUrl: './components/jquery-integration/jquery-integration.html'
})
export class JqueryIntegration implements OnInit {
    elementRef: ElementRef;

    constructor(private elementRef: ElementRef) {
    }

    ngOnInit() {
        jQuery(this.elementRef.nativeElement).find('.moving-box').draggable({containment:'#draggable-parent'});
    }
}

More info here: http://www.syntaxsuccess.com/viewarticle/using-jquery-with-angular-2.0

Demo: http://www.syntaxsuccess.com/angular-2-samples/#/demo/jquery

TGH
  • 38,769
  • 12
  • 102
  • 135
  • 11
    You should really try NOT to use jQuery. Its really not need w/ modern dom traversal and standardization and ng2 provides a lot of the functions you might need that browser doesn't have – amcdnl May 20 '16 at 13:47
  • @amcdnl Can you provide an example of how to access an element with a class ".moving-box" as shown in the code above? – Chris Fremgen Nov 15 '16 at 14:22
  • 1
    elementRef.nativeElement.getElementsByClass – amcdnl Nov 16 '16 at 15:01
  • 1
    @juanpastas How does it have no conventions when there's a style guide for it, and it's literally the same 3 files (html/style/typescript) over and over again basically. Try the Angular CLI, it'll make life easier for you as well. – Mark Pieszak - Trilon.io Mar 22 '17 at 21:25