StencilJS check if named slot is empty - web-component

I have a stencilJS component with two named slots. Is there a way to determine if the slots have been assigned values? For instance, the code snippet below show named slots for "logo" and "menu." How can I check inside the component if both named slots are not empty? Ideally I want to check from inside the component and during componentWillMount() . Thank you.
<koi-menu breakpoint="768" userToggled="false">
<div class="logo__header " slot="logo"><img src="assets/images/logo.png" /></div>
<ul class="nav__wrapper list mw8-ns center-m" slot="menu">
<li class="nav__listitem"><a class="ttu nav__link" href="???">Why Live Grit</a></li>
<li class="nav__listitem"><a class="ttu nav__link" href="???">Clients</a></li>
<li class="nav__listitem"><a class="ttu nav__link" href="???">Our Programs</a></li>
<li class="nav__listitem"><a class="ttu nav__link" href="???">Our Story</a></li>
</ul>
</koi-menu>

The use of a slot can be detected from the host element in the componentWillLoad() function:
hasLogoSlot: boolean;
hasMenuSlot: boolean;
#Element() hostElement: HTMLStencilElement;
componentWillLoad() {
this.hasLogoSlot = !!this.hostElement.querySelector('[slot="logo"]');
this.hasMenuSlot = !!this.hostElement.querySelector('[slot="menu"]');
}

This might not apply to your problem, but if you only want to style the slotted elements—say only add margins on non-empty slots—you can use the ::slotted pseudo-element:
::slotted([slot="logo"]) {
/* Apply styles to non-empty logo */
}
::slotted([slot="menu"]) {
/* Apply styles to non-empty menu */
}

Related

Angular mix different [ngClass] expressions

I would like to mix the [ngClass] Expressions in Angular, first I want to add an array of classes to my component and then I would also like to add a class based on a Boolean expression.
I can add an array with <li [ngClass]="item.status"> and I can add a class based on a Boolean expression with <li [ngClass]="{active: item.active}">
Note: item.status is an array like item.status=['done', 'progressed']
But how can I mix both styles?
The following doesn't work because only the attribute is duplicate.
<li [ngClass]="item.status" [ngClass]="{active: item.active}">
Thanks in advance
Because you could not have [ngClass] multiple time in same element you could use the combination of [ngClass] and class. When use class in this way with interpolation then you could add several classes as well. Try to use following way.
E.g. HTML
<li class="t-size {{item.status.join(' ')}}" [ngClass]="{'active': item.active }">
E.g. Component
item = {
status : ['done', 'progressed'],
active: true
}
E.g. CSS
.done{
font-weight: bold;
}
.progressed{
color:blue;
}
.t-size{
font-size: 2rem;
}
.active{
background-color: yellowgreen;
}
WORKING DEMO
Hope this will help to you! :)
You can use both as below:
<li [class]="item.status" [ngClass]="{active: item.active}">
You cannot use ngClass more than one time in the single element.
You can do [class.myClass]="booleanExpression", but I don't recommend adding it together with [ngClass] because then you could have a class multiple times.
Instead I would recommend either making [class.myClass]="booleanExpression" for each and every class you use in that HTML-tag (keep in mind the "booleanExpression" can set/unset more than one class so it would not increase the lines of code in your .ts file, only in the template). Or I would make a getter method for [ngClass]:
[ngClass]="liClasses"
get liClasses(): string {
const result = "";
if (conditionOne) { result += " myClassOne"}
if (conditionTwo) { result += " myClassTwo"}
return result;
}
note that solution 1) is more perfomant as getter-methods get triggered on every change-detection and not only when conditionOne or conditionTwo changes.
you can try this code here your app component is
member = true;
paidAmount = true;
setMyClass() {
let classes = {
member: this.someProperty,
paid: this.paidAmount,
};
return classes;
}
where 'member' and 'paid' is class name
and your template use this similar code
<div class="myclass" [ngClass]="setMyClass()">Call to Action</div>

How to access style element in react component?

