Vue - bind a css class name with a property value - css

I would like to set a class to a child component based on in which parent component I am using it. So, for example, I have a dropdown menu, that I would like to use in more components, but I would like to give it a different class based on in which component I am using it.
Something like this, parent component top-bar:
<dropdown-menu :menu="link" :parent:'top-bar'></dropdown-menu>
And then in the dropdown-menu component:
<div class="dropdown" :class="{ parent: parent }">
<script>
export default {
name: 'dropdown-menu',
props: ['parent'],
But, that is not working, how can I do this?

You had a typo :parent:'top-bar' -> :parent='top-bar' and your class binding would always pass the 'parent' string as a class. Learn more here.
I also made a small working example:
Vue.component('parent1', {
template: '<div><dropdown-menu :menu="link" :parent="top_bar"></dropdown-menu></div>',
data () {
return {
link: 'a link',
top_bar: 'parent1'
}
}
});
Vue.component('parent2', {
template: '<div><dropdown-menu :menu="link" :parent="top_bar"></dropdown-menu></div>',
data () {
return {
link: 'another link',
top_bar: 'parent2'
}
}
});
Vue.component('dropdown-menu', {
template: '<div class="dropdown" v-bind:class="parent">{{ menu }}</div>',
props: ['parent', 'menu']
});
var vm = new Vue({
el: '#app'
});
.parent1 {
color: red;
}
.parent2 {
color: blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.js"></script>
<div id="app">
<parent1></parent1>
<parent2></parent2>
</div>

Related

Vue JS 3 Class Binding Does not Apply

In attempting to apply an example from the Vue 3 guide on class and style bindings, I'm unable to get class binding to work.
const App = {
data() {
return {
message: 'Vue version: ' + Vue.version,
classObj: {
active: true,
'text-danger': false
}
}
}
}
Vue.createApp(App).mount('#result')
.active {
color: #00FF00;
}
.text-danger {
color: #FF0000;
}
<script src="https://unpkg.com/vue#3.2.16"></script>
<div id="result" class="static" :class="classObj">{{ message }} <br> {{ classObj }}</div>
It appears that Vue is loading and the Vue app is returning classObj properly { "active": true, "text-danger": false }. Note that I'm printing the value of classObj to the DOM merely to inspect its value.
I have tried:
applying the classObj payload directly in the div statement with the same outcome (placing the object { "active": true, "text-danger": false } as the class attribute in the <div>.
several different CDN sources and several different Vue 3 build versions.
similar examples using Vue 2 with success, but have not been able to apply the binding (at all) in Vue 3.
The issue here is you are trying to bind to attributes to the top level element that Vue instance is mounted to. Try move the reactive component to a child node:
const App = {
data() {
return {
message: 'Vue version: ' + Vue.version,
classObj: {
active: true,
'text-danger': false
}
}
}
}
Vue.createApp(App).mount('#result')
.active {
color: #00FF00;
}
.text-danger {
color: #FF0000;
}
<script src="https://unpkg.com/vue#3.2.16"></script>
<div id="result">
<div class="static" :class="classObj">{{ message }} <br> {{ classObj }}</div>
</div>

Vue.js change background-image on multiple conditions

is there a way to change a background-image on conditions?
Im trying to build a weatherapp and I will change the background of a div.
Like:
Weather api returns:
rainy -> change background image to rain.jpg
snow -> change background image to snow.jpg
sunny -> change background to sunny.jpg
etc.
I've tried multiple ways already but nothing worked.
<template>
<div :class="{background_image}"></div>
</template>
<script>
export default {
// ...
data() {
return {
backgroundImage: 'snow'
},
computed:{
background_image : function () {
switch(this.backgroundImage) {
case 'snow':
return 'bg-snow';
case 'rainy':
return 'bg-rainy';
case 'sunny':
return 'bg-sunny';
default:
return 'bg-default';
}
}
},
methods: {
apiCall() {
//api call update background image according to the response
this.backgroundImage = response ; // response contains sunny text
}
},
// ...
}
</script>
<style>
.bg-sunny{
background-image: url('sunny.jpg');
}
.bg-snow{
background-image: url('snow.jpg');
}
.bg-rainy{
background-image: url('rainy.jpg');
}
.bg-default{
background-image: url('default.jpg');
}
</style>
You can achieve this behavior by looking up the image in an object, where you have defined your key and the corresponding image value. In addition, you need to tell webpack to require that media file. That require tells webpack to treat this file as a request.
<template>
<div :style="{ backgroundImage: `url(${weatherTypes[getWeatherType()]})` }"></div>
</template>
<script>
export default {
// ...
data() {
return {
weatherTypes: {
snow: require('some/path/snow.png'),
sun: require('some/path/sun.png'),
// ...
}
}
},
methods: {
getWeatherType() { return 'snow'; },
},
// ...
}
</script>
Reproduction link

Angular Material Dialog panelClass Wont Update

I'm working with an Angular Material Dialog Box and I'm trying to make the background a custom color.
This question has been asked quite a few times and I've tried to apply the answer but it doesn't seem to work. Specifically, it doesn't appear that the panelClass of the dialog container is updating. Below is the component opening the dialog, the _theming.scss file, and the HTML element
import { Component, OnInit} from '#angular/core';
import { AuthService } from 'src/app/AuthenticationPackage/core/auth.service'
import { MatDialog, MatDialogConfig } from '#angular/material';
import { FactiondialogComponent } from './factiondialog/factiondialog.component';
#Component({
selector: 'app-factions2',
templateUrl: './factions2.component.html',
styleUrls: ['./factions2.component.scss']
})
export class Factions2Component implements OnInit {
constructor( public authService: AuthService,
public dialog: MatDialog ) { }
ngOnInit(){ }
openDialog(faction): void{
const dialogConfig = new MatDialogConfig()
dialogConfig.disableClose = true;
dialogConfig.autoFocus = true;
dialogConfig.data = {faction};
dialogConfig.panelClass = ".faction-dialog";
const dialogRef = this.dialog.open(FactiondialogComponent, dialogConfig)
dialogRef.afterClosed().subscribe(result => {
console.log("dialog closed");
});
}
}
The _theming.scss section:
#mixin mat-dialog-theme($theme) {
$background: map-get($theme, background);
$foreground: map-get($theme, foreground);
.faction-dialog{
background-color:rgb(28, 31, 32)
}
.mat-dialog-container {
#include _mat-theme-elevation(24, $theme);
background: mat-color($background, dialog);
color: mat-color($foreground, text);
}
}
#mixin mat-dialog-typography($config) {
.mat-dialog-title {
#include mat-typography-level-to-styles($config, title);
}
}
This is the markup generated and but it does not include my custom class.
<mat-dialog-container aria-modal="true" class="mat-dialog-container ng-tns-c12-2 ng-trigger ng-trigger-dialogContainer ng-star-inserted" tabindex="-1" id="mat-dialog-0" role="dialog" style="transform: none;"><!--bindings={
"ng-reflect-portal": ""
}--><app-factiondialog _nghost-lda-c13="" class="ng-star-inserted"><div _ngcontent-lda-c13="" class="dialogCard"><h2 _ngcontent-lda-c13="" class="mat-dialog-title">The Harpers</h2><mat-dialog-content _ngcontent-lda-c13="" class="mat-typography mat-dialog-content"><p _ngcontent-lda-c13="">
SOME CONTENT THAT DOESNT MATTER TO THE EXAMPLE
</p></mat-dialog-content><mat-dialog-actions _ngcontent-lda-c13="" align="end" class="mat-dialog-actions"><button _ngcontent-lda-c13="" mat-button="" mat-dialog-close="" class="mat-button mat-button-base" ng-reflect-dialog-result="" type="button"><span class="mat-button-wrapper">Close</span><div class="mat-button-ripple mat-ripple" matripple="" ng-reflect-centered="false" ng-reflect-disabled="false" ng-reflect-trigger="[object HTMLButtonElement]"></div><div class="mat-button-focus-overlay"></div></button></mat-dialog-actions></div></app-factiondialog></mat-dialog-container>
I believe the upper section should say:
<mat-dialog-container aria-modal="true" class="mat-dialog-container faction-dialog ng-tns-c12-2 ng-trigger ng-trigger-dialogContainer ng-star-inserted" tabindex="-1" id="mat-dialog-0" role="dialog" style="transform: none;">
but I'm not sure since I haven't gotten this to work. I've followed the documentation:
https://material.angular.io/components/dialog/api#MatDialogConfig
I'm not sure if there's something I need to add in my app module or somewhere else.
Per the request of Mr. Khan:
faction2.component.ts:
openDialog(faction): void{
const dialogRef = this.dialog.open(FactiondialogComponent, {panelClass: 'faction-dialog'})
dialogRef.afterClosed().subscribe(result => {
console.log("dialog closed");
});
}
Screen with modal open:
HTML Inspect Element
<mat-dialog-container aria-modal="true" class="mat-dialog-container ng-tns-c12-4 ng-trigger ng-trigger-dialogContainer ng-star-inserted" tabindex="-1" id="mat-dialog-2" role="dialog" style="transform: none;"><!--bindings={
"ng-reflect-portal": ""
}--><app-factiondialog _nghost-yis-c13="" class="ng-star-inserted"><div _ngcontent-yis-c13="" class="dialogCard"><h2 _ngcontent-yis-c13="" class="mat-dialog-title"></h2><mat-dialog-content _ngcontent-yis-c13="" class="mat-typography mat-dialog-content"><p _ngcontent-yis-c13=""></p></mat-dialog-content><mat-dialog-actions _ngcontent-yis-c13="" align="end" class="mat-dialog-actions"><button _ngcontent-yis-c13="" mat-button="" mat-dialog-close="" class="mat-button mat-button-base" ng-reflect-dialog-result=""><span class="mat-button-wrapper">Close</span><div class="mat-button-ripple mat-ripple" matripple=""></div><div class="mat-button-focus-overlay"></div></button></mat-dialog-actions></div></app-factiondialog></mat-dialog-container>
If you want to add your own custom class to style the material modal, then firstly passes your custom class to the panelClass key in your modal this way:
this.dialogRef = this.dialog.open(AddCustomComponent,{
panelClass: 'custom-dialog-container', //======> pass your class name
});
this.dialogRef.afterClosed();
Once that's done, all you gotta do is style your modal by using your class and other models won't be affected. For example, you can remove the padding and margin this way.
/*Material Dialog Custom Css*/
.custom-dialog-container .mat-dialog-container{
padding: 0;
}
.custom-dialog-container .mat-dialog-container .mat-dialog-content{
margin: 0;
}
/*---------------------------*/
The panelClass gets added to the parent of the dialog container, so the space is what I was missing:
.custom-dialog-container <<space>> .mat-dialog-container{
padding: 0;
}
Also use ng-deep or put the style in the root stylesheet, not the component
Great answer above, but I wanted to just extend it by saying that the MatDialogConfig can also be passed via some config object.
i.e. I passed in custom class SingleViewDialog for my scenario, where the my-custom-maximize padding override was added to our mat-dialog.scss override file:
export interface SingleViewDialog {
examUID: string;
examData: TrendOctExam;
layoutMode: Layout;
}
public launchSingleView(examUID: string, examData: TrendOctExam) {
const config: MatDialogConfig<SingleViewDialog> = {};
config.hasBackdrop = true;
config.disableClose = false;
config.panelClass = 'my-custom-maximize';
const mode: Layout = 'volume2d';
config.data = { examUID, examData, layoutMode: mode};
const dialogRef = this.matDialog.open(OCTThreeDSingleComponent, config ); //*** INJECT HERE ***
dialogRef.afterClosed().subscribe(_ => this.viewStateService.updateLayoutMode('glaucoma'));
}

