Create static left navbar component with Vue - javascript

I need a vertical navbar to the left of my app's layout, and with the content all across the right.
As it stands, I can't do that. My navbar pushes all the content down. How can I fix this?
This is what my App.vue's template looks like:
<template>
<div id="app">
<v-app>
<navbar/>
<v-main>
<v-container class="main-container">
<router-view/>
</v-container>
</v-main>
</v-app>
</div>
</template>
For the navbar component I am using vuetify, specifically the mini navbar
As per the example code, here is what it currently looks like:
<template>
<v-navigation-drawer
v-model="drawer"
:mini-variant.sync="mini"
permanent
>
<v-list-item class="px-2">
<v-list-item-avatar>
<v-img src="https://randomuser.me/api/portraits/men/85.jpg"></v-img>
</v-list-item-avatar>
<v-list-item-title>John Leider</v-list-item-title>
<v-btn
icon
#click.stop="mini = !mini"
>
<v-icon>mdi-chevron-left</v-icon>
</v-btn>
</v-list-item>
<v-divider></v-divider>
<v-list dense>
<v-list-item
v-for="item in items"
:key="item.title"
link
>
<v-list-item-icon>
<v-icon>{{ item.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
</v-navigation-drawer>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
#Component<Navbar>({})
export default class Navbar extends Vue {
private value: string = 'home';
private drawer: boolean = true;
private items: Array<Object> = [
{ title: 'Home', icon: 'mdi-home-city' },
{ title: 'My Account', icon: 'mdi-account' },
{ title: 'Users', icon: 'mdi-account-group-outline' }
]
private mini: boolean = true;
}
</script>
As mentioned on the top, this is still just pushing all the app's content down. This is what it currently looks like:
What can I do so that on the left I have my navbar but on the right
Edit: For the sake of clarification, I do want the navbar to be vertical, but I just don't want it to push down all the content. Basically I want my app to have a side navigation

Wrap the navbar and main in a <v-row>:
<template>
<div id="app">
<v-app>
<v-row>
<navbar/>
<v-main>
<v-container class="main-container">
<router-view/>
</v-container>
</v-main>
</v-row>
</v-app>
</div>
</template>
By default, v-app has an internal wrapper with flex-direction: column CSS, meaning that its child elements are placed vertically. By wrapping these 2 child elements in a v-row, you place them in a wrapping element with flex-direction: row.

I guess you are misunderstanding the concepts. You are wrapping a v-navigation-drawer inside a navbar component.
In Vuetify v-navigation-drawer is a component used to build Sidebars and that's why your component is on vertical.
You should try to use v-app-bar instead. Here's the documentation: https://vuetifyjs.com/en/components/app-bars/
Edit 1
You can configure an app sidebar or an app navbar using the app option inside the component like that:
<v-navigation-drawer
app
>
...
</v-navigation-drawer>
Just like in the official documentation. If you do it that way, the component will work properly and your content will not be pushed down.

Related

Vue Vuetify Performance/Freeze Issue

I'm building an vue2.js app, which gets data from an API and shows it within an vuetify data-iterator and on a leaflet map.
The App.vue component handles the communication between the other components (FilterPanel, ListPanel and MapPanel) ...
<template>
<div id="app">
<v-app id="inspire">
<v-app>
<v-navigation-drawer app width="26em" class="pb-16 pb-lg-0">
<FilterPanel #change="getData" />
</v-navigation-drawer>
<v-sheet id="widget-panel" app class="open" :elevation="8">
<v-container
class="grey lighten-5 scrollContainer"
style="display: flex; overflow-y: auto; flex: 1"
>
<ListPanel :loading="loading" :data="data">
<template #card="{ data }">
<DataCard :data="data" :id="'data' + data.id"></DataCard>
</template>
</ListPanel>
</v-container>
</v-sheet>
<v-main>
<MapPanel :loading="loading" :data="data" />
</v-main>
</v-app>
</v-app>
</div>
</template>
.. and the the API call to get the data.
...
methods: {
getData() {
this.loading = true;
fetch("https://jsonplaceholder.typicode.com/posts")
.then((response) => response.json())
.then((json) => (this.data = json))
.finally(() => (this.loading = false));
},
},
...
Within the ListPanel.vue component an slot is defined, so it can be used somewhere else with other data visualization.
<template>
<v-data-iterator
no-results-text="no - data"
loading="loading"
loading-text="loading - data"
no-data-text="no - data"
:items="data"
hide-default-footer
disable-pagination
>
<template v-slot:default="props">
<v-row>
<v-col v-for="item in props.items" :key="item.id" cols="12">
<slot name="card" :data="item">
no slot provided
</slot>
</v-col>
</v-row>
</template>
</v-data-iterator>
</template>
<script>
export default {
name: "ListPanel",
props: ["data", "loading"],
};
</script>
The MapPanel.vue component shows all data entries on the map with clickable markers, which will open popups to show some information about the data. The map also holds an loading indicator (v-progress-linear) to show if data is loading or not.
<template>
<l-map
id="map"
ref="myMap"
:style="styleObject"
v-if="showMap"
:zoom="zoom"
:center="center"
:options="mapOptions"
>
<v-progress-linear
style="z-index: 800 !important"
:indeterminate="loading"
:active="loading"
absolute
top
color="info"
></v-progress-linear>
<l-tile-layer :url="url" :attribution="attribution" />
<l-control-zoom position="bottomright"></l-control-zoom>
<l-marker
v-for="(item, index) in markers"
:key="'marker-' + index"
:lat-lng="item.location"
>
<l-popup :options="getPopupOptions(item.id)">
<DataCard :data="item" showRequest flat></DataCard>
</l-popup>
</l-marker>
</l-map>
</template>
For better visual representation I'm using an reusable component (DataCard) with v-card to show the necessary data.
I have setup a simplified version of the app. https://codesandbox.io/s/sleepy-hoover-s5w5l
Loading the app or getting the data, let the app freezes for some seconds. You'll see it at the progress bar on the top. The animation stops!
I figured out, that the v-card component is slowing down the whole app. If I remove the card component entirely, loading the data seems to work just fine.
Getting the data from the API isn't the problem, since getting the data from it is very fast.
Vue DevTools Extension within FireFox shows Problems with the Map Markes and the DataCard component.
So I'm asking, what I'm doing wrong with the DataCard Component ?

How to modify a child component's colour attribute from the parent in vue js

I have a child Card component:
<template>
<v-card
class="mx-auto"
max-width="500"
color=color
outlined
dark
>
<v-list-item three-line>
<v-list-item-content>
<div class="overline mb-4">
OVERLINE
{{color}}
</div>
<v-list-item-title class="headline mb-1">
Headline 5
</v-list-item-title>
<v-list-item-subtitle>Greyhound divisely hello coldly fonwderfully</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-avatar
tile
size="80"
color="grey"
></v-list-item-avatar>
</v-list-item>
<v-card-actions>
<v-btn
outlined
rounded
text
>
Button
</v-btn>
</v-card-actions>
</v-card>
</template>
<script>
export default {
name: 'Card',
props: {
color: String
}
}
</script>
And from the parent component I want to pass in the color to the child. A part of the parent component's code is shown below.
<template>
<Card v-bind:color="color"/>
</template>
<script>
export default {
data() {
return {
color: "#FFC400"
}
},
}
</script>
As you can see I tried to use a prop to pass the color from the parent to the child, however even though I'm able to pass the data to the child, {{color}} prints out #FFC400 I'm not sure how to assign the color value to the v-card's color attribute. How can I achieve this? Thank you.
The only thing missing is to also bind the prop to the color attribute of the <v-card>, otherwise it's only receiving the string "color", rather than the variable of that name.
You can use v-bind:color="color" or the shorthand :color="color"
<v-card
class="mx-auto"
max-width="500"
:color="color"
outlined
dark
>

Vuetify overlay overlays the drawer

I'm trying to create a navbar for Vue application, I'm using Vuetify, on large screen there is toolbar, on small there is hamburger icon which opens the navigation drawer.
It works, but there is an overlay that comes in front of drawer and I can't change a page.
Here is the code:
<template>
<div>
<v-app-bar dark>
<v-app-bar-nav-icon class="hidden-md-and-up" #click="sidebar = !sidebar"></v-app-bar-nav-icon>
<v-navigation-drawer v-model="sidebar" app>
<v-list>
<v-list-item v-for="(item, i) in menuItems" exact :key="i" :to="item.path">{{item.title}}</v-list-item>
</v-list>
</v-navigation-drawer>
<v-toolbar-title>Jobify</v-toolbar-title>
<v-spacer></v-spacer>
<v-toolbar-items class="hidden-sm-and-down">
<v-btn text v-for="item in menuItems" :key="item.title">
<router-link :to="item.path">{{item.title}}</router-link>
</v-btn>
</v-toolbar-items>
</v-app-bar>
</div>
</template>
<script>
export default {
data: function() {
return {
sidebar: false,
menuItems: [
{ path: "/jobs", name: "jobs", title: "Jobs" },
{ path: "/companies", name: "companies", title: "Companies" },
{ path: "/jobs/new", name: "new-job", title: "Add job" }
]
};
}
};
</script>
<style>
</style>
Only Vuetify components that I'm using are here, there is, actually, v-app in App component.
Try to add hide-overlay prop to the v-navigation-drawer component in order to hide the overlay :
<v-navigation-drawer v-model="sidebar" app hide-overlay >

VuetifyJS toolbar overlays content as soon as fixed prop gets added

I need to use a fixed toolbar (with an extension). Problem is that The toolbar overlays the content as soon as the fixed prop gets added. How to place the content below the toolbar?
CodePen example: https://codepen.io/anon/pen/jpgjyd?&editors=101
<div id="app">
<v-app id="inspire">
<v-toolbar
color="primary"
dark
fixed
extended
>
<v-toolbar-side-icon></v-toolbar-side-icon>
<v-toolbar-title slot="extension">Title</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn icon>
<v-icon>search</v-icon>
</v-btn>
</v-toolbar>
<v-layout>
<v-flex xs1 >
+++ FIRST LINE +++
[lots of text...]
</v-flex>
</v-layout>
</v-app>
</div>
One solution is adding app to v-toolbar and wrap v-layout inside v-content
<v-toolbar
app
color="primary"
dark
fixed
extended
>
...
</v-toolbar>
<v-content>
<v-layout>
</v-layout>
</v-content>
Demo https://codepen.io/ittus/pen/mGdbeN?editors=1010
References: Vuetify application layout tutorial
Add app to v-toolbar and wrap v-layout inside v-content
Couldn't get exactly your question, is this what you wanted to achieve:
new Vue({
el: '#app',
mounted(){
var fixedElement = document.getElementsByClassName('v-toolbar--fixed')[0];
var layout = document.getElementsByClassName('layout')[0];
layout.style = "padding-top: "+ fixedElement.offsetHeight + 'px';
}
});
https://codepen.io/Younes_k/pen/LJEjOL

Open Vue dialog(modal) on parent

First of all, i've already read this post but it didn't help me (or i wasn't able to solve with that)
I've got a component which is a Vuetify Modal (ModalTurno.vue).
I'm trying to open this from a button in my Bottom Nav (bottomNav.Vue) (also a component).
I was even able to do that! but it opens on the Bottom Nav div. So I'd like to open this on my main vue (Turno.Vue) so i'll be able to see the whole modal. This is driven me crazy
Vuetify Modal (ModalTurno.Vue):
<template>
<v-dialog v-model="dialog" persistent max-width="500px" >
<v-card>
<v-card-title>
<span class="headline">User Profile</span>
</v-card-title>
</v-card>
</v-dialog>
</template>
<script>
export default {
data: () => ({
dialog: false
})
}
</script>
Bottom Nav (bottomNav.Vue) (Where the button is):
<template>
<v-card height="200px" flat>
<v-bottom-nav
:value="true"
color="indigo"
fixed
>
<v-btn
dark
flat
value="favorites">
<span>Agregar turno</span>
<v-icon>add_circle</v-icon>
</v-btn> <!--THIS IS THE BUTTON TO OPEN THE DIALOG -->
</v-bottom-nav>
</v-card>
</template>
<script>
export default {
name: 'header',
data () {
return {
bottomNav: 'recent'
}
}
}
</script>
Turno.Vue (Where the modal should be displayed):
<template>
<div>
<bottomNav> </bottomNav>
<modalTurno> </modalTurno>
</div>
</template>
<script>
import bottomNav from "./components/bottomNav.vue";
import modalTurno from "./components/ModalTurno.vue";
export default {
name: 'turno',
components: {
bottomNav,
modalTurno
}
}
</script>
Thanks to everyone who use his/her time to read this post!
You could do this by :
in Turno.vue
add ref="modalTurno" to (to make this component accessible via this.$refs.modalTurno - Accessing Child Component Instances & Child Elements)
in bottomNav.Vue
add #click="$parent.$refs.modalTurno.dialog = true" to v-btn (this will update dialog in modalTurno)

Categories

Resources