I have next HTML
// This is parent
<div class="some-class">
// This is child
<totalizer</totalizer>
</div>
How can I change parents style ( add new class ) from child?
You can use an EventEmitter #Output() property that signals the parent component to add/remove a css class dynamically using ngClass.
In your child totalizer component, define,
#Output() cssRefresh = new EventEmitter<boolean>();
//when you need to add/remove css emit an event out to the parent like this
// (preferably in a method in this component),
this.cssRefresh.emit(true); // or 'false' depending on add/remove
Then in the parent html modify this,
<div class="some-class" [ngClass]="{ 'dynamicClass1 dynamicClass2 dynamicClass3': addCss}">
// This is child
<totalizer (cssRefresh)=refreshCss($event)></totalizer>
</div>
Inside your parent component add this method and property,
addCss = false; // set 'initial state' based on your needs
refreshCss(add: boolean) {
this.addCss = add ? true : false;
}
More on ngClass here.
Related
I am working on a component for uploading files. I have made two web components (wc):
wc-uploader the parent with the select files button.
wc-upload the children that are added to wc-uploader as file are added.
If the parent (wc-uploader) has the readonly or disabled attribute, I wish to style the wc-upload items differently.
I think the styling should be within the wc-upload component as it pertains to it.
This is the selector I tried within the wc-upload template but it does not work. I am guess it can't see beyond its shadow root.
wc-uploader[readonly] :host #close { /* here host = wc-uploader */
opacity: 0.5;
}
How would one style this element depending on its parent.
E.g. like if a select item is disabled, then it's option children are disabled too.
For loose coupling, so it doesn't matter when or where children Web Components are attached:
Make the children listen:
this.closest("wc-uploader").addEventListener("close",(evt)=>{
let parent = evt.detail; //evt.target could do
if parent.hasAttribute("close") ...
else ...
});
Then the parent reports its state:
attributeChangedCallback(name,oldValue,newValue){
if(name=="close" || name=="readonly" || name=="disabled"){
this.dispatchEvent(new CustomEvent(name, {
bubbles: false, // Event stays at wc-uploader
detail: this // wc-uploader
}));
}
}
If your children are deeper down in shadowRoots you need:
Custom Element getRootNode.closest() function crossing multiple (parent) shadowDOM boundaries
Or use document. as your "Event Bus", but then you have to be careful with your Event-names.
Be aware addEventListener attaches a listener outside the Web Component scope; so it is not garbage collected when the Component is removed; your task to remove them in the disconnectedCallback
I have a Parent that has a deeply nested child which can get an attribute if selected.
How do I style the background-color of the parent, only if a deeply nested child has an attribute of 'selected'?
<Parent>
<Child>
<NestedChild selected>
This is what I have tried:
const Parent = styled.div`
&[selected] { // But this only styled the child, not the parent}
`;
The CSS way
There isn't one - CSS doesn't allow an element to affect styling of its parents, only that of its children or siblings.
The React purist way
Use the useContext hook along with createContext and a context Provider, or simply pass a callback down through all the nested levels.
The hacky-yet-simple React + vanilla JavaScript way
// set up some styles for `has-child-selected` class
// ...
const Parent = ({ ... }) => {
return <div className="parent">
...
</div>
}
const Child = ({ selected }) => {
const ref = useRef(null)
useEffect(() => {
ref.current.closest('.parent')
.classList[selected ? 'add' : 'remove']('has-child-selected')
}, [selected])
return <div ref={ref}>
...
</div>
}
Edit: I realized I didn't even mention Styled Components in this answer, but I don't think it would change very much. Perhaps someone with more knowledge of Styled Components would be able to enlighten.
I think you can do it with CSS only. So I remember at least. try this.
you can change any tag, and any attr
li:has(> a[href="https://css-tricks.com"]){
color:red;
}
Looks Like it doesn't work at this time. but check when you see this.
:D :D
When Gutenberg creates a class, it seems to be of the format
div.wp-block
div.editor-block-list__insertion-point
div.editor-block-list__block-edit
div.editor-block-contextual-toolbar
div
<your actual block html goes here>
I'd like to be able to add a class to that top div.wp-block element so I can properly style my block in the editor. The class is dynamically generated based on an attribute so I can't just use the block name class. Is there a clean way of doing this? I can hack it using javascript DOM, but it gets overwritten quickly enough.
https://wordpress.org/gutenberg/handbook/designers-developers/developers/filters/block-filters/#editor-blocklistblock
const { createHigherOrderComponent } = wp.compose
const withCustomClassName = createHigherOrderComponent((BlockListBlock) => {
return props => {
return <BlockListBlock { ...props } className={ 'my-custom-class' } />
}
}, 'withCustomClassName')
wp.hooks.addFilter('editor.BlockListBlock', 'my-plugin/with-custom-class-name', withCustomClassName)
You can add class in your block edit view by using className that is present in this.props, className will print class in following format wp-blocks-[block_name]
edit( { className } ) { // using destructing from JavaScript ES-6
return <div className={ className }></div>
}
Suggestion
Always try to look for manipulating DOM via React instead of manipulating DOM directly because React manages it's own state and issues can occur by manipulating DOM directly.
I have a Ractive component that includes within its template html a Bootstrap .collapse element. Calling show() and hide() on this will trigger Bootstrap show and hide transitions.
# the component template:
<div id='my_component>
<div class='collapse'>
some stuff to show or hide in here
</div>
</div>
# the component code:
Ractive.extend({
template : "#component_template",
show : function(){
// call show() on the .collapse element within the rendered template
// but how to get a reference on that element?
},
hide : function(){
// call hide() on the .collapse element within the rendered template
// but how to get a reference on that element?
}
})
How can I get a reference in the javascript to the collapse element? this.el seems to refer to the root (component's parent) and not the component's view fragment.
thanks in advance
ractive.find(querySelector) is what you're looking for:
Ractive.extend({
template : "#component_template",
show : function(){
this.find('.collapse').show();
},
hide : function(){
this.find('.collapse').hide();
}
})
I am using tabs for an app. I want a user button which when clicked on tab-detail.html to update the CSS of an element on its parent tab page tab.html
.controller('TabCtrl', function($scope,Tabs) {
$scope.tabs = Tabs.all() ;
// this populates the "tab.html" template
// an element on this page is: <span id="tab_selected_1">
// when user selects a listed item on tab.html
// it calls tab-detail.html
})
.controller('TabDetailCtrl', function($scope,$stateparams,Tabs) {
$scope.tabs = Tabs.get($stateparams.tabID) ;
// on tab-detail.html is a button <button ng-click="tabSelect()">
$scope.tabSelect = function(thisID) {
// update css on TabCtrl elementID
document.getElementById('tab_selected_1').style.color = "green" ;
}
})
The only way to get to tab-detail.html is via tab.html, thus tab.html must be loaded. But no matter what method I try I can't seem to find a way to access the element that is on another controller's page.
I have tried:
var e = angluar.element('tab_selected_1');
or
var e = angluar.element(document.querySelector('tab_selected_1') ;
e.style.color = "green" ;
The approach you are doing will never do a JOB for you as the DOM you want isn't available. You could achieve this by creating a sharable service that will maintain all of this variable in it and it will be used on UI. For ensuring binding of them your service variable should be in object structure like styleData OR you could also achieve this by creating angular constant.
app.constant('constants', {
data: {
}
});
Then you could inject this constant inside you controller & modify it.
.controller('TabCtrl', function($scope, Tabs, constants) {
$scope.constants = constants; //make it available constants on html
$scope.tabs = Tabs.all() ;
// this populates the "tab.html" template
// an element on this page is: <span id="tab_selected_1">
// when user selects a listed item on tab.html
// it calls tab-detail.html
})
.controller('TabDetailCtrl', function($scope,$stateparams,Tabs, constants) {
$scope.tabs = Tabs.get($stateparams.tabID) ;
$scope.constants= constants; //make it available constants on html
// on tab-detail.html is a button <button ng-click="tabSelect()">
$scope.tabSelect = function(thisID) {
// update css on TabCtrl elementID
$scope.constants.data.color = "green" ;
}
})
Markup
<div id="tab_selected_1" ng-style="{color: constants.data.color || 'black'}">
one way to do this is ....
1) Create a service
2) set a value to a variable in service on button click(tab-detail.html)
3) use that service variable value in tab.html
(Correction update at bottom)
#pankajparkar solution does work, however it does not work with IONIC as the IONIC Framework somehow overrides the DOM settings. Via the DOM Element inspector an see: style='color:green' being added inline to the ITEM/SPAN and can see the element defined as: element.style{ color: green}...but the color of the rendered HTML does not change....it stays black.
Further research shows this is somehow an IONIC problem as other users have the same problem. Other SOFs and blogs indicate that there appears to be a work around but I have yet to see it work.
The above is reformatted for others future use (even though it doesn't work with IONIC), thus I am still looking for a solution to work with IONIC:
.constant('constants', {
tabColors: {
curID:0,
},
})
.controller('TabCtrl', function($scope,Tabs,constants) {
$scope.constants = constants;
}
.controller('TabDetailCtrl', function($scope,$stateparams,Tabs,constants) {
$scope.constants = constants;
$scope.setItem= function(thisID) {
$scope.constants.tabColors.oldID = $scope.constants.tabColors.curID ;
delete $scope.constants.tabColors['tabID_'+$scope.constants.tabColors.curID] ;
$scope.constants.tabColors.curID = thisID ;
$scope.constants.tabColors['tabID_'+thisID] = 'green' ;
}
// HTML in Tab.html
<span id='tab_tabID_{{tab.tabID}}' ng-style="{color: constants.tabColors['tabID_'+tab.tabID] || 'black'}">
Some Text Here
</span>
//HTML in TabDetail.html
<button id="tab_button" class="button button-small button-outline button-positive" ng-click="setItem({{tab.tabID}});">
Select This Item
</button>
Correction: This method does work and does work with IONIC. The problem with IONIC is every element embedded within an ionic tag <ion-item>... <ion-nav>
...etc inherits its own properties from predefined classes...so you must either update the class (not optimal) or have ID tags on every element and/or apply CSS changes (using above method) to every child element. This is not optimal however it will work.
In my case my HTML actually looked like:
<span id='tab_tabID_{{tab.tabID}}' ng-style="{color: constants.tabColors['tabID_'+tab.tabID] || 'black'}">
<h2>Header Text Here</h>
<p>More text here</p>
</span>
The above CSS method works with this:
<span id='tab_tabID_{{tab.tabID}}'>
<h2 ng-style="{color: constants.tabColors['tabID_'+tab.tabID] || 'black'}">
Header Text Here
</h>
<p ng-style="{color: constants.tabColors['tabID_'+tab.tabID] || 'black'}">
More text here
</p>
</span>