I am using Quasar, and I would like the drawer changes into mini mode automatically whenever the browser gets smaller by users. For now, it's always opened even I narrows the browser.
It's what I tried below:
<q-drawer
v-model="drawer"
show-if-above
:mini="!drawer || miniState"
#click.capture="drawerClick"
:width="220"
:breakpoint="500"
bordered
:content-style="{ backgroundColor: '#f5f7f9' }"
>
<q-scroll-area class="fit">
<q-list padding>
<q-btn
v-if="!miniState"
flat
left
#click="miniState = !miniState"
class="logo-btn"
>
<img
src="~assets/os_logo.png"
width="144px"
height="24px"
contain
/>
</q-btn>
<q-btn v-else flat left #click="miniState = !miniState">
<img src="~assets/os_logo_no_text.png" width="24px" contain />
</q-btn>
<!-- MENU -->
<q-expansion-item
default-opened
v-for="(menu, index) in menus"
:style="index === 0 && 'margin-top: 27px'"
:icon="menu.icon"
:label="menu.title"
:key="menu.id"
:expand-icon="menu.subMenus.length === 0 ? 'none' : ''"
header-class="header-bg text-black"
expand-icon-class="text-gray"
>
<q-expansion-item
v-for="(sub, index) in menu.subMenus"
:key="index"
:label="sub.title"
expand-icon="none"
class="sub-content"
:to="{ name: sub.link }"
/>
</q-expansion-item>
</q-list>
</q-scroll-area>
</q-drawer>
And the script code below :
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
// name: 'ComponentName'
data() {
return {
drawer: false,
miniState: false,
},
computed: { // <-- I tried this one..
miniState() {
if (window.innerWidth < 600) {
return (this.miniState = true);
}
}
},
});
</script>
I have also checked this answers here : How to default navigation drawer to closed on mobile and open on desktop?
But this is not really what I am aiming for since the mode of drawer gets stuck and not changes responsively. Any suggestions?
In your code you have made several mistakes;
You are repeating your data property miniState as a computed property. The miniState property inside the data function will override the computed property; thus it will always be false
You are not returning a value from your computed property; instead you are only assigning.
Still; window properties such as innerWidth is not reactive and watchable in Vue. Please correct me if I am wrong. Thus, watching window.innerWidth will not trigger every time the window is resized.
Since you are using Quasar, you can make use of the the screen plugin which comes with Quasar. You dont have to install anything, the screen plugin is installed by default. Here is the link to the docs.
I have put below a very minimal example of the code with the functionality you required. This is not the code you have put in your post above. I extracted the drawer and the content from the Quasar documentation here
Now the drawer will automatically go into mini mode when the screen size is below 500 px; this is of course configurable.
Also; on a side note, if you are only starting with Vue and Quasar, Vue is now upgraded to Vue3 which comes with the composition api. Quasar is also being upgraded to version 2 which supports Vue3.
Follow the comments below and you will understand the code!
new Vue({
el: '#q-app',
data: function() {
return {
drawer: true,
// using a property to track when to show the mini drawer: this way is easy to maintain.
switchToMini: 500
}
},
computed: {
// use ministate as a computed property
miniState: function() {
// use the screen plugin of Quasar -> this is very handy
return this.$q.screen.width < this.switchToMini
// you can do better and compare agains Quasars default breakpoints; following code checks whether the current screen size is 'sm'. You can comapare against 'xs', 'sm', 'md', 'lg' and 'xl'
// return this.$q.screen.name === "sm"
}
}
})
<!-- quasar and vue includes -->
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/#quasar/extras/material-icons/material-icons.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/quasar/dist/quasar.min.css">
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://cdn.jsdelivr.net/npm/quasar/dist/quasar.umd.min.js"></script>
<!-- end of includes -->
<!-- app -->
<div id="q-app">
<template>
<div class="q-pa-md">
<q-layout view="hHh Lpr lff" container style="height: 300px" class="shadow-2 rounded-borders">
<q-header elevated class="bg-black">
<q-toolbar>
<q-toolbar-title>Header</q-toolbar-title>
</q-toolbar>
</q-header>
<q-drawer
v-model="drawer"
show-if-above
:mini="miniState"
:breakpoint="200"
:width="200"
bordered
class="bg-grey-3"
>
<q-scroll-area class="fit">
<q-list padding>
<q-item clickable v-ripple>
<q-item-section avatar>
<q-icon name="send" />
</q-item-section>
<q-item-section>
Send
</q-item-section>
</q-item>
</q-list>
</q-scroll-area>
</q-drawer>
<q-page-container>
<q-page padding>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Fugit nihil praesentium molestias a adipisci, dolore vitae odit, quidem consequatur optio voluptates asperiores pariatur eos numquam rerum delectus commodi perferendis voluptate?
</p>
</q-page>
</q-page-container>
</q-layout>
</div>
</template>
</div>
Related
I'm trying to create a component showing ckeditor which allows me, to write Markdown content. For an unknown reason, there's no visible ckeditor in the frontend but DOM contains html code:
<div style="display: none;"></div>
<div class="ck ck-reset ck-editor ck-rounded-corners" role="application" dir="ltr" lang="de" aria-labelledby="ck-editor__label_ec33b345a3a3b06ddbf2aacd8201fc695">
<label class="ck ck-label ck-voice-label" id="ck-editor__label_ec33b345a3a3b06ddbf2aacd8201fc695">Rich Text Editor</label>
<div class="ck ck-editor__top ck-reset_all" role="presentation">
<div class="ck ck-sticky-panel">
<div class="ck ck-sticky-panel__placeholder" style="display: none;"></div>
<div class="ck ck-sticky-panel__content">
<div class="ck ck-toolbar ck-toolbar_grouping" role="toolbar" aria-label="Editor Werkzeugleiste">
<div class="ck ck-toolbar__items"></div>
</div>
</div>
</div>
</div>
<div class="ck ck-editor__main" role="presentation">
<div class="ck-blurred ck ck-content ck-editor__editable ck-rounded-corners ck-editor__editable_inline" lang="de" dir="ltr" role="textbox" aria-label="Bearbeitungsbereich des Editors: main" contenteditable="true">
<br data-cke-filler="true">
</div>
</div>
</div>
This is my VueComponent:
<template>
<ckeditor :editor="editor" v-model="editorData" :config="editorConfig"/>
</template>
<script>
import CKEditor from '#ckeditor/ckeditor5-vue';
import ClassicEditor from '#ckeditor/ckeditor5-editor-classic/src/classiceditor';
import Essentials from '#ckeditor/ckeditor5-essentials/src/essentials';
import Bold from '#ckeditor/ckeditor5-basic-styles/src/bold';
import Italic from '#ckeditor/ckeditor5-basic-styles/src/italic';
import Markdown from '#ckeditor/ckeditor5-markdown-gfm/src/markdown';
export default {
components: {
ckeditor: CKEditor.component
},
data: () => ({
editor: ClassicEditor,
editorData: 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut...',
editorConfig: {
extraPlugins: [Markdown, Essentials, Bold, Italic]
}
})
};
</script>
As I already said, nothing is visible in the frontend but html is rendered inside of the dom.
I do not need all features of the editor, just a few and Markdown. These are the packages I've installed:
"#ckeditor/ckeditor5-basic-styles": "^35.1.0",
"#ckeditor/ckeditor5-build-classic": "^35.1.0",
"#ckeditor/ckeditor5-core": "^35.1.0",
"#ckeditor/ckeditor5-editor-classic": "^35.1.0",
"#ckeditor/ckeditor5-essentials": "^35.1.0",
"#ckeditor/ckeditor5-markdown-gfm": "^35.1.0",
"#ckeditor/ckeditor5-vue": "^4.0.1",
"ckeditor5": "^35.1.0"
Could anyone help me please? I really appreciate your support.
If you are using Vue 3 you can use this one
<template>
<ckeditor :editor="editor" v-model="editorData" :config="editorConfig"></ckeditor>
</template>
<script setup>
import ClassicEditor from '#ckeditor/ckeditor5-build-classic';
import CKEditor from '#ckeditor/ckeditor5-vue';
import { ref } from '#vue/reactivity';
const ckeditor = CKEditor.component;
const editor = ClassicEditor;
const editorData = ref('<p>Content of the editor.</p>');
const editorConfig = ref({
})
</script>
<style>
</style>
Remember to install the plugin by
npm install --save #ckeditor/ckeditor5-vue #ckeditor/ckeditor5-build-classic
Ref Vue.js 3+ rich text editor component
I need to create a component which can be used in a php project as well. Do I have to crate the component in vue and duplicate the same code in php page using UMD? or is there any other method?
I am testing below code in a html page but getting blank page
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/#quasar/extras/material-icons/material-icons.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/quasar/dist/quasar.min.css">
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://cdn.jsdelivr.net/npm/quasar/dist/quasar.umd.min.js"></script>
<div id="q-app">
<q-layout view="hHh lpR fFf">
<q-header elevated class="bg-primary text-white" height-hint="98">
<q-toolbar>
<q-btn dense flat round icon="menu" #click="left = !left" />
<q-btn dense flat round icon="menu" #click="right = !right" />
</q-toolbar>
</q-header>
<q-drawer show-if-above v-model="left" side="left" bordered>
</q-drawer>
<q-drawer show-if-above v-model="right" side="right" bordered>
</q-drawer>
<q-page-container>
</q-page-container>
<q-footer elevated class="bg-grey-8 text-white">
<q-toolbar>
<q-toolbar-title>
Title
</q-toolbar-title>
</q-toolbar>
</q-footer>
</q-layout>
</div>
<script>
new Vue({
el: '#q-app',
data: function () {
return {
version: Quasar.version,
left: false,
right: false
}
},
methods: {
notify: function () {
this.$q.notify('Running on Quasar v' + this.$q.version)
}
}
})
</script>
It actually works.
See in action: https://codepen.io/disfated/pen/QWyYQXN
I only removed self-closed q-btn tags:
<q-btn dense flat round icon="menu" #click="left = !left"></q-btn>
<q-btn dense flat round icon="menu" #click="right = !right"></q-btn>
(You can read more on why it's needed and other caveats when using in-DOM templates, here)
Also added some content to demonstrate that everything functions well.
It appears that something from "outside world" is somehow prevents your code from working.
Take a closer look to Console and Network tabs in browser console. Something there should give you a hint about what's causing the issue.
The docs say
<a #click="doSomething"> ... </a>
is short-hand for
<a v-on:click="doSomething"> ... </a>
Why does this short-hand not work in all cases? For example,
<v-dialog v-model="dialog" width="500">
<template v-slot:activator="{ on }">
<v-btn dark v-on="on">Working button</v-btn>
<v-spacer />
<v-btn dark #on="on">Non-working button</v-btn>
</template>
<v-card>
<v-card-title>Some Dialog</v-card-title>
<v-card-text>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</v-card-text>
</v-card>
</v-dialog>
The "Working button" works, but the "Non-working button" doesn't. It seems like the v-on shorthand only works for events. But if that's true, I haven't seen it documented.
I'm using Vuetify in this example, but my question is about Vue.js in general.
Here's a CodePen of above: https://codepen.io/keeganwitt/pen/bGGVeZY
v-on:click short hand is #click not #on
so you can use #click instead of v-on="on". But here slotProp on is an Object with click (function) property. so you can use it like below
<v-btn dark #click="on.click">Now working button</v-btn>
but it is recommended to use v-on="on" since there could be multiple properties under on Object
Ex: if on is { click: fn(), mouseup: fn() }
<v-btn dark #click="on.click" #mouseup="on.mouseup" >Now working button</v-btn>
<!-- same as -->
<v-btn dark v-on="on">Now working button</v-btn>
So, #on in your example is shorthand for v-on:on, meaning that the event handler is listening for the child component to emit an on event. The v-btn component does not ever emit an on event, so nothing ever happens.
This isn't the thrust of your question, but you technically can use the v-on shorthand to bind to an on event:
Vue.config.devtools = false;
Vue.config.productionTip = false;
Vue.component('toggle', {
template: `
<button #click="onClick">
{{ isOn ? 'On' : 'Off' }}
</button>
`,
data() {
return {
isOn: false
};
},
methods: {
onClick() {
this.isOn = !this.isOn;
if (this.isOn) {
this.$emit('on');
} else {
this.$emit('off');
}
}
}
});
new Vue({
el: '#app',
methods: {
alert(val) {
alert(val);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<toggle #on="alert('on')" #off="alert('off')"/>
</div>
I think what is confusing you is the v-on="on" syntax. It's not obvious from the code, but the on variable being set as the value for v-on in this case is not a function, but an object.
From the vue docs on v-on:
Starting in 2.4.0+, v-on also supports binding to an object of event/listener pairs without an argument. Note when using the object syntax, it does not support any modifiers.
So the on variable in this case probably looks something like this:
{ click: onClick, mouseover: onMouseover }
Passing all of the event listeners this way allows for less boilerplate code. The way you would need to do this before v2.4 was like this:
<template v-slot:activator="{ onClick, onMouseover }">
<v-btn
dark
#click="onClick"
#mouseover="onMouseover"
>
Working button
</v-btn>
</template>
Working with VueJS and trying to style some text based on Vuetify's viewport breakpoints I have achieved this by binding the style to a condition like this:
:style="$vuetify.breakpoint.name === 'xs' ? { 'font-size': '1.5rem !important' }: { 'font-size': '2.5rem !important' }"
however I would like to use computed property instead just to make it cleaner and according to Vuetify's docs this can be achieved using breakpoint object however I can't get it to work for some reason
I've looked at this discussion and trying to copy the answer from #raina77ow but not too sure what I'm doing wrong.
below is my code; I'm trying to style the h3 element inside v-card-title
<template>
<div>
<section>
<v-layout>
<v-flex xs12 sm10 offset-sm1>
<v-card flat width="auto">
<v-card-title primary-title>
<h3
class="text-xs-center headline mb-0"
:style="fontSize"
>
Some Header here
</h3>
<div class="text-xs-center pa-5 mx-5">{{ card_text }}</div>
</v-card-title>
</v-card>
</v-flex>
</v-layout>
</section>
</div>
</template>
<script>
export default {
computed: {
fontSize() {
switch (this.$vuetify.breakpoint.name) {
case "xs":
return "1.5rem !important";
default:
return "3rem !important";
}
}
},
data() {
return {
card_text:
"Lorem ipsum dolor sit amet, brute iriure accusata ne mea."
};
}
};
</script>
Looking at Vuejs devtool I can see the computed property value changing as intended but just can't figure out why it's not being applied to CSS
Can someone tell me what am I doing wrong please!
Looks like the computed property isn't returning a full style specification. So you could either change the computed function
case "xs":
return {"font-size": "1.5rem !important"};
default:
return {"font-size": "3rem !important"};
or change how it's being used
:style="{'font-size': fontSize}"
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!