I'm trying to create a directive which takes an input element and surrounds it with a div, so for example, my html would be something like:
<input type="text" class="form-control" inputWrapper />
and the desired outcome:
<div class="input-wrapper">
<input type="text" class="from-control" />
</div>
Directive:
@Directive({
selector: '[inputWrapper]'
})
export class InputWrapperDirective {
constructor(private viewContainerRef: ViewContainerRef, private elementRef: ElementRef) {
// what goes here?
}
}
here is how you do it with a directive. The code uses Renderer2, which I think is Angular 4. Probably the same thing can be achieved with Renderer (which is marked deprecated now)
I did all the work in the ngAfterViewInit method. You may probably use the constructor instead.
import { Directive, Renderer2, ElementRef, AfterViewInit } from '@angular/core'
@Directive({
selector: '[inputWrapper]'
})
export class InputWrapperDirective implements AfterViewInit {
constructor(private _renderer:Renderer2, private _el: ElementRef) {
}
ngAfterViewInit() {
// Get parent of the original input element
var parent = this._el.nativeElement.parentNode;
// Create a div
var divElement = this._renderer.createElement("div");
// Add class "input-wrapper"
this._renderer.addClass(divElement, "input-wrapper");
// Add the div, just before the input
this._renderer.insertBefore(parent, divElement, this._el.nativeElement);
// Remove the input
this._renderer.removeChild(parent, this._el.nativeElement);
// Remove the directive attribute (not really necessary, but just to be clean)
this._renderer.removeAttribute(this._el.nativeElement, "inputWrapper");
// Re-add it inside the div
this._renderer.appendChild(divElement, this._el.nativeElement);
}
}
Here is another version, this one works with Renderer, instead of Renderer2. Notice that here I am doing the work in the constructor, deferring it to AfterViewInit does not work.
import { Directive, Renderer, ElementRef } from '@angular/core';
@Directive({
selector: '[inputWrapper]'
})
export class InputWrapperDirective {
constructor(private _renderer:Renderer, private _el: ElementRef) {
// Remove the inputWrapper attribute (not really necessary, but just to be clean)
this._renderer.setElementAttribute(this._el.nativeElement, "inputWrapper", null);
// Get parent of the original input element
var parent = this._el.nativeElement.parentNode;
// Create a div and add it to the parent
// Note: it seems that Renderer creates the element in the right place,
// no need to specify where.
var divElement = this._renderer.createElement(parent, "div");
// Add class "input-wrapper"
this._renderer.setElementClass(divElement, "input-wrapper", true);
// Move the input as a child of the div
divElement.appendChild(this._el.nativeElement);
}
}
I would use a component...
Here is what I would do:
@Component({
selector: 'input-wrapper',
template: '<div><ng-content></ng-content></div>'
})
export class InputWrapperComponent {}
In the template, the <ng-content> tag means that whatever your <input-wrapper> tag is wrapped around goes here.
Note that this component template is <div><ng-content></ng-content></div>. You can change the "div" to be whatever you want it to be...
Wherever you wanted to use this you would not just annotate a tag, you would need to do something like this:
<input-wrapper>
<!-- Anything you want wrapped goes here -->
</input-wrapper>
That is fairly reusable... Change your template and you would change all wrappings.