Need a bit of help with react code.
Suppose a component has style declared in it like
<style>{
.list-done {
color:red;
}
`}</style>
then how do I use this style to style my link tag
<li>item1</li>
I want to use classnames for this. but how to use it in this case?
I was told to use something like this, but can't find anything in the documentation --> import cx from 'classnames';
Well, I have now declared a variable here
const styleItem={color:'red'}
How do I conditionally use it?
I am doing it like this
style={item.done?styleItem:''}
and it doesn't work.
What am I doing wrong here?
You need to add a className to your list Item, With react you can add it using className attribute
<li className={'list-done'}>item1</li>
You might need to load styles in your component. You need to configure style-loader with webpack for it.
Check this answer on how to configure styles with webpack
or else write inline styles
render() {
const listStyles = {
color:red;
}
return (
<li styles={item.done? listStyles: {}}>item1</li>
)
}
You can't declare styles like this in react. You need to put it as a variable and assign it in style attribute.
let styles = { color:red; }
<li style={styles}> item 1 </li>
OR
Declare the class outside (may be in some css) and use className attribute to assigne that class
<li className='list-done'> item1 </li>

Adding and Removing Styles to li group using Angular 2

<ul>
<li (click)="AddColor($event)">ONE</li>
<li (click)="AddColor($event)">TWO</li>
<li (click)="AddColor($event)">THREE</li>
</ul>
AddColor(e){
e.srcElement.style.color="blue"
}
I have the above list when i click any one of the li item out of 3, the clicked label color should be changed. when i click another all item colors should be revert back to original and change color of current clicked item.
#Mehdi said, you should not access DOM directly untill there is a need.
Always keep in mind, drive your view with data rather than accessing
DOM directly
I have forked and working snippet https://plnkr.co/edit/fgINMc?p=preview
When using Angular, you don't want to directly manipulate the DOM element. Rather let angular deal with it.
In your example, you can generate your list from an array you declare in the code like so
export class YourClass{
links:any;
activeLink = -1;
//...
constructor(){
this.links = ['ONE','TWO','THREE']
}
//...
}
and then in your template you could have :
<ul>
<li *ngFor="let link of links; let i = index"
(click)="activeLink = i"
[ngClass]="activeLink == i? 'blue' : '' " >
</li>
</ul>
and declare a css class blue :
.blue{
color:blue;
}

Angular: Animations for an immutable model inside ngFor

I've been unable to get CSS transitions working when using an array of immutable model objects.
Does angular require an in-place update to the property for these to work, or am I doing something wrong?
As an example: (https://plnkr.co/edit/PBSPtk9vMig7cxnHoJqA)
export class Box {
constructor(public selected:boolean){
}
}
function toggle(box:Box):Box{
return new Box(!box.selected)
}
#Component({
selector: 'my-app',
template: `
<ul>
<li *ngFor="let box of boxes">
<div
style="display:inline-block; width: 20px; height:20px; transition: 1s; margin:5px; cursor:pointer;"
[ngStyle]="{'background': box.selected?'green':'red'}"
> </div>
<button (click)="toggle_in_place(box)">toggle (in-place)</button>
<button (click)="toggle_replace_box(box)">toggle (replace box)</button>
<button (click)="toggle_replace_array(box)">toggle (replace array)</button>
</li>
</ul>
`,
})
export class App {
boxes: Box[];
constructor() {
this.boxes = [new Box(true), new Box(false), new Box(true)];
}
toggle_in_place(box:Box){
box.selected=!box.selected;
}
toggle_replace_box(box:Box){
const index = this.boxes.indexOf(box);
this.boxes[index]=toggle(box);
}
toggle_replace_array(box:Box){
this.boxes = this.boxes.map(v=>v===box?toggle(v):v);
}
}
Here I'm trying to animate the color of the div through
[ngStyle]="{'background': box.selected?'green':'red'}"
box.selected can be updated by:
changing the property in-place (toggle_in_place),
replacing the 'box' model (toggle_replace_box)
replacing the entire array (toggle_replace_array).
but toggle_in_place is the only one that results in a visible transition.
Is this just something I have to live with?
What's happening here: When the array changes the ngFor compares the object references in the new array to the old and re-renders any elements that have changed, which breaks the css transitions.
To solve this, ngFor can be given a tracking function which will then be used instead of the reference comparison.
To fix, specify a tracking function to use an id rather than object reference:
trackingFunction(index:number, box:Box){
return box.id;
}
Have ngFor use it:
<li *ngFor="let box of boxes;trackBy:trackingFunction">
Example:
https://plnkr.co/edit/4OXemdSf1Hsm1NU6ZhzT
I think it is something you have to live with. In the latter two cases, since you're creating a new instance of the box each time, it has nothing to transition from.
You could probably make a component which is basically a wrapper around a box and can pass a "transition" boolean to its constructor. Workarounds are pretty dependent on the use case though.

Applying CSS class conditionally in Angular2

I have below HTML code which recursively create list items based on list returned from Component and I want to apply 'first-child' CSS class to first List item only.
<ul class="link-list-horz">
<li *ngFor="let menu of menulist" [ngClass]="first-child:">
{{menu}}
</li>
</ul>
.first-child a
{
border-radius: 10;
}
export class AppComponent {
name = 'Quiz';
menulist = ['Home','AngularQuiz'] ;
useremailid = 'Gaurav-Gupta';
}
Please suggest. I am totally new to Angular2.
ngClass needs a condition to know whether to set that class on the element. You can use the built-in index that comes with ngFor for that.
Try this:
<li *ngFor="let menu of menulist; let i=index" [ngClass]="{'first-child': i === 0}">

Resources