Let's assume we have a very simple component as below
#Component({
tag: 'custom-button',
})
export class CustomButton {
render() {
return <button class={{ 'button--blue': true }}>Press Me</button>
}
}
The component above will render as below on the DOM tree
<custom-button class="hydrated">
<button class="button--blue">Press Me</button>
</custom-button>
Now everything is working fine. The problem comes only if we define width: 100% in button--blue class. It will not work because width: 100% of custom-button is essentially pointless.
I know we can solve the problem by using Host element and applying css on it
#Component({
tag: 'custom-button',
})
export class CustomButton {
render() {
return (
<Host class={{ 'full-width': true }}><button class={{ 'button--blue': true }}>Press Me</button></Host>
)
}
}
I am looking for alternative solution, reason being we need to purposely create another generic utility css class just because we are using stencil which I feel its not optimal.
Ultimately it will be great if my CSS is not polluted and I only have button--blue class.
Wondering if there is any alternatives available? Maybe somehow we can omit custom-button element when we render to the DOM tree?
Related
I am able to register a custom vue.js component with
// register
Vue.component('my-component', {
template: '<div class="my-class">A custom component!</div>'
})
Also see https://v2.vuejs.org/v2/guide/components.html
How can I include css classes for my component?
I would expect something like
Vue.component('my-component', {
template: '<div class="my-class">A custom component!</div>',
css: '#... my css stylesheet...'
})
but there does not seem to be a css option.
I know that I could
a) define all css classes in a global css stylesheet or
b) use singe-file-vue-components (would require build tool supporing *.vue files, see https://v2.vuejs.org/v2/guide/single-file-components.html)
but I would prefer to
c) specify a css stylesheet for the component when registering the component.
=> How to do so?
there does not seem to be a css option.
That is correct. You cannot do what you describe. The documentation on single file components is pretty clear that one of the advantages is that you can do this with them and cannot do it without them.
In many Vue projects, global components will be defined using
Vue.component, followed by new Vue({ el: '#container' }) to target a
container element in the body of every page.
This can work very well for small to medium-sized projects, where
JavaScript is only used to enhance certain views. In more complex
projects however, or when your frontend is entirely driven by
JavaScript, these disadvantages become apparent:
[...]
No CSS support means that while HTML and JavaScript are
modularized into components, CSS is conspicuously left out
Here is a way to achieve what you're looking for:
export const ContactUs = Vue.component(
'mycomponent-contact-us'
,{
props: {
backgroundColor: {
type: String
,required: false
,default: "red"
}
}
,data: function(){
return {
}
}
,template: `
<div>
<span class='contact_us_text' >Contact Us Component and its bg color will be {{backgroundColor}}</span>
</div>
`
,mounted: function(){
var css_text = `
.contact_us_text{
color: `+this.backgroundColor+`;
}
`;
var css = document.createElement('style');
css.type='text/css';
css.setAttributeNode( document.createAttribute('scopped') );
css.appendChild( document.createTextNode( css_text ) );
this.$el.appendChild( css );
}
}
);
Its true that you cannot add <style> inside a Vue template or add CSS within
component directly, unless you bind it or define your css globally. But you can create a custom component that will dynamically do it for you. sample
Keep in mind that Vue components are effectively macros.
Where render is the substitution function, and the vueDefinition (defn3 below) is effectively a class for the given tagName.
A template is just a convenient syntactic-sugar shorthand that will be compiled (with some vue-usage pattern restrictions) into a render function if you don't provide your own render function.
const defn3 = {
tagName: 'ae-css',
mounted() {
const vueDefinition = this.$options;
this.$el.appendChild(document.createTextNode(vueDefinition.css));
},
css: `
* {
color: blue;
}
`,
render(h) {
return h('style');
}
}
Vue.component(defn3.tagName, defn3);
With that solution in hand, there are a number of good reasons why you probably don't want something as simplistic as what I just provided.
Namely, you want to have your css modularized such that it does not affect any aspects of your page you did not intend it to. For that, you either need carefully designed css rules with your intended inheritance and scope; probably using a class=... But a better approach would be to just use Vue's facilities that offer similar capabilities automatically.
If you want to use modern browser architecture capabilities for this, then you might want your components to be real browser DOM WebComponents that make use of the shadowDOM and keep your internal elements and styles encapsulated within a shadowRoot. See this library, for doing that in Vue.
You can embed a style tag within your template root element:
Vue.component('my-component', {
template: `
<div class="my-class" my-component>
A custom component!
<style>
.my-class[my-component] {
// ... my-component styles
}
</style>
</div>
`
})
Try this:
this.$el.style.cssText = "border: 5px solid blue;"
I found These Snippets in Stencil's Official Docs.
I am not able to understand how my-embedded-component is accessible in my-parent-component without providing the path of child component. Can anyone help me understand this concept?
Child Component
import { Component, Prop } from '#stencil/core';
#Component({
tag: 'my-embedded-component'
})
export class MyEmbeddedComponent {
#Prop() color: string = 'blue';
render() {
return (
<div>My favorite color is {this.color}</div>
);
}
}
Parent Component
import { Component } from '#stencil/core';
#Component({
tag: 'my-parent-component'
})
export class MyParentComponent {
render() {
return (
<div>
<my-embedded-component color="red"></my-embedded-component>
</div>
);
}
}
There is no path. The relationship is created because the elements are nested in the HTML source.
In plain HTML the following structure, a paragraph inside a div, creates a parent/child relationship in the DOM:
<div>
<p>Hello World!</p>
</div>
You are doing the same thing by using my-embedded-component inside the template of MyParentComponent. Before the parent component is rendered on the page, the initial HTML source is something like:
<my-parent-component>
<div>
<my-embedded-component color="red"></my-embedded-component>
</div>
</my-parent-component>
This is then compiled to apply the behaviors described in the respective components.
The tag property in the #Component decorator defines the name of the custom tag you use in the HTML.
When the Angular compiler reads your initial HTML source code it looks for directives (tags, attributes, etc.) that have to be transformed. When those directives are nested, it creates in implicit relationship: the parent may use some of the children's properties or vice-versa.
In trying to keep React code as reusable as possible, I have often passed CSS classes as a property in React components. The use case for this is that these components will function exactly the same but can look different depending on where they are in the page.
Is passing a CSS class as a property in a React component acceptable, or are there better ways of accomplishing the use case above?
Quick, simplified example:
const ToolTipButton = ({ buttonClass, onButtonClick, children }) => (
<button
className={buttonClass}
onClick={onButtonClick}
data-toggle="pages-tooltip"
data-placement="top"
title="Do Something"
>
{children}
</button>
);
<ToolTipButton buttonClass={'ButtonClass1'} onButtonClick={this.doesSomething}/>
<ToolTipButton buttonClass={'ButtonClass2'} onButtonClick={this.doesSomething}>
// Text and other stuff
</ToolTipButton>
Css selectors are chainable, so with just pure css, you can have individual styles based on the container. The button component can have a static class such as tool-tip-button, and have the stylesheet scope it to the containing component. E.g.
.tool-tip-button {
color: black;
}
.component-a .tool-tip-button {
color: green;
}
.component-b .tool-tip-button {
color: red;
}
Note that react components can (and are suggested to) have their own individual stylesheet, e.g. ComponentA.css and ComponentB.css, and simply import them:
// ComponentA.js
import './ComponentA.css'
As long as your class definitions are in scope, passing class names as props is perfectly acceptable. In cases where class definitions are not in scope, the alternative is to pass a style definition object. For example:
const ToolTipButton = ({ style, onButtonClick, children }) => (
<button
style={style}
onClick={onButtonClick}
data-toggle="pages-tooltip"
data-placement="top"
title="Do Something"
>
{children}
</button>
);
<ToolTipButton style={{backgroundColor: 'red'}} onButtonClick={this.doesSomething}/>
Some component libraries (e.g Material UI) allow you to pass both a class name and a style definition object as props. Generally it's better just to use a class name (to avoid defining styles in your js) if possible.
I have a material UI RaisedButton and I want to change the background color.
I have this:
const style = {
backgroundColor: 'green'
};
export default class CreateLinksave extends React.Component {
<RaisedButton
label="Continue"
style={style}/>
}
but when I refresh the page the material-ui style remains, so how can I override the style?
A second question is, how to avoid doing inline styling? I was thinking on create a js file with constants with the styles I need for my components, import it and access to my styles that way, is that a good approach?
I'm new to React so some help would be nice...
Regards.
You can override the style by passing in the attribute. See the docs for which attributes are supported.
creating a JS file (or multiple files) with your styling sound like a good idea, as long as the rules are simple. if you find yourself merging & overriding styles it would be easier just keeping the style in the component file.
You'll end up with something like
import {green} from './my-style/colors';
...
<RaisedButton label="change min" backgroundColor={green} />
If you are using Material UI you should use jss as seen in their documentation. And use the withStyles HOC. Actual link https://material-ui.com/customization/css-in-js/
Here is an example:
import { withStyles } from '#material-ui/core/styles';
const style = {
back: {
background: green,
},
};
class CreateLinksave extends React.Component {
{classes} = this.props;
<RaisedButton
label="Continue"
style={classes.back}/>
}
export default withStyles(styles)(CreateLinksave );
I think you should check the documentation to have better and reusable components.
I am trying to use the Hidden property in Angular2 and when I include a style that alters the display of the DIV, the hidden property is ignored.
When the code below is run, both divs are displayed.
When I remove the class .displayInline the first DIV is hidden and the second is displayed (as expected).
Can I use Hidden and the display CSS together?
import {ComponentAnnotation as Component, ViewAnnotation as View, bootstrap, NgIf} from 'angular2/angular2';
#Component({
selector: 'hello'
})
#View({
template: `<style>.displayInline{ display:inline }</style><span *ng-if="name">Hello, {{name}}!</span>
<div>
<div [hidden]="hideDiv1()" class="displayInline">should be hidden.</div>
<div [hidden]="hideDiv2()" class="displayInline">should be displayed.</div>
</div>`,
directives: [NgIf]
})
export class Hello {
name: string = 'World';
constructor() {
setTimeout(() => {
this.name = 'NEW World'
}, 2000);
}
hideDiv1(){
return true;
}
hideDiv2(){
return false;
}
}
bootstrap(Hello);
Repository:https://github.com/albi000/ng2-play
I faced similar problem with btn class of bootstrap
<button [hidden]="!visible" class="btn btn-primary">Click</button>
I solved this by adding
[hidden] {
display: none;
}
at the end of css stylesheet I use globally.
This is solved the problem for now.
Note: <span>'s default to "display: inline", you may want to use them instead.
Currently classes override [hidden]. I agree, this isn't as effective as ng-hide/ng-show and I hope it is fixed in future versions of angular2. It is currently an open on issue on the Angular2 repo.
You can overcome the problem by simply wrapping your [hidden] element with the class.
<span class="someClass">
<span [hidden]="hideDiv1()">should be hidden.</span>
</span>
You can use style.display instead of hidden if you need more fine-grained control over visibility.
<div [style.display]="active?'inherit':'none'"></div>