Adding class to custom element to override style - javascript

Im trying to change the style of an external label of a custom element. The classname is attached correctly but style is not defined.
The custom element is created on stencil and react.
elementRef: ElementRef;
constructor(#Inject(ElementRef) elementRef: ElementRef) {
this.elementRef = elementRef;
}
ngAfterViewInit(): void {
const parentElement = document
.querySelector('.mobilePhone')
.shadowRoot.querySelector('label');
parentElement.className = 'newClassName';
}
<custom-form-element class="mobilePhone" >
<label></label>
</custom-form-element>

parentElement.setAttribute('style', 'color:green');

Related

How to measure element width of an HTML element in Angular?

I want to dynamically create an element and then get its clientWidth. The code snippet looks like this using the regular DOM API.
HTML Snippet:
<div id="non-existent-element"></div>
The element has its css property visibility set to 'hidden'.
Typescript/ Javascript snippet
let labelContainer = document.querySelector('div#non-existent-element');
labelContainer.innerHTML = `
<span>${fundName}</span>
<span> (${performance}%)</span>
`;
let children = labelContainer.children as HTMLCollectionOf<HTMLElement>
for(let i = 0; i < children.length; i++) {
children[i].style.fontSize = fontSize + + 1 +'px';
}
return labelContainer.clientWidth;
How can I achieve the goal using Angular's Element Ref and Renderer2 API?
Simple usage of clientWidth
app.component.html
<p #test>test is the elementRef name</p>
app.component.ts
export class AppComponent implements AfterViewInit {
#ViewChild('test') test: ElementRef;
constructor(private renderer: Renderer2) {
//ERROR TypeError: Cannot read property 'nativeElement' of undefined
// console.log(this.test.nativeElement.clientWidth);
}
ngOnInit() {
//logs: 583
console.log(this.test.nativeElement.clientWidth);
}
ngAfterViewInit() {
this.renderer.setStyle(this.test.nativeElement, 'backgroundColor', 'red');
this.renderer.setStyle(this.test.nativeElement, 'color', 'white');
this.renderer.setStyle(this.test.nativeElement, 'width', '500px');
//logs: 500
console.log(this.test.nativeElement.clientWidth);
}
}

Angular Style DOM element issue

