ngx-stripe
ngx-stripe copied to clipboard
[BUG] The 'containerClass' attribute doesn't work for component level css files due to missing '_ngcontent' tokenizations
Describe the bug When using the 'containerClass' attribute on ngx-stripe elements, the specified class does get added to the wrapping div of the Stripe element as expected, however if the styles for the target element are defined in an individual component's css file, the css will be transformed to include Angular's '_ngcontent' tokenizations, and because these tokenizations aren't making it to the wrapping div element the style fail to apply to the container element.
Note, this occurs regardless of component encapsulation settings.
To Reproduce Steps to reproduce the behavior:
- Create a component with a
ngx-stripe-card-number
which includes the 'containerClass' attribute with a class that defines style in the component's CSS file:
// component.html
<label class="subtitle1" for="card-number-element">
Credit card information
<div style="margin-top: 14px"></div>
<ngx-stripe-card-number
#cardNumber
containerClass="stripe-element-input"
disabled="isProcessingPayment"
[options]="cardNumberOptions"
(change)="handleStripeChange($event)"
></ngx-stripe-card-number>
</label>
// component.scss
.stripe-element-input {
border: 1px solid rgba(73, 80, 87, 0.23);
border-radius: 4px;
padding: 0 0.857em;
}
.stripe-element-input:hover {
outline: 2px solid #163f6c;
}
- Build and run the component, observe in developer tools that the specified container class is applied to the wrapping div, but the styles defined for that class are not shown or found:
Note also the lack of _ngcontent
tokenization on the wrapping element. If we look at the css definitions in the webpack chunks we can see the defined css classes gain a [_ngcontent]
token:
-
Move the container class style definition to the root
/styles.scss
file in your angular project, which should bypass tokenization as it's not part of a component's style, and rebuild/run. -
The styles are now applied to the wrapping element.
Expected behavior The container class should account for angular build time tokenizations so defined classes at the component level can be applied.
Screenshots See above.
Desktop (please complete the following information):
- OS: macOS 12.5.1
- Browser chrome
- Version 105.0.5195.125
Hi @KyleLehtinenDev, thanks for the very well document issue.
Can you please share with me the versions you're using of @angular/core, @stripe/stripe-js and ngx-stripe, in case there is a problem with some specific version.
Also, can you share with me what's the value of cardNumberOptions
so I can see what options are you passing in case the conflict in an unexpected way?
Thanks in advance
R
@richnologies you're welcome, thanks for your work on this module 😄
Your requested details: "@angular/core": "^14.0.6", "@stripe/stripe-js": "^1.32.0", "ngx-stripe": "^14.1.0",
baseStyle: StripeElementStyleVariant = {
fontFamily: 'Lato, sans-serif',
fontSize: '16px',
fontWeight: '400',
padding: '50px',
lineHeight: '3',
color: '#242F3E',
letterSpacing: '0.15px',
};
cardNumberOptions: StripeCardNumberElementOptions = {
style: {
base: this.baseStyle,
invalid: {
color: 'red',
},
},
showIcon: true,
placeholder: 'Credit card number',
};
Hello again @KyleLehtinenDev, thanks again for your very detailed answer.
I've been checking your problem I was able to replicate it. What I realice a bit too late is that this is the Angular expected behaviour. By default Angular components styles are private and they're not suppose to apply to child elements.
What you call _ngcontent tokenization
is actually the implementation of the Angular view encapsulation and is a private API, not meant to be use or modified by library authors.
https://angular.io/guide/component-styles#component-styling-best-practices
This leaves us with three alternatives:
- Using the styles.scss to set global styles as you suggested in point 3
- You can also change the encapsulation of your component to None
- You can use the
::ng-deep
pseudoselector that helps you do exactly what you want. Of the three options this is by far the more "private" as this styles will only apply to the component and it's childs. It won't apply to siblings or ancestors.
You can change this snippet you've shared:
.stripe-element-input {
border: 1px solid rgba(73, 80, 87, 0.23);
border-radius: 4px;
padding: 0 0.857em;
}
.stripe-element-input:hover {
outline: 2px solid #163f6c;
}
to this:
::ng-deep .stripe-element-input {
border: 1px solid rgba(73, 80, 87, 0.23);
border-radius: 4px;
padding: 0 0.857em;
}
::ng-deep .stripe-element-input:hover {
outline: 2px solid #163f6c;
}
and it should work fine.
Here is a trivial example, but check how without the ::ng-deep
pseudoselector the h1 element is not red
https://stackblitz.com/edit/angular-ivy-s2hqmd?file=src%2Fapp%2Fapp.component.ts&file=src%2Fapp%2Fhello.component.ts
Hope this helps you
I leave the ticket open in case you have more questions
Kind regards
R
I'm closing this due to inactivity. If you have any other question feel free to re-open it or create a new issue