How to have sub components CSS class react on parent components CSS class?

I am trying to get my head around a scenario with CSS components:
I have a react component that uses its own classes. This component has a little helper subcomponent that also has its own classes. Now: When a specific state in the main component is set and a specific class is applied then the helper component's css should react on that class.
For instance:
Component A uses Component B to show something.
Component A gets clicked on and react sets a "clicked"-class on that component
Component B should then visually react on that class
In plain CSS (or similar) I would do this:
Component A:
.component {
height: 10px;
}
.component.clicked {
height: 5px;
}
Component B
.clicked {
.subComponent {
background-color: orange;
}
}
I know that there is a react way to do this. This kind of thing should be done with states and props which are being passed between the components so that this kind of situation gets avoided altogether. But I am currently refacturing a project that still has these issues and I don't really get how to do this properly with react-css-modules.
By the way: My current workaround uses :global but I'd really, really like to avoid this...
Component B:
.clicked:onclick, .subComponent {
// code ...
}
This should do it.
If not I'm just bad at css, or confused about your question.
Parent:
var ComponentA = React.createClass({
getInitialState: function() {
return {
isClicked: false
}
},
onClick: function() {
this.setState({ isClicked: !this.state.isClicked });
}),
render() {
return (
<div className={this.state.isClicked ? "component clicked" : "component"}>
<ComponentB isClicked={this.state.isClicked}/>
</div>
);
}
});
Child:
var ComponentB = React.createClass({
getDefaultProps: function() {
return {
isClicked: false
}
},
render() {
return (
<div className={this.props.isClicked ? "subComponent clicked" : "subComponent"}>
I am the subComponent
</div>
);
}
});

