How to display items in a CSS grid with Vue.js - css

I have a list of cars as below:
<script>
const sampleCars = [
{
id: 1,
name: 'Cressida',
model: 'XXC',
manufacturer: 'Toyota',
price: '$10,000',
inEditMode: false
},
{
id: 2,
name: 'Corolla',
model: 'ZD-2',
manufacturer: 'Toyota',
price: '$12,000',
inEditMode: false
},
{
id: 3,
name: 'Condor',
model: '27-9',
manufacturer: 'Mazda',
price: '$8,000',
inEditMode: false
}
]
export default {
data() {
return {
cars: sampleCars
}
}
}
</script>
<style>
.grid {
display: grid;
grid-template-columns: repeat(5, auto);
gap: 10px;
}
</style>
I want to display the items in a css grid with 5 columns.
If I use vue code like below:
<template>
<div >
<div class="grid">
<div>Name</div>
<div>Model</div>
<div>Manufacturer</div>
<div>Price</div>
<div>buttons</div>
</div>
<div v-for="car in cars" :key="car.id" class="grid">
<div>{{car.name}}</div> // "child div"
<div>{{car.model}}</div>
<div>{{car.manufacturer}}</div>
<div>{{car.price}}</div>
<div>buttons</div>
</div>
</div>
</template>
The problem with this code is that each item is displayed in its own grid. (And therefore not aligned as in image below). Using v-for all car properties become a child of the root div. So essentially I want all "child divs" to be a root of one CSS grid div. How can I achieve that?
With VueJS 2

