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>
Related
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
I have an view in Polymer whereby users can scan a QR code and the data will appear as a heading below the video preview. This is the code.
<link rel="import" href="../bower_components/polymer/polymer-element.html">
<link rel="import" href="shared-styles.html">
<script type="text/javascript" src="../js/instascan.min.js"></script>
<dom-module id="my-view1">
<template>
<style>
:host {
display: block;
text-align: center;
}
#preview {
width: 100% !important;
height: auto !important;
border-radius: 2px;
}
</style>
<!-- Video preview of camera for QR code scanning -->
<video id="preview"></video>
<!-- List of QR code items scanned-->
<h1>{{bucketItems}}</h1>
</template>
<script>
class MyView1 extends Polymer.Element {
static get is() { return 'my-view1'; }
static get properties() {
return {
bucketItems: {
type: String,
reflectToAttribute: true
},
}
}
// Once page has loaded
connectedCallback() {
super.connectedCallback();
// List of items in bucket (contains scanned ingredients)
var itemsBucket = [];
// Scan QR Code using Instascanner
let scanner = new Instascan.Scanner({ video: this.$.preview });
scanner.addListener('scan', function (content) {
// Access the QR code content using "content"
if (!itemsBucket.includes(content)) {
// Only add items once to the bucket
itemsBucket.push(content);
}
this.bucketItems = itemsBucket.toString();
console.log(this.bucketItems);
});
Instascan.Camera.getCameras().then(function (cameras) {
if (cameras.length > 0) {
scanner.start(cameras[0]);
} else {
console.error('No cameras found.');
}
}).catch(function (e) {
console.error(e);
});
}
}
window.customElements.define(MyView1.is, MyView1);
</script>
</dom-module>
When I console.log(this.bucketItems) I can see the list of items they have scanned but the data is not visible in the h1 tag. How do I bind it correctly. I am new to polymer and just beginning to learn data bindings.
This problem is the context of your callback is not bound to the Polymer object, so it uses the outer context.
You could switch to arrow functions to automatically bind the Polymer object, use :
scanner.addListener('scan', content=> {
instead of:
scanner.addListener('scan', function (content) {
DEMO
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>
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>
);
}
});
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;
}