Trouble using pseudo selector with a directive inside ng-repeat

I have a directive for a table with collapsible rows that only allows one row to be open at a time like this:
HTML:
<div class="my-table">
<div class="table-header">
... table headers ...
</div>
<my-table-row ng-repeat="itm in itms" itm="itm"></my-table-row>
</div>
JS Directive:
app.directive('myTable', function() {
return {
restrict: 'E',
scope: {
itms: '='
},
controller: 'TableController',
templateUrl: '/views/directives/my-table.html'
};
});
JS Controller:
app.controller('TableController', ['$scope', function($scope) {
$scope.rows = [];
$scope.toggleRow = function(row) {
row.open = !row.open;
};
this.addRow = function addRow(row) {
$scope.rows.push(row);
};
this.toggleOpen = function toggleOpen(selectedRow) {
angular.forEach($scope.rows, function(row) {
if (row === selectedRow) {
$scope.toggleRow(selectedRow);
} else {
row.open = false;
}
});
};
}]);
and the rows like this:
HTML:
<div class="table-row" ng-class="{ 'open': open }" ng-click="toggleOpen(this)">
... row contents code ...
</div>
JS Directive:
app.directive('myTableRow', function() {
return {
require: '^myTable',
restrict: 'E',
scope: {
itm: '='
},
link: function(scope, element, attrs, tableCtrl) {
scope.open = false;
scope.toggleOpen = tableCtrl.toggleOpen;
tableCtrl.addRow(scope);
},
templateUrl: '/views/directives/my-table-row.html'
};
});
used in template like this:
<my-table itms="itms"></my-table>
This all works, but I have a CSS pseudo element to round the corners of the final row like:
.table .table-row:last-child {
border-radius: 0 0 4px 4px;
}
However, ng-repeat is wrapping a tag around my table rows which is causing the pseudo selector to see them all as the last child. I've tried restructuring, tried using $last and making an actual class for the last row, moving things around, but I'm out of ideas. Any thoughts out there?
as I understood, css class table-row is located within myTableRow directive, which does not have replace: true property. This means that table-row css class is wrapped by my-table-row directive attribute, so, in order to get to the last row, your CSS rule should be:
.table my-table-row:last-child .table-row {
border-radius: 0 0 4px 4px;
}

Resources