v-for with varaible number of content blocks - vue.js

Is there a way to have variable amount of content blocks inside a v-for loop? Say one of the instances has 1 content paragraph and an other needs 3 content paragraphs? Would you have to create a nested loop inside the outer loop?
https://codepen.io/mDDDD/pen/dyWRqJW
<div id='timeline' class="timeline container">
<div class="timeline__main">
<div
class="timeline__main--inner"
v-for="year in timelines"
:key="year.id"
ref="timelines"
>
<div class="timeline-title">
<h3 class="title-3">{{ year.year }}</h3>
</div>
<div class="timeline-content">
**(one of the instances this needed 3 P tags with class history content?)**
<p class="history-description">
{{ year.content }}
</p>
const Timeline = {
data() {
return {
timelines: [
{ id: 2020, year: '2020', content: `Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. `},
{ id: 2019, year: '2019', content: `Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. ` },
],
};
},
};
Vue.createApp(Timeline).mount("#timeline");
</div>
</div>
</div>
</div>

Two adjustments: (1) to the data, representing N paragraphs per year, and (2) the important one in the markup, nesting a v-for to iterate the nested array.
var app = new Vue({
el: '#app',
data() {
return {
timelines: [{
id: 2020,
year: '2020',
content: [`The first paragraph in 2020`, `Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. `]
},
{
id: 2019,
year: '2019',
content: [`The first paragraph in 2019`, `Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. `]
},
],
};
},
})
.timeline__main--inner {
display: flex;
margin-bottom: 75px;
}
.timeline__main {
width: 100%;
max-width: 785px;
margin: 0 auto;
}
.timeline-title {
display: flex;
align-items: center;
padding-right: 65px;
border-right: 1px solid grey;
h3 {
color: green;
font-size: 60px;
}
}
.timeline-content {
padding: 15px 54px 15px 62px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div id='timeline' class="timeline container">
<div class="timeline__main">
<div class="timeline__main--inner" v-for="year in timelines" :key="year.id" ref="timelines">
<div class="timeline-title">
<h3 class="title-3">{{ year.year }}</h3>
</div>
<div class="timeline-content">
<p class="history-description" v-for="(p,i) in year.content" :key="i">
{{ p }}
</p>
</div>
</div>
</div>
</div>
</div>

Related

Put ellipsis ... on v-data-table

I have a v-data-table with 5 columns, which some have predefined widths, and some don't.
I'd like only one line of text maximum for each item and display "..." when the text exceeds 1 line.
<v-data-table
dark
:footer-props="{ 'items-per-page-options': [10, 25, -1] }"
dense
calculate-widths
fixed-header
height="498"
:headers="headers"
hide-default-header
:items="res"
sort-by="publicationDate"
:sortDesc="sortVal"
>
<template v-slot:header="{ props: { headers } }">
<thead>
<tr>
<th v-for="h in headers" :class="h.class"
:key="h.text">
<span>{{h.text}}</span>
</th>
</tr>
</thead>
</template>
<template #item.video="{ item }">
<a
target="_blank"
v-if="item.video != ''"
class="links1 video-icon"
:href="item.video"
>
<v-btn dark icon>
<v-icon class="ic1">mdi-movie</v-icon>
</v-btn>
</a>
</template>
<template #item.title2="{ item }">
<a
target="_blank"
v-if="item.file != ''"
class="links1"
:href="item.file"
>
<span style="color:white"> {{ item.title }} </span>
</a>
</template>
</v-data-table>
I looked through the CSS properties to do so and I found things like :
text-overflow: ellipsis;
white-space: nowrap;
But it doesn't add the ... at the end of the lines and it also takes place beyond the widths applied to each columns so not all 5 columns are displayed on my panel.
Here's the headers in the data section
headers: [
{ text: "Source", value: "dataSource", width: "120px", class:"source-field"},
{ text: "News", value: "title2", width: "214px", class:"news-field"},
{ text: "Actors", value: "concernedActors", width: "242px" },
{ text: "Video", value: "video", width: "58px" },
{ text: "Publication", value: "publicationDate", width: "80px" },
],
And I tried this css but it won't work, the column news just goes bigger so that the entire text fits on 1 line.
th.news-field{
max-width: 100px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
I would also love to make the hover darker on each line in the table
Here's a solution:
<template #item.foo="{ item }">
<div class="ellipsis-wrapper">{{ item.foo }}</div>
</template>
#your-table td {
position: relative;
}
#your-table td .ellipsis-wrapper {
position: absolute;
max-width: calc(100% - 1rem);
line-height: calc(3rem - 1px); /* change this if needed */
top: 0;
left: 1rem;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
But it creates another problem (easier to solve, IMHO): it takes the cell content out of the flow, therefore the table no longer calculates column width based on cells content (from the table's perspective, that column has no content) - so it relies on its header content (or on the provided width) to calculate the column width.
However, I believe making an "empty" column have a particular width (or a proportion from the table's width) is an easier to solve problem than your current one.
Demo: https://codepen.io/andrei-gheorghiu/pen/wvjoQXR

VueJS generating random "whitespace" in list loop

I have this weird problem where my v-if loop generates a random whitespace in the DOM. I can also visually see it (see image). You see the whitespace to the left of the upload link. The code used to generate the links are pretty simple, but I cannot find out where the whitespace is coming from. Any ideas? Or am I doing something wrong? I tried to reset the cache also to see if that fixed it, but nope.
<template>
<nav class="navbar">
<ul class="nav">
<li class="nav-item nav-brand">
<router-link class="brand nav-link" :to="{ name: 'home' }">{{ this.$appName }}</router-link>
</li>
<template v-for="(link, index) in links">
<li class="nav-item" :key="index">
<router-link class="nav-link" :to="{ name: link.name }">{{ link.title }}</router-link>
</li>
</template>
</ul>
</nav>
</template>
And here is how I use the template:
<AppNavigation :links="[
{ name: 'upload', title: 'Upload' },
{ name: 'login', title: 'Login' }
]" />
<style lang="scss">
// Import variables and mixins
#use '../../scss/vars';
#use '../../scss/mixins';
.navbar {
height:vars.$headerHeight;
.nav {
margin:0;
padding:0;
list-style:none;
height:inherit;
}
.nav-brand {
.brand {
background:-moz-linear-gradient(-35deg, #33b2d9 0%, #e368f5 100%);
background-size:100% 100%;
transition:background-size .25s ease-in-out!important;
&:hover {
background:-moz-linear-gradient(-35deg, #33b2d9 0%, #e368f5 100%)!important;
background-size:100% 200%!important;
}
}
}
.nav-item {
display:inline-flex;
height:inherit;
.nav-link {
height:100%;
display:flex;
align-items:center;
padding:0 20px;
color:vars.$navLinkColor;
font-size:vars.$navLinkSize;
transition:background .05s ease-in-out;
&:hover {
background:lighten(vars.$navItemBg, 5%);
}
}
}
}
</style>
I reproduced the problem by having a predefined <li> element. Then following that, a v-for on a <li> element. Like this:
<li ...>Test</li>
<li v-for="" ...></li>
The whitespace gets added above the v-for.
Solved it by just adding the brand to the list of links to be rendered, and removing the predefined brand element.

How can we show multiple items with Bootstrap-Vue Carousel?

I think boostrap-vue carousel not so detailed. For this reason i can't reach to nice solution.
I wanna just show 3 items (like in image) in my app and i didnt find the solution and i searched other package and there was no solution for me.
All i want to do like this;
data() {
return {
slide: 0,
sliding: null
};
},
methods: {
onSlideStart(slide) {
this.sliding = true;
},
onSlideEnd(slide) {
this.sliding = false;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div>
<b-carousel id="carousel-1" v-model="slide" :interval="0" controls indicators background="white" img-width="650" img-height="480" #sliding-start="onSlideStart" #sliding-end="onSlideEnd">
<b-carousel-slide caption="First slide" text="Nulla vitae elit libero, a pharetra augue mollis interdum." img-src="https://picsum.photos/1024/480/?image=52"></b-carousel-slide>
<b-carousel-slide img-src="https://picsum.photos/1024/480/?image=54">
<h1>Hello world!</h1>
</b-carousel-slide>
<b-carousel-slide img-src="https://picsum.photos/1024/480/?image=58"></b-carousel-slide>
<b-carousel-slide>
<img slot="img" class="d-block img-fluid w-100" width="1024" height="480" src="https://picsum.photos/1024/480/?image=55" alt="image slot">
</b-carousel-slide>
</b-carousel>
<p class="mt-4">
Slide #: {{ slide }}<br> Sliding: {{ sliding }}
</p>
</div>
If you have any other library suggestion i would appreciate.
Thanks for help.
BootstrapVue <b-carousel> is designed to only show a single slide at a time (as is the standard bootstrap V4.x carousel component). There are never more than 2 slides visible at once (during the slide or fade transition, all other slides are hidden. CSS transforms are used to make the appearance of the slides moving)
You would need to either find a 3rd party component that supports multiple slides showing, or generate your own custom component.
I'm not familiar with this specific component, but this is from its documentation:
<!-- Slide with blank fluid image to maintain slide aspect ratio -->
<b-carousel-slide caption="Blank Image" img-blank img-alt="Blank image">
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse eros felis, tincidunt
a tincidunt eget, convallis vel est. Ut pellentesque ut lacus vel interdum.
</p>
</b-carousel-slide>
I would try using a blank image as the default and inserting whatever other images/content you want as children of the slide:
<!-- Slide with blank fluid image to maintain slide aspect ratio -->
<b-carousel-slide caption="Blank Image" img-blank img-alt="Blank image">
<img class="my-img" src="img1.jpg"/>
<img class="my-img" src="img2.jpg"/>
<img class="my-img" src="img3.jpg"/>
</b-carousel-slide>
And use absolute positioning or flexbox to display them the way you want.
I did the same thing using b-card-group. Because b-carousel does not support showing multiple items.
In template area
<div>
<!-- carousel area -->
<b-card-group deck class="mb-0">
<b-card v-for="(item,index) in currentPageCards" :key="index" class="mr-0">
<!-- card content -->
</b-card>
</b-card-group>
<!-- pagination area -->
<div class="pagination" v-if="cards.length>cardsPerPage">
<div class="index" v-for="i in pageCount" :key="i" #click="next(i)" :class={active:currentPage(i)}></div>
</div>
</div>
In script area
data() {
return {
cards: [{
//Data in the card as objects
},{},{},{},{}],
paginatedCards:[],
pageCount:0,
cardsPerPage:4,
currentPageIndex:0
}
},
computed: {
currentPageCards(){
this.createPages();
return this.paginatedCards[this.currentPageIndex];
}
},
methods:{
currentPage(i){
return i-1===this.currentPageIndex;
},
createPages() {
let cardsLength = this.cards.length;
let fullPagesCount = Math.floor(cardsLength/this.cardsPerPage);
if(cardsLength>this.cardsPerPage) {
this.pageCount = 0;
for (let i = 0; i < fullPagesCount * this.cardsPerPage; i += this.cardsPerPage) {
this.paginatedCards[this.pageCount] = this.cards.slice(i,i + this.cardsPerPage);
this.pageCount++;
}
this.paginatedCards[this.pageCount] = this.cards.slice(cardsLength-this.cardsPerPage,cardsLength);
this.pageCount = this.pageCount+1;
} else {
this.paginatedCards[0] = this.cards;
}
},
next(i){
this.currentPageIndex=i-1;
}
}
In style area
.pagination{
display:flex;
align-items: center;
justify-content: center;
padding:10px;
}
.index{
margin-left:10px;
width:10px;
height:10px;
background:#000000
}
.active{
width:15px;
height:15px;
}
It shows like as below
Try this one. Thank you!

Vue JS transition toggle based of class binded - ease down and up

Using Vue JS i am trying to transition the read more/read less by dropping/closing by transitioning the max-height properties with ease.
This itemBio is sitting in a bootstrap vue modal (if this is relevant).
If the readMore data is true then the readMore class is binded to the itemBio class div. This then activates the max-height property to 100%.
Though doesn't seem to be working at all. Just instantly shows/closes the div.
this is what i have so far:
.itemBio {
max-height: 51px;
overflow: hidden;
transition: max-height 5s ease;
&.readMore {
max-height: 100%;
overflow: auto;
transition: max-height 5s ease;
&::-webkit-scrollbar {
display: none;
}
}
}
<div class="itemBio font-14 text-grey69 w-100"
:class="{'readMore':readMore}"
style="line-height: 17px;"
:ref="'countLines' + menuItem.uuid">
{{ menuItem.description }}
<button #click="$refs.allergensModal.show()"
class="mt-10 w-100 text-left"
v-if="dietaryTrue !== 0"
>
<span class="allergen green"
v-if="menuItem.dietary.vegetarian">
V
</span>
<span class="allergen aqua"
v-if="menuItem.dietary.vegan">
VG
</span>
<span class="allergen gold"
v-if="menuItem.dietary.gluten_free">
GF
</span>
<span class="allergen pink"
v-if="menuItem.dietary.halal">
HA
</span>
<span class="allergen yellow"
v-if="menuItem.dietary.soy_free">
SF
</span>
<span class="allergen brown"
v-if="menuItem.dietary.nut_free">
NF
</span>
<span class="allergen blue"
v-if="menuItem.dietary.dairy_free">
DF
</span>
</button>
</div>
<button class="text-teal font-black font-12" #click="showItemBio()" v-if="lines > 2">
<span v-if="!readMore">Read More</spa`enter code here`n>
<span v-if="readMore">Read Less</span>
</button>
Typescript:
readMore: boolean = false;
showItemBio() {
this.readMore = !this.readMore;
}
I have made one sample example. Check this once. This may help.(you mention 100% in max-height and in starting max-height you mentioned 51px thats might be the problem for not transitioning. Maintain both in px or %)
<template>
<div id="app">
<div class="paragraph" :class="{showmore:checkStatus}">
<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).
</p>
</div>
<button #click="changeData">{{ readStatus }}</button>
</div>
</template>
<script>
export default {
name: 'App',
data:function(){
return {
readStatus:'readmore',
checkStatus:false
}
},
methods:{
changeData:function(){
if(this.readStatus=='readmore'){
this.readStatus='readless';
this.checkStatus=true;
}else{
this.readStatus='readmore';
this.checkStatus=false;
}
}
}
}
</script>
<style scoped>
.paragraph {
max-height: 100px;
overflow: hidden;
transition: max-height 2s;
}
.paragraph p{
color: black;
}
.showmore{
max-height: 500px;
overflow: auto;
}
</style>

Bootstrap 3 Column Alignment

MY (COMMON) PROBLEM:
I am struggling with what I feel is an old or common problem with BootStrap 3 column alignment, but I am not sure how to articulate myself enough to find specific answers online via googling them, thus why I am posting this here.
Here is an example of my layout:
Depiction of Layout:
If you look at the first column, second row on the lower left you'll see a VERY long title, that now creates a bad alignment with the second column paragraph on the lower right and it looks unprofessional/unintentional.
WHAT I TRIED:
I've tried making a specific min-height to the h4 tags, but, of course the layout will make the layout change as the viewport changes.
I've tried specific CSS properties (like center-fix or flex???) and that only makes the element centered within the parent div, it STILL looks off and thus unprofessional.
MY CODE:
<style type="text/css">
.feature{
margin-bottom: 6%;
padding-left: 40px;
padding-right: 40px;
}
.bottom_icon_padding {
margin-bottom: 27px;
}
#other_features h4 {
text-align: center;
min-height:10%;
}
.field-item img {
padding: 0px;
}
</style>
<div class="col-md-offset-1 col-md-10">
<h4 style="color:#666;line-height: 2.3rem;">Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</h4>
<hr>
</div>
<div class="col-sm-12" id="other_features">
<div class="row">
<div class="col-md-offset-1 col-md-5 feature" ><img alt="Unmeeting Logo" src="/sites/default/files/unmeeting_logo.png" />
<h4 class="bottom_icon_padding" >Unmeeting RFAs</h4>
<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s.</p>
<p>Learn more</p>
</div>
<div class="col-md-5 feature" ><img alt="Synergy Logo" src="/sites/default/files/synergy_logo.png" />
<h4 class="bottom_icon_padding" >Synergy RFAs</h4>
<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s</p>
<p>Learn more</p>
</div>
</div>
<div class="row" >
<div class="col-md-offset-1 col-md-5 feature" ><img alt="Certificate Variant With Image" src="/sites/default/files/030-certificate-variant-with-image.png" />
<h4 class="bottom_icon_padding" >CTSA Program Collaborative Innovation Awards: Administrative Supplements</h4>
<p>These supplements allow investigators from two or more CTSA Program hubs to form collaborations to implement, assess, and/or disseminate discoveries in methods, approaches, education and training in clinical and translational science.</p>
<p>View site</p>
</div>
<div class="col-md-5 feature" ><img alt="Premium Badge" src="/sites/default/files/035-premium-badge.png" />
<h4 class="bottom_icon_padding" >SmartIRB</h4>
<p>SMART IRB is a platform designed to ease common challenges associated with initiating multisite research and to provide a roadmap for institutions to implement the NIH Single IRB Review policy.</p>
<p>View site</p>
</div>
</div>
</div>
MY QUESTION:
This is for a client that has ALOT of restrictions on layout so I can't just move anything around, can anyone make any suggestions so the bottom two paragraph sections align no matter what the viewport size is?
There could be one solution to set min-height:40px if you are sure of the content in your website.
Another solution could be that you modify your structure in a way that your Images come in one row and your describing text go to next row. Can post details if it suits you
.feature {
margin-bottom: 6%;
padding-left: 40px;
padding-right: 40px;
}
.bottom_icon_padding {
margin-bottom: 27px;
}
#other_features h4 {
text-align: center;
min-height: 40px;
}
}
.field-item img {
padding: 0px;
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<div class="container">
<div class="col-md-offset-1 col-md-10">
<h4 style="color:#666;line-height: 2.3rem;">Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It
has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop
publishing software like Aldus PageMaker including versions of Lorem Ipsum.</h4>
<hr>
</div>
<div class="col-sm-12" id="other_features">
<div class="row">
<div class="col-md-offset-1 col-md-5 feature"><img alt="Unmeeting Logo" src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSqrfVof7CYYRtsdCu1VD4AWoPB2gs25PP5hQAuhOwhZngrOhsS" />
<h4 class="bottom_icon_padding">Unmeeting RFAs</h4>
<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s.</p>
<p>Learn more</p>
</div>
<div class="col-md-5 feature"><img alt="Synergy Logo" src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSqrfVof7CYYRtsdCu1VD4AWoPB2gs25PP5hQAuhOwhZngrOhsS" />
<h4 class="bottom_icon_padding">Synergy RFAs</h4>
<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s</p>
<p>Learn more</p>
</div>
</div>
<div class="row">
<div class="col-md-offset-1 col-md-5 feature"><img alt="Certificate Variant With Image" src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSqrfVof7CYYRtsdCu1VD4AWoPB2gs25PP5hQAuhOwhZngrOhsS" />
<h4 class="bottom_icon_padding">CTSA Program Collaborative Innovation Awards: Administrative Supplements</h4>
<p>These supplements allow investigators from two or more CTSA Program hubs to form collaborations to implement, assess, and/or disseminate discoveries in methods, approaches, education and training in clinical and translational science.</p>
<p>View site</p>
</div>
<div class="col-md-5 feature"><img alt="Premium Badge" src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSqrfVof7CYYRtsdCu1VD4AWoPB2gs25PP5hQAuhOwhZngrOhsS" />
<h4 class="bottom_icon_padding">SmartIRB</h4>
<p>SMART IRB is a platform designed to ease common challenges associated with initiating multisite research and to provide a roadmap for institutions to implement the NIH Single IRB Review policy.</p>
<p>View site</p>
</div>
</div>
</div>
</div>