I am starting learning Vue.js and I got a problem with understanding Vuetify's grid system. Now I want to center that content to the center. I've read docs and tried for many ways, but positioning elements it is still random, I would say.
Can anybody explain me v-content, v-layout and v-flex?
Here's code.
<template>
<v-container>
<v-layout text-center>
<v-flex xs4 >
<v-img
:src="require('../assets/logo.svg')"
class="my-3"
contain
height="200"
>
</v-img>
<v-text-field v-model="newPrice" placeholder="Add price"></v-text-field>
<v-btn #click="addPrices">Add price </v-btn>
<v-text-field v-model="newVolume" placeholder="Add volume"></v-text-field>
<v-btn #click="AddVolume">Add volume </v-btn>
</v-flex>
</v-layout>
</v-container>
</template>
<script>
export default {
name: 'MainPage',
data () {
return {
prices: [],
volumes: [],
newPrice: '',
newVolume: ''
}
},
methods: {
addPrices () {
this.prices.push(this.newPrice)
},
addVolume () {
this.volumes.push(this.newVolume)
}
}
}
</script>
So vuetify's grid system takes advantage of CSS flexbox. v-container determines the spacing around and between elements; v-layout determines the flex axis/direction/order of the elements, and v-flex determines how big the elements will be in the 12-point grid system.
To center something, try the below:
<v-layout row justify-center></v-layout>
This is equivalent to:
.layout {
display: flex;
flex-direction: row;
justify-content: center;
}
There is no prop in the v-layout component named text-center so I'm guessing that's why it's not centering.
Related
The v-switch seems to always take 100% width, as seen below. Is there a way to reduce its clickable area to the minimum? Thanks!
clickable v-switch in the blank: https://vuetifyjs.com/en/components/switches/#usage
I suggest that you read this link from vuetify documentation. the reason that v-switch is continuing to the right of page is that v-container above it. you can modify the layout with vuetify grid system like below:
a Vue js page
<template>
<div class="about">
<v-container
class="px-0"
fluid
>
<v-row>
<v-col md="4">
<v-switch
class="my-border"
v-model="switch1"
:label="`Switch 1: ${switch1.toString()}`"
></v-switch>
</v-col>
</v-row>
</v-container>
</div>
</template>
<script>
export default {
data () {
return {
switch1: true,
}
},
}
</script>
<style scoped>
.my-border {
border: 1px solid #000;
}
</style>
You can add d-inline-block class to v-switch which will reduce the div width to the content's max width.
The alternative, as the previous answer explained, is to wrap it in a row/column. Either don't set a column value or set it to a number because col-auto may cause it to be more narrow than desired and cause label to go over multiple lines.
I'm writing my first app in vuetify 2.x. The app is displaying a list of items which are objects with a text (item.t) string field and a checked (item.c) boolean field.
I would like to display checked items in the current theme color and the unchecked items in the opposite theme color (highlighted). It thus depends on the value of the item.c field value.
I assume that changing the them of the list item will kind of reverse the colors of its content. Black <-> white.
How could I do that ?
This is my list component:
<template>
<v-list dense>
<template v-for="(item, index) in items">
<v-list-item :key="item.r">
<v-list-item-content class="font-weight-medium">
<v-layout>
<v-row align="center">
<v-col cols="2">
<v-row no-gutters justify="end">
{{ item.n }}
</v-row>
</v-col>
<v-col cols="10" class="px-0">
<v-row no-gutters>{{ item.t }}</v-row>
</v-col>
</v-row>
</v-layout>
</v-list-item-content>
</v-list-item>
<v-divider
v-if="index < items.length - 1"
:key="`divider-${index}`"
></v-divider>
</template>
</v-list>
</template>
<script>
export default {
name: "itemList",
computed: {
items() {
return this.$store.getters.currentListItems;
},
},
};
</script>
I tried many things without success and couldn't find an example how to do that.
Edit: since the items contains just text and no icons, maybe it's enough the change the background and text color. The nice thing of theme is that it also reverse icons.
You can do it by different ways by v-menu by v-select or v-combo-box
and here you can use option multiple
https://vuetifyjs.com/en/components/combobox/#dense
but i think you need to use combo-box
and here you have slots
freedom of thought
I finally solved it. It's a bit hacky but it does the job.
The solution is to use a style computed with a method receiving the item object as argument. The hacky part is the way I change the style.
The highlighted text and background colors swap with the dark or light setting. Switching the them switches all the colors.
<template>
<v-list dense>
<template v-for="(item, index) in items">
<v-list-item :key="item.r" v-bind:style="highlighted(item)">
<v-list-item-content class="font-weight-medium">
<v-layout>
<v-row align="center">
<v-col cols="2">
<v-row no-gutters justify="end">
{{ item.n }}
</v-row>
</v-col>
<v-col cols="9" class="px-0">
<v-row no-gutters>{{ item.t }}</v-row>
</v-col>
</v-row>
</v-layout>
</v-list-item-content>
</v-list-item>
<v-divider
v-if="index < items.length - 1"
:key="`divider-${index}`"
></v-divider>
</template>
</v-list>
</template>
<script>
export default {
name: "itemList",
computed: {
items() {
return this.$store.getters.currentListItems;
},
},
methods: {
highlighted(item) {
let backColor = "white";
let textColor = "#1E1E1E";
if (
(this.$vuetify.theme.dark && item.c) ||
(!this.$vuetify.theme.dark && !item.c)
) {
[textColor, backColor] = [backColor, textColor];
}
return {
"background-color": backColor,
color: textColor,
};
},
},
};
</script>
I'm having a variety of CSS issues with Vuetify that I'm hoping someone can help me resolve. I'm using a split panel view (vue-split-panel) with Vuetify, but Vuetify doesn't seem to consistently recognize when to trigger the full-column width, as shown below. I'm able to "trigger" the full column width (for the same split panel width) by just opening and then closing the Chrome js console.
I put this into a codesandbox so that it's reproducible. In doing so, I see a new issue that the radio buttons aren't showing.
https://codesandbox.io/s/split-view-test-7mlx1
If you're able to show me how to tweak the sandbox to make the responsivity work I'd so appreciate it!
Supposed to be a radio button:
Also, an issue that I can't reproduce in the codesandbox but I'm experiencing in my app (it's a JupyterLab extension) is shown in the bottom screenshot: the select label has the border line going through it. I tried to find if there is a CSS conflict somewhere but didn't know exactly where to look.
Furthermore I also have an issue that the select menu is offset proportional to the left menu, for some reason... why does opening the left and top menus effect the position? How can I fix it? I've tried using the "attach" property and adding an id to the element itself, or creating a parent div, but neither seems to solve it.
This is ~slightly reproducible in the sandbox by making the split panel wide and clicking the multi-select, then making it narrower and clicking again. You'll see that the menu is offset when it opens.
Solutions that don't involve iFrames would be preferred, and yes, I do have my app wrapped with <v-app>, however since it's a JupyterLab extension I only have access to the main tab space (not the left or top menus) so the v-app is wrapped around the HTML element which is the main tab area, not the full screen.
I think there might be a bug in the Vuetify code somewhere around this function: https://github.com/vuetifyjs/vuetify/blob/054555a42e2ef368df2d6e168d1eec7fc06fb12c/packages/vuetify/src/components/VSelect/VSelect.ts#L456
I have resolved all the CSS issues
Refactotred the grid layout by adding the proper components and breakdowns in UI. Added a fix to radio buttons. Added the css dependencies, material icons dependencies, fonts which vuetify uses internally
Check for the working codepen here: https://codesandbox.io/s/split-view-test-47f2h
<template>
<div id="app">
<v-app>
<Split style="height: 500px;">
<SplitArea :size="25">panel left</SplitArea>
<SplitArea :size="75">
<v-container fluid grid-list-md>
<v-layout row wrap>
<v-flex class="d-flex" xs="12" sm="12" md="6" lg="4">
<v-text-field
v-model="params.c.selected"
label="C"
hint="Penalty parameter C of the error term."
persistent-hint
return-object
type="number"
outlined
></v-text-field>
</v-flex>
<v-flex class="d-flex" sm="12" md="12" lg="6">
<v-select
v-model="params.kernel.selected"
hint="Specifies the kernel type to be used in the algorithm. It must be one of ‘linear’, ‘poly’, ‘rbf’, ‘sigmoid’, ‘precomputed’ or a callable. If none is given, ‘rbf’ will be used. If a callable is given it is used to pre-compute the kernel matrix from data matrices; that matrix should be an array of shape (n_samples, n_samples)."
:items="params.kernel.items"
label="Kernel"
persistent-hint
return-object
outlined
></v-select>
</v-flex>
<v-flex class="d-flex" sm="12" md="12" lg="6">
<v-text-field
v-model="params.degree.selected"
label="Degree"
hint="Degree of the polynomial kernel function ('poly'). Ignored by all other kernels."
persistent-hint
return-object
type="number"
outlined
></v-text-field>
</v-flex>
<v-flex class="d-flex" sm="12" md="12" lg="6">
<v-text-field
v-model="params.coef0.selected"
label="Coef0"
hint="Independent term in kernel function. It is only significant in 'poly' and 'sigmoid'."
persistent-hint
type="number"
outlined
></v-text-field>
</v-flex>
<v-flex class="d-flex" sm="12" md="12" lg="6">
<v-radio-group
v-model="params.probability.selected"
hint="Independent term in kernel function. It is only significant in 'poly' and 'sigmoid'."
persistent-hint
>
<template v-slot:label>
<div style="font-size: 12px">
Probability:
boolean, optional (default=False)
</div>
<br>
</template>
<v-radio label="True" :value="true" color="black"></v-radio>
<v-radio label="False" :value="false" color="black"></v-radio>
</v-radio-group>
</v-flex>
</v-layout>
</v-container>
</SplitArea>
</Split>
</v-app>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
params: {
c: { default: 1, selected: 1 },
kernel: {
default: "rbf",
selected: "rbf",
items: ["linear", "poly", "rbf", "sigmoid", "precomputed"]
},
degree: { default: 3, selected: 3 },
coef0: { defaul: 0.0, selected: 0.0 },
probability: { default: true, selected: true }
}
};
}
};
</script>
<style>
</style>
I am trying to use vueDraggable with vuetify, to create a photo gallery which allowed to rearrange the order.
Without Draggable, the v-img is loading the image correctly.
While after I added the draggable, the images is not going to be loaded.
To test the images path is correct, I added a below the and the are loaded properly.
https://codepen.io/brian661/pen/zVygap/
<template xmlns:v-slot="http://www.w3.org/1999/XSL/Transform">
<v-layout>
<v-flex xs12>
<v-card>
<v-container grid-list-sm fluid>
<v-layout row wrap>
<draggable
class="draggableArea"
:list="photos"
:group="{ name: 'photo', pull: 'clone', put: false }"
:sort= false
>
<v-flex
v-for="photo in photos"
:key=photo.name
xs3
d-flex
>
{{photo.name}}
<v-card flat tile class="d-flex">
// this is not able to be loaded
<v-img
:contain="true"
:src=photo.img
:lazy-src=photo.img
class="grey lighten-2"
>
// this is loaded without problem
<img :src=photo.img class="Photo"/>
<template v-slot:placeholder>
<v-layout
fill-height
align-center
justify-center
ma-0
>
<v-progress-circular indeterminate color="grey lighten-5"></v-progress-circular>
</v-layout>
</template>
</v-img>
</v-card>
</v-flex>
</draggable>
</v-layout>
</v-container>
</v-card>
</v-flex>
</v-layout>
</template>
<script>
import draggable from 'vuedraggable';
export default {
name: "AvailablePhoto",
components: {
draggable,
},
data() {
return {
photos : [ {name: "photo1", img: "https://somewhere/1.png"},
{name: "photo2", img: "https://somewhere/2.png"},
{name: "photo3", img: "https://somewhere/3.png"},
{name: "photo3", img: "https://somewhere/4.png"}]
}
}
}
</script>
<style scoped>
.draggableArea {
display: flex;
}
.Photo {
max-width: 100%;
max-height: 100%;
}
</style>
Try
<v-img :src="require('photo.img')"></v-img>
instead of
<v-img :src="photo.img"></v-img>
Vue loader converts relative paths into require functions automatically for you. Unfortunately, this is not the case when it comes to custom components. You can circumvent this issue by using require. If you're using Vuetify as a Vue-CLI 3 plugin, you can edit your project's vue.config.js file by modifying the options for vue-loader.
// Incorrect
<v-img src="../path/to/img" />
// Correct
<v-img :src="require('../path/to/img')" />
Source: Vuetify
Hope it helps.
I have a main component that is used to display items using a loop:
<v-list-tile v-for="(item, index) in items" :key="item.title">
...
<report type="item.type"> </report>
</v-list>
The report component is used to report abuse on the system, and report type may vary depending on the item from the parent loop.
As users are very unlikely to use report on a regular basis I would like to only load v-select elements when the Dialog (modal) is opened.
Using created or mounted triggers the loading method everytime the report component is generated and not when the report component is opened.
Is there a smart way to prevent this and only have the loading method within report being triggered only when the component is opened.
=== Report.vue file ===
=== This file is loaded in the parent component
<template lang="html">
<v-dialog v-model="dialog" persistent max-width="800px" lazy>
<v-btn icon slot="activator">
<v-icon>error_outline</v-icon>
</v-btn>
<v-card>
<v-card-title>
<div class="headline"><v-icon large>error_outline</v-icon> Reporting</div>
</v-card-title>
<v-card-text>You are about to report the following {{ reportType }}: "<i>{{ reportContent.title }}</i>"
<v-container v-if="this.$store.getters['report/getLoadedState']" grid-list-md >
<v-layout wrap>
<v-flex xs12 sm12>
<v-select
label="Select a reason"
required
cache-items
:items="items"
item-text="title"
item-value="id"
></v-select>
</v-flex>
<v-flex xs12 sm12>
<v-text-field
label="Please provide additional information here"
multi-line></v-text-field>
</v-flex>
</v-layout>
</v-container>
<v-container v-else grid-list-md>
<v-layout>
<v-flex xs12 sm12>
Loading
</v-flex>
</v-layout>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="green darken-1" flat="flat" #click.native="cancel">Cancel</v-btn>
<v-btn color="green darken-1" flat="flat" #click.native="report">Report</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
export default {
name: 'report',
data () {
return {
dialog: false,
items: this.$store.getters['report/getItems']
}
},
props: ['reportType', 'reportContent'],
methods: {
cancel () {
this.dialog = false
},
report () {
this.dialog = false
},
loadReasons (type) {
if (!this.$store.getters['report/getLoadedState']) {
this.$store.dispatch('report/setItems', type)
}
}
}
}
</script>
<style lang="css" scoped>
</style>
PS 1: I'm not using JQuery and do not intend to use it
PS 2: Calling the method outside of the report component is not an option as I want to maximize reusability of this compenent and only pass arguments to it using props
How I have done this in the past is to use a "dynamic component." Vue uses a special syntax for dynamic components <component v-bind:is="currentView"></component> see: Vue dynamic components
You can set the "currentView" property to null and then on a button click another event set the currentView to report. Doing it this way will allow you to use the mounted method as the component is not rendered or created until it is dynamically called.
<component :is="myComponent"></component>
<v-btn #click="loadComponent">Show Report</v-btn>
then...
data:{
myComponent: null;
},
methods: {
loadComponent: function(){
this.myComponent = 'report';
}
}
P.S. You can of course use this method to render any other components in the same place. i.e. you can set the 'currentView' to the name of any available component and it will be instantly rendered in its place.
I ended up using a watch on the boolean managing the dialog...
Make a function which loads the v-select. Don't initialize it just create it. Now whenever user clicks the report model you can initialize multiple functions using v-on:click or #click.
For example :
<div v-on:click="return function() { fn1('foo');fn2('bar'); }()"> </div>
This solution can also be found on :
Stackoverflow Link