Multibrand strategy in angular 2/4 - javascript

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>

Related

How to protect svelte component from outer css styles

I have page index.html which includes file styles.css ( .foo{width:100%; ..... and other styles} )
via
<link href="styles.css">
And also this page includes svelte application
Svelte application has component with same class name .foo
<div class="foo"></div>
<style>
.foo{background-color:red;}
<style>
Svelte render it into something like this
<input class="foo svelte-12sas231dad">
Is there any method to protect my component's input from outer css ?
For example something to make
<input class="foo-svelte-12sas231dad">
without outer .foo class or may be another decision ?
The best solution is to not use global styles that accidentally apply where you do not want them to. I.e. remove the rules or use very specific class names that will not accidentally be used in components. Everything else is just a workaround to a fundamental problem you will run into again.
Given that Svelte adds classes with hashes you could target the element using a different method, e.g. just the tag type (input) or some data attribute.
As far as I know the only way to really isolate the element from styles would be adding an iframe which is far from ideal.
Are you using Sveltekit?
Perhaps you could use the The CSS !important Rule in your Svelte component styles.
Better yet, why not just change the class names?

How to use same shadow DOM on different places with different classes

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>

Change CSS properties directly in Angular 2+

In jQuery, we can do
JQuery('.someStyle')
.css({"background", "red"})
to directly change the CSS property in style.
In Angular 2+, we can use [style.<property>] to manipulate, is there an option to directly manipulate the style sheet?
Angular offer template reference variable like this. Here #textCont is a template variable which can be passed to component and it can be used to add styles
<div #textCont> I wil change color on click<div>
<button type="button" (click)="changeColor(textCont)">Click</button>
In component
changeColor (elem){
elem.style.color="red"
}
DEMO
Even with the jQuery snippet you posted you don't manipulate the stylesheet, only the CSS properties of the object representing the DOM element.
Why would you want to manipulate the stylesheet directly? You would have to modify it before the browser parses it - before it is sent from server to client. This is hardly desirable.
What you are probably looking for is just a way how to change styles of your DOM element dynamically.
There are many ways how to achieve that with Angular ranging from injecting ElementRef (object providing access to component's host element) to your component's constructor or using Renderer2 in order to change the styles directly on the DOM element, to templating (like the [style.<property>] you have mentioned) or even Angular Animations.
Just fixed a small bug from #brk here. In Angular 13+ you may use:
<div #textCont> I wil change color on click</div>
<button type="button" (click)="changeColor(textCont)">Click</button>
And in Component more "strict typing"
changeColor(elem: { style: { color: string } }) {
elem.style.color = 'red';
}

Angular 5: creating a parent component with an external template and overriding the inner part

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

How to change <body> tag style from child component?

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");
}

Categories