I'm using a pre-made web component called rssapp-feed. I want to use this in 2 different places one standard
Like this, this is what the original shadow DOM markup looks like:
and one with modifier class rssapp-feed--sidebar.
Like this:
I have added this modifier class through javascript.
var a = document.querySelector('rssapp-feed')
.shadowRoot.querySelector('.rssapp-feed')
.classList.add('rssapp-feed--sidebar');
The problem is I just want the modifier class to be added in one of the web components only. By adding a modifier class like this it's been adding on both the places. And by this whatever styles I give to modifier class it applies to both of those. Like if I want to set display: none to the last element with the class card-root it affects both the components.
Is there any way to use the same web component on two different places with different classes?
Here is the web component that I'm using in my HTML.
<rssapp-feed id="i0QRdHCkHgYPGNWs"></rssapp-feed><script src="https://widget.rss.app/v1/feed.js" type="text/javascript" async></script>
Here is the example on codeSandbox
Please any help would be appreciated.
All you need is a better CSS selector to pass to the querySelector function. If you have control over the code where <rssapp-feed></rssapp-feed> is being added then simply add some unique class or data-* attribute to it. And then query using it.
Using class:
<rssapp-feed id="i0QRdHCkHgYPGNWs"></rssapp-feed>
<!-- Unique selector using class -->
<rssapp-feed id="i0QRdHCkHgYPGNWs" class="sidebar"></rssapp-feed>
In your JS code:
// Using class + element selector
document.querySelector('rssapp-feed.sidebar')
.shadowRoot.querySelector('.rssapp-feed')
.classList.add('rssapp-feed--sidebar');
Or using data-* attribute:
<rssapp-feed id="i0QRdHCkHgYPGNWs"></rssapp-feed>
<!-- Unique selector using data-* attribute -->
<rssapp-feed id="i0QRdHCkHgYPGNWs" data-id="sidebar"></rssapp-feed>
In your JS code:
// Using attribute selector
document.querySelector('rssapp-feed[data-id="sidebar"]')
.shadowRoot.querySelector('.rssapp-feed')
.classList.add('rssapp-feed--sidebar');
Finally, if you don't have control over the code, use some nested CSS selector using some parent which is unique to the web-component.
You can extend any defined Web Component; and build your own Web Component on top of it:
Example: You can steal a 52 SVG playing cards Web Component with this Dev.to Post
<component-a>
Hello Web Component A!
</component-a>
<component-b>
Hello Web Component B!
</component-b>
<script>
customElements.define('component-a',
class extends HTMLElement {
constructor() {
super()
.attachShadow({mode:'open'})
.innerHTML=`<h1 style="color:red"><slot></slot></h1>`;
}
});
customElements.whenDefined('component-a').then(()=>{
customElements.define('component-b',
class extends customElements.get("component-a") {
connectedCallback(){
if (super.connectedCallback) super.connectedCallback();
this.shadowRoot.querySelector("h1").style.color = "green";
}
});
})
</script>
Related
I'm using a framework similar to bootstrap in Angular that provides custom HTML elements, like , and I want to update the table with a click of the button. In my typescript file, I have code to get the element
(<HTMLTableElement>document.getElementById("resultTable")).updateRows()
and the issue I'm getting is that when I cast it to a HTMLElement, the methods that are predefined such as updateRows() don't work, and I get the error message that Property updateRows doesn't exist on HTML Element. Is there a way for me to call the custom methods so I can use the custom elements? Thanks so much!
(<HTMLTableElement>document.getElementById("resultTable")).updateRows()
Why not view child?
First add a reference to the html element in the component.html file
<result-table #resultTable>...</result-table>
Then you can get that element using viewChild
export class ExampleComponent {
#ViewChild('resultTable') resultTable: ElementRef<any>;
ngAfterViewInit() {
const element = this.resultTable && this.resultTable.nativeElement ? this.resultTable.nativeElement: this.resultTable;
element.updateRows();
}
I'm trying to create some sort of parent component that gives me some general appearance and logic for all my components, but in which I can write some specific logic and templating inside.
Look at these images:
Both of these components have common traits: an upper part with a title and a "hide/show" button, and then, a "body" that differs between both of them. They also share that edit button and so, but my idea is to make that "body" part completely dynamic: that's the part of the component that I want to override with my implementation components.
In other words: I want to make an abstract class with Typescript that holds the template: the rectangle, the header and body, the title (which I want to override from the subclass)... and then, the "body" will be filled with the template provided by the subclass.
How can this be achieved in Angular? This far I've only seen ways of overriding completely the parent components templates, not to complement one with the other. I guess there must be a way to insert HTML code with Angular directives inside a component...
Thing is, which is it?
Thank you!
Let's assume you named your component CardComponent and your selector is app-card.
For the title
You can simply use the #Input() component decorator to get the title string and use it in your generic CardComponent, see the angular documentation: https://angular.io/guide/component-interaction
For the body:
You can create your component containing the header and Edit button and then use the <ng-content> tag to get what's inside the component selector, for example:
<h2>{{title}}</h2>
<ng-content></ng-content>
<button>Edit</button>
And so you can use your card component like this:
<app-card [title]="Channel select">
<!-- Insert your body html -->
</app-card>
PS: to learn more about ng-content, see this: https://medium.com/claritydesignsystem/ng-content-the-hidden-docs-96a29d70d11b
I have this in index.html:
<body class="light-gray">
<app-root></app-root>
<div id="preloader">
<div></div>
</div>
</body>
In app-root i have this:
<laylout></laylout>
Inside layout i have one component where in scss i want to change body on index. Any suggestion how can i do that?
.light-gray{
background: red!important;
}
I tried :host but this is not main parent i need something like: :host :host :host. Host of host of host :)
Use ::ng-deep, like so:
CSS
::ng-deep .light-gray{
background-color: red;
}
DEMO
Use ViewEncapsutation.None:
CSS
.light-gray{
background-color:red
}
Class:
import { ViewEncapsulation } from '#angular/core';
...
#Component({
...
encapsulation: ViewEncapsulation.None
DEMO
To expand a little, View Encapsulation is the default, and it wraps all CSS defined within a view with extra selectors that limit it to only applying within the base object of the view.
It does this (from memory - may not be exactly correct) by adding an attribute to tags in that view (_ng_content-* being the usual format) and then modifying all the embedded CSS attached to the view to add a condition of [_ng_content-*]
So, for example,
A view with view encapsulation enabled might be allocated the attribute "_ng_content-c2".
As a result if its html was
<div class='mydiv'></div>
It would render as
<div _ng_content-c2 class='mydiv'></div>
and if the css for the view had
div.mydiv {color:black}
then the style block with the div would be
div.mydiv[_ng_content-c2] {color:black}
So if you add a body css entry
body {color:black}
it renders as
body[_ng_content-c3] {color:black}
and as a result avoids actually changing your body tag (because your body tag won't have that attribute)
View encapsulation is very useful for allowing clean class names in objects without worrying about namespace collision. It's particularly relevant for shared components where you can't be sure your class names aren't used elsewhere.
On the other hand it makes lots of standard CSS difficult or impossible.
Personally I just don't use it, and prefer to use singular central css files for the whole document, but I'm not suggesting that is best practice.
For those that are interested, here is one of the issues with view encapsulation...
If you have an encapsulated view within another encapsulated view it can be impossible for the outer views CSS to control the inner view as it will wrap it's CSS with the outer attribute, but the items in the inner view will have the inner attribute.
A possible fix to angular for this (but I'm not sure of its consequences) would be that inner views should have both their parents attribute as well as their own. This would become ugly though as the view stack grew.
Another feature I'd love to see would be a way to mark CSS items as not to be encapsulated or to only be "base encapsulated" (that being, encapsulation that only checked they existed within the base object where the CSS was, not that they were themselves created in the base object. That way you could have css like this...
div.taginbase {}
#noEncaps body {}
#baseEncaps div.taginchild
and this would render in the style tag as
div.taginbase[_ng_content_c] {}
body {}
[_ng_content_c] div.taginchild
This would then allow a view to define specific (and context aware) styling of view encapsulated child views.
Give your body an ID. Define a CSS class to be used by a particular component in the global styles.css or styles.scss. Then make sure your component implements AfterViewInit and OnDestroy.
Inside the functions required by these interfaces put:
ngAfterViewInit(): void {
document.getElementById("my-body-id").classList.add("specific-class");
}
ngOnDestroy(): void {
document.getElementById("my-body-id").classList.remove("specific-class");
}
I have an use case which requires me to style an angular component several different ways. I am building seperate components (buttons, lists etc..) for other developers to use for their respective brand. For exampe, the same angular component could be used in 4 different sites (thus the component is exported as a module), but all sites require different css for this component.
What does angular 2/4 cli offer to accomadate this ?
You have several options :
1. Stay very basic with your template
Sometimes, you can do the job with only one DOM node. Build a VERY basic template which can be placed everywhere without affecting the DOM or the styling, and let your collegues create the rest of the template. Thus, the component will not depend on your template, your class names or any styles you would have created otherwise.
Of course, this is kind of an extreme solution :) !
2. Build your component as a wrapper with internal logic
Suppose you need to create a button. Instead of creating a template with a button element, you could also create a div element which contains all the logic you need (a (click) event handler for exemple). This element is going to wrap a button provided by your collegues through transclusion. See below a possible custom-button component template :
<div (click)="handleClickEvent($event)">
<ng-content></ng-content>
</div>
In the parent component you may imagine to have this :
<custom-button>
<button class="btn btn-primary">
<i class="fa fa-chevron-right"></i>
</button>
</custom-button>
3. Use a classic html/css method
Give a template to your component and add class names to the DOM nodes in a way your collegues will be able to style them with a basic CSS.
It will be easy for you to prepare the component, but your collegues may be blocked by the names you choosed or the way the nodes are nested.
4. Give an entry point to provide styles used by the component template
For this, you will need an #Input() variable.
#Input() customStyles: any;
The variable will be provided to a HTML node through [ngStyles].
<button [ngStyle]="customStyles">Send</div>
The customStyles will necessarily be an object of a specific format (see official documentation here).
In the parent template, you could provide the styles like this :
<custom-button [customStyles]="{'max-width.px': widthExp}"></custom-button>
The problem here is that you have to provide as much variables (entry points) as you need to style all DOM nodes of your template...
5. Give an entry point to provide a class
Like before, a variable holds a value provided by your collegues in the parent template. Here, you expect a string to be provided, corresponding to a class name.
#Input() customClass : string;
In your template, you are going to add this custom class to the outer div
<div class="someClass {{ customClass }}> <!-- Outer div -->
<!-- Some DOM elements -->
</div>
Then, your collegues will be able to provide a class and to style all children elements in a CSS file according to the structure you create.
.bigButton {
height: 500px;
width: 1000px;
}
.bigButton > div {
padding: 100px;
}
.bigButton:hover > div {
// ...
}
In the parent template :
<custom-button [customClass]="'bigButton'"></custom-button>
Let's say I have a custom element <foo-bar></foo-bar>
Rather than render the markup into the tags, I want to replace them so that the "foo-bar" element is no longer part of the DOM. I believe Angular does this via the transclude property.
Is there a way to do this in Aurelia?
You need to use the containerless decorator on your component.
From the documentation's Custom Elements Section:
#containerless() - Causes the element's view to be rendered without the custom element container wrapping it. This cannot be used in conjunction with #sync or #useShadowDOM. It also cannot be uses with surrogate behaviors.
So your component should look like this:
import {customElement, bindable, containerless} from 'aurelia-framework';
#customElement('say-hello')
#containerless()
export class SayHello {
#bindable to;
speak(){
alert(`Hello ${this.to}!`);
}
}