Make the table header in one grid and table rows in another grid :
Vue.config.devtools = false;
Vue.config.productionTip = false;
const sampleCars = [{
id: 1,
name: 'Cressida',
model: 'XXC',
manufacturer: 'Toyota',
price: '$10,000',
inEditMode: false
},
{
id: 2,
name: 'Corolla',
model: 'ZD-2',
manufacturer: 'Toyota',
price: '$12,000',
inEditMode: false
},
{
id: 3,
name: 'Condor',
model: '27-9',
manufacturer: 'Mazda',
price: '$8,000',
inEditMode: false
}
]
new Vue({
el: '#app',
data() {
return {
cars: sampleCars
}
}
})
.grid {
display: grid;
grid-template-columns: repeat(5, minmax(92px,1fr));
gap: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app" class="container">
<div>
<div class="grid">
<div>Name</div>
<div>Model</div>
<div>Manufacturer</div>
<div>Price</div>
<div>buttons</div>
</div>
<div v-for="car in cars" :key="car.id">
<div class="grid">
<div>{{car.name}}</div>
<div>{{car.model}}</div>
<div>{{car.manufacturer}}</div>
<div>{{car.price}}</div>
<div>buttons</div>
</div>
</div>
</div>
</div>

The best solution I found was to use Vue-Fragment from https://github.com/Thunberg087/vue-fragment
The child elements in the v-for loop will end up being at the root level:
<template>
<div >
<div class="grid">
<div>Name</div>
<div>Model</div>
<div>Manufacturer</div>
<div>Price</div>
<div>buttons</div>
<fragment v-for="car in cars" :key="car.id" >
<div>{{car.name}}</div>
<div>{{car.model}}</div>
<div>{{car.manufacturer}}</div>
<div>{{car.price}}</div>
<div>buttons</div>
</fragment>
</div>
</div>
</template>
See also this SO question: Is there something like React.Fragment in Vue?

Related

vuedraggable custom style for each individual item

I need to have each vuedraggable item to have different styling in the wrapper tag (for example based on the element's index) like so:
<div class="wrapper">
<div class="grid_item" style="grid-row: 1">
I am Item 1
</div>
<div class="grid_item" style="grid-row: 2">
I am Item 2
</div>
<div class="grid_item" style="grid-row: 3">
I am Item 3
</div>
</div>
I know this is a very simple example that doesn't really need the index but suppose a more complex scenario where it is necessary to have access to the index in the draggable component (not its child template).
Suppose the current component looks like this:
<template>
<div class="wrapper">
<draggable
:list="items"
class="grid_item"
item-key="name">
<template #item="{ element }">
I am {{ element.name }}
</template>
</draggable>
</div>
</template>
<script>
import draggable from 'vuedraggable'
export default {
components: {
draggable,
},
data() {
return {
items: [
{
name: 'Item 1'
},
{
name: 'Item 2'
},
{
name: 'Item 3'
},
],
}
},
methods: {
rowForItem(index) {
return `grid-row: ${index + 1}`;
}
},
}
</script>
<style>
.wrapper {
display: grid;
}
.grid_item {
background-color: gray;
}
</style>
How can I make use of the rowForItem method here?

Matching flex distance for heading of table and content

I have a table with heading and entries under it.
I want the heading and entries aligned with each other.
Table.jsx:
<div className="home-trending">
<h1 className="home-trending-heading"> CRYPTO TRENDING ON DEX</h1>
<div className="cex-trending-data">
<div className="dex-content-heading bold">
<p className="cex-content-heading-item">Chain</p>
<p className="cex-content-heading-item">Token Amount (out)</p>
<p className="cex-content-heading-item">Token Amount (in)</p>
<p className="cex-content-heading-item">TXN Value</p>
<p className="cex-content-heading-item">Wallet Type</p>
<p className="cex-content-heading-item">Time</p>
<p className="cex-content-heading-item">Twitter</p>
</div>
{topDEXTrades.slice(0, 10).map((trade) => {
return (
<TrendingDexRows
chain={trade.chain_name}
time={trade.trade_time}
token_out={trade.token_out}
token_in={trade.token_in}
trade_id={trade.id}
symbol_out={trade.symbol_out}
symbol_in={trade.symbol_in}
value={trade.txn_value}
hash={trade.txn_hash}
wallet={trade.wallet}
category={trade.category}
timeToGo={timeToGoSec}
href={twitterHref}
detailedView={true}
view="large"
/>
);
})}
</div>
</div>
Table.css:
.dex-content-heading {
display: flex;
justify-content: space-between;
}
Rows.jsx:
<div>
<div className="cex-trending-row">
<div className="cex-trending-row-name row-item">
<img
src={chainIcon(props.chain)}
alt="btc"
className="base-asset-img"
/>
<p className="trending-symbol">{props.chain}</p>
</div>
<p className="row-item bold">{millify(props.token_out)}</p>
<p className="row-item bold">{millify(props.token_in)}</p>
<p className="row-item bold">{millify(props.value)}</p>
<p className="row-item" style={categoryStyle(props.category)}>
{categoryName(props.category)}
</p>
<p className="row-item"> {props.timeToGo(props.time)}</p>
<div>
<a
href={props.href(
props.value,
props.token_out,
props.token_in,
props.chain,
props.symbol_out,
props.symbol_in
)}
target="_blank"
rel="noreferrer"
style={{ textDecoration: "none", paddingRight: "25px" }}
>
<img
src={Twitter}
alt="twitter"
className="graph-icon-img-dex"
style={{ marginLeft: "10px" }}
/>
</a>
</div>
</div>
<hr className="horrizontal-line" />
</div>
Row.css:
.cex-trending-row {
display: flex;
}
.row-item {
flex: 1;
}
The heading and the rows are not aligned with each other. How do I make sure they are perfectly aligned? Which flex property can be used to make sure both divs have the same amount of space between the items of the div?
Here's an example of using grid with React.
const App = () => {
const headingData = [
"Chain",
"Token",
"Title 3",
"EzPz",
];
const dataArr = [
{ chain: "curve", token: "4m", foo: "bar", lemon: "squeezy" },
{ chain: "bird", token: "5m", foo: "tar", lemon: "teasy" },
{ chain: "lard", token: "1m", foo: "guitar", lemon: "jeezy" },
{ chain: "hard", token: "20k", foo: "blar", lemon: "measly" },
{ chain: "charged", token: "ayyyy", foo: "mars", lemon: "⚰" },
];
return (
<div className="myGrid">
{
headingData.map(str => <div key={"head-" + str}>{str}</div>)
}
{
dataArr.map((o,i) => [
<div key={"data-chain-"+i}>{o.chain}</div>,
<div key={"data-token-"+i}>{o.token}</div>,
<div key={"data-foo-"+i}>{o.foo}</div>,
<div key={"data-lemon-"+i}>{o.lemon}</div>,
])
}
</div>
);
};
ReactDOM.createRoot(
document.getElementById("root")
).render(
<App />
);
.myGrid {
display: grid;
grid-template-columns: repeat(4, 1fr);
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>

css3 transtion not working with v-for in vue2

I use transtion in vue2. I added transition in css. Vue template show two box. One way use v-for and array, another way is use variable. btn2 is effective but btn1 not.
<style lang="sass">
.item
width: 120px
height: 120px
background-color: bisque
transition: margin-left 500ms
</style>
<template>
<div>
<div class="item" v-for="item in list" :key="item.index" :style="{marginLeft: item.index + 'px'}">{{ item.value }}</div>
<div class="item" :style="{marginLeft: left + 'px'}">123</div>
<button #click="addone">btn1</button>
<button #click="addtwo">btn2</button>
</div>
</template>
<script>
export default {
name: 'Heap',
data() {
return {
left: 100,
list: [
{
value: 12,
index: 10
}
]
}
},
methods: {
addone() {
this.list[0]['index']+=10
},
addtwo() {
this.left+=10
}
}
}
</script>
You are using the code :key="item.index" on your first div. Your code then updates that same index.
When a key's value changes, the component it is attached to re-renders. You are not seeing the animation occur because instead of dynamically incrementing the CSS, you are effectively just re-rendering the element with the new CSS.
The purpose of a key is to help Vue keep track of the identity of a given node in a list. It lets Vue know which nodes it can keep and patch up and which ones need to be rendered again.
You should use a static, non-changing value as a key where possible. In the following example I have added an id property to your object and used that as the key.
<style lang="sass">
.item
width: 120px
height: 120px
background-color: bisque
transition: margin-left 500ms
</style>
<template>
<div>
<div
v-for="item in list"
:key="item.id"
class="item"
:style="{marginLeft: item.index.toString() + 'px'}"
>
{{ item.value }}
</div>
<div class="item" :style="{marginLeft: left.toString() + 'px'}">123</div>
<button #click="addone">btn1</button>
<button #click="addtwo">btn2</button>
</div>
</template>
<script>
export default {
name: 'Example',
data() {
return {
left: 100,
list: [
{
id: '1',
value: 12,
index: 10,
},
],
};
},
methods: {
addone() {
this.list[0].index += 10;
},
addtwo() {
this.left += 10;
},
},
};
</script>

Why CSS styles can't be applied to elements created by Buefy?

I'm trying to change the background-color of a table row that was generated by Buefy.
I don't understand why I'm unable to change the color of the table row, but I can change its children color.
I tried both ways, setting the whole class name or just the class name of the element that I want:
.table.is-hoverable tbody tr.detail:not(.is-selected):hover {
background-color: rgb(218, 11, 11) !important;
}
.detail {
background-color: rgb(218, 11, 11) !important;
}
I can, for example change the color of an inner element:
.media {
background-color: yellow !important;
}
Full code
<template>
<section class="hero">
<div class="hero-body">
<div class="container">
<b-table
:data="downloads"
ref="table"
:loading="isLoading"
hoverable
detailed
detail-key="version"
selectable
#select="toggle"
>
<template slot-scope="props">
<b-table-column
class="is-unselectable"
cell-class="has-pointer-cursor"
field="version"
label="Version"
width="70"
>
<span class="tag is-info">{{ props.row.version }}</span>
</b-table-column>
<b-table-column
class="is-unselectable"
cell-class="has-pointer-cursor"
field="download_count"
label="Download Count"
sortable
numeric
>{{ props.row.download_count.toLocaleString() }}</b-table-column>
<b-table-column
class="is-unselectable"
cell-class="has-pointer-cursor"
field="release_date"
label="Release Date"
sortable
centered
>{{props.row.release_date ? new Date(props.row.release_date).toLocaleDateString() : "unknown"}}</b-table-column>
</template>
<template slot="empty">
<section v-if="!isLoading" class="section">
<div class="content has-text-grey has-text-centered">
<p>Looks like it was not possible to load the data.</p>
</div>
</section>
</template>
<template slot="detail" slot-scope="props">
<article class="media">
<div class="media-content">Some text.
<hr>Some other text.
</div>
</article>
</template>
<template slot="footer">
<div v-if="!isLoading" class="has-text-right">This is a footer.</div>
</template>
</b-table>
</div>
</div>
</section>
</template>
<script>
export default {
name: "HelloWorld",
data() {
return {
downloads: [],
isLoading: true
};
},
mounted() {
this.downloads = [
{ version: "1.5", download_count: 500, release_date: new Date() },
{ version: "1.4", download_count: 400, release_date: new Date() },
{ version: "1.3", download_count: 300, release_date: new Date() },
{ version: "1.2", download_count: 200, release_date: new Date() },
{ version: "1.1", download_count: 100, release_date: new Date() }
];
this.isLoading = false;
},
methods: {
toggle(row) {
this.$refs.table.toggleDetails(row);
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="scss" scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
//This is how I expect it should work.
.table.is-hoverable tbody tr.detail:not(.is-selected):hover {
background-color: rgb(218, 11, 11) !important;
}
//Applying the property to the
.detail {
background-color: rgb(218, 11, 11) !important;
}
.media {
background-color: yellow !important;
}
</style>
Reproduction link:
https://codesandbox.io/s/misty-sea-bmyvc?file=/src/components/HelloWorld.vue
Your CSS is scoped. In the end those classes are assorted with a [data-xxx] selector so they are applied only on this component.
.detail[data-xxx] won't override the lib's CSS, you need just .detail
So put this CSS code into a separated simple <style lang="scss"> and let your component's own CSS in <style lang="scss" scoped>

How to hide wrapped components in React

I need to be able to hide components that gets wrapped because it goes over the max width.
<div style={{width:100}}>
<div style={{width:50}}>
component1
</div>
<div style={{width:50}}>
component2
</div>
<div style={{width:50}}>
component3
</div>
</div>
//But I actually use map to render children
<div style={{width:100}}>
{components.map((item, index) => {
return <div style={{width:50}}>component{index + 1}</div>)
}}
</div>
as shown in the code above, the parent div is of with 100. So the last component (component3) would go over the width of the parent by 50px and will be rendered in the second line. However, I want any component that leaves the first line to be not rendered at all. How do I make sure that only component1 and component2 shows and excludes component3?
You could add up the widths of all the components in a separate variable, and render null for all components remaining after the total width of those already rendered exceeds 100.
Example
class App extends React.Component {
state = {
components: [{ width: 50 }, { width: 50 }, { width: 50 }]
};
render() {
const { components } = this.state;
let totalWidth = 0;
return (
<div style={{ width: 100 }}>
{components.map((item, index) => {
totalWidth += item.width;
if (totalWidth > 100) {
return null;
}
return (
<div key={index} style={{ width: 50 }}>
component{index + 1}
</div>
);
})}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Resources