I am trying to query the DOM and add a style to it in Angular. One would think its simple but the following does not work and gives an error, is there an Angular specific way to do it?
let target = document.querySelector(".dom-element");
target.style.width = "100%";
You can do it using ngStyle: https://angular.io/api/common/NgStyle
[ngStyle]="{width: variableFromController}"
Or using #ViewChild and ngAfterViewInit: https://angular.io/api/core/ViewChild
template:
<div #domElement></div>
component:
#ViewChild('domElement') element: ElementRef;
public ngAfterViewInit() {
this.domElement.nativeElement.style.width = '100%';
}
What you need is the renderer2, you should avoid any direct DOM manipulation. This should do it:
constructor(private renderer: Renderer2) {
#ViewChild("dom-element") target: ElementRef;
ngAfterViewInit() {
// target is set here
this.renderer.setStyle(this.target.nativeElement, 'width', '100%');
}

Angular CDK Overlay, change default overlay container

Is there a way to change the OverlayContainer?
I have created a tooltip component, but sometimes I want to attach the overlay to a specific element (by default the overlay is attached to the document body).
Here is how I am creating the overlay:
private initOverlay(): void {
const positionStrategy = this.overlayPositionBuilder
.flexibleConnectedTo(this.elementRef)
.withPositions([this.resolvedConfig]);
this.overlayRef = this.overlay.create({positionStrategy});
}
And this is how I am attaching a template to it:
show(): void {
this.overlayRef.attach(new TemplatePortal(this.tpl, this.viewContainerRef));
}
Please reference this stackblitz example.
looks like you can accomplish this by extending the
OverlayContainer class via the following in app.module.ts
{ provide: OverlayContainer, useFactory: () => new AppOverlayContainer() }
Stackblitz
https://stackblitz.com/edit/angular-material2-issue-ansnt5?file=app%2Fapp.module.ts
This GitHub comment also provides an example of how to package this in a directive
GitHub comment
https://github.com/angular/material2/issues/7349#issuecomment-337513040
Revision 3/22/19 working directive example
Extend the OverlayContainer class via cdk-overlay-container.ts
Stub the class in app.module.ts
providers: [
{ provide: OverlayContainer, useClass: CdkOverlayContainer },
]
In your cdk-overlay-container.ts you are preventing the default _createContainer() from working, and providing your own custom public method myCreateContainer to replace it.
You are essentially creating an empty div here, adding a custom class to it my-custom-overlay-container-class and appending it to the
div the directive is attached to, then passing that container to the
private variable _containerElement in the true OverlayContainer
class.
/**
* Create overlay container and append to ElementRef from directive
*/
public myCreateContainer(element: HTMLElement): void {
let container = document.createElement('div');
container.classList.add('my-custom-overlay-container-class');
element.appendChild(container);
this._containerElement = container;
}
/**
* Prevent creation of the HTML element, use custom method above
*/
protected _createContainer(): void {
return;
}
Then in your cdk-overlay-container.directive.ts your are calling myCreateContainer() and passing the ElementRef as an argument.
this.cdkOverlayContainer['myCreateContainer'](this.elementReference.nativeElement);
Then in your HTML assign the directive where you want it to show up.
<div myCdkOverlayContainer
Stackblitz
https://stackblitz.com/edit/angular-material2-issue-6nzwws?embed=1&file=app/app.component.html

Set content of custom HTML element

I implemented a modal as a custom HTML tag.
class ModalDialog extends HTMLElement {
constructor() {
super();
this.shadow = this.attachShadow({
mode: 'open'
});
this.modal = document.createElement('div');
this.modal.className = 'modal';
this.modalWrapper = document.createElement('div');
this.modalWrapper.className = 'modal-wrapper';
this.modalHeader = document.createElement('div');
this.modalHeader.className = 'modal-header';
this.modalHeader.innerHTML = 'Oops, nothing found!';
...
}
Also, I implemented another class which inherits from HTMLElement. Let's call it A. Said class is trying to create a ModalDialog and should add it to the DOM so it will be displayed.
Now, my question is: How can I set the text of the modalHeader from class A?
I tried to set an attribute and read it in the ModalDialog class but at that time, the attribute is undefined.
class A extends HTMLElement {
...
this.modal.setAttribute('headerText', 'Blablabla');
...
}
Is there any good way to solve this?
Your class A should be able to just access the inner elements and set either their innerHTML or textContent like this:
class A extends HTMLElement {
...
this.modal.innerHTML = 'Blablabla';
...
}
Also, make sure you are placing this.modal into the shadowRoot:
this.shadowRoot.appendChild(this.modal);
On other thing to be aware of is that you do not need to save off the results of this.attachShadow:
this.shadow = this.attachShadow({mode: 'open'});
Since that is already available as this.shadowRoot.

custom web component event call back function in tag

class UioKey extends HTMLElement {
...
eKey(){windows.alert('class eKey function')}
}
function eKey(){
eKey(){windows.alert('document eKey function')}
<template id="uio-key-temp">
<div class="uio-key">
<div class="i" onclick="eKey()"></div><span></span>
</div>
</template>
when clikcing on the .i div agot the document ekey that is firing, i want
the class ekey() to be fired
if i omit the dodument eKey() fuction i got function eKey() undefined
onclick will only work with globally defined functions.
Here is a very quick hack that allows you to use a class function.
// Class for `<uio-key>`
class UioKey extends HTMLElement {
constructor() {
super();
let shadow = this.attachShadow({mode: 'open'});
shadow.innerHTML = '<div><div on-click="eKey">div</div><span>span</span></div>';
let a = shadow.querySelectorAll('[on-click]');
a.forEach(
el => {
const handlerName = el.getAttribute('on-click');
el.addEventListener('click', this[handlerName]);
}
);
}
eKey() {
window.alert('class eKey function');
}
}
// Define our web component
customElements.define('uio-key', UioKey);
<hr/>
<uio-key></uio-key>
<hr/>
I use a custom attribute on-click as a way to grab all elements that want a click handler then I take the value of that attribute and use it as the class function name and pass it into the addEventListener function.
Alternatly to #Intervalia's answer, you could use the getRootNode() method and then the host property to access the Custom Element object from inside the Shadow DOM.
class UioKey extends HTMLElement {
constructor() {
super()
this.attachShadow( { mode: 'open' } )
.innerHTML = uio-key-temp.innerHTML
}
eKey(){
window.alert('class eKey function' )
}
}
customElements.define( 'uio-key', UioKey )
<template id="uioKeyTemp">
<style> .i { background: gray ; height: 10pt } </style>
<div class="uio-key">
<div class="i" onclick="this.getRootNode().host.eKey()"></div>
</div>
</template>
<uio-key></uio-key>
Note that it's always a good practice to use inline scripts because in some situations they can be disabled (for security reasons).

Categories

Resources