I have a site layout using Bootstrap as follows (and it needs to stay like this):
<div class='row'>
<div class='col-md-2'>
<main-nav-component></main-nav-component>
<sub-nav-component></sub-nav-component> <!-- Problem Area -->
</div>
<div class='col-md-10'>
<router-view></router-view> <!-- MAIN CONTENT SECTION -->
</div>
</div>
This gives me two columns across the whole site:
A navigation column on the left
A "contents" column on the right
Various pages (but not all) that are displayed in the main content section (via the router-view) also need to display their own sub-nav below the main nav. There are different sub-navs for different pages and I can't figure out how to accomplish this in VueJS. Any ideas?
There's a couple ways of doing this. The easiest is to include the sub header nav components on the components themselves, but you said the above will not change, so instead, you need to pass some kind of data to the sub-nav-component to let it know what to render. One way you can do this is to use the route.
For example, in one application we needed to have a specific sub nav for a group of pages. We have declared a 'zone' in meta, then in the header component we read the zone and display the proper sub-nav for it.
In the router.js:
{
path: '/profile/user',
name: 'Profile',
component: Profile,
meta: { zone: 'profile'}
},
On the header component:
computed: {
zone(){
return this.$store.state.route.meta.zone
},
And in the html of the header component:
<profile-nav v-if="zone == 'profile'"></profile-nav>
Related
I am very new to vue/nuxt and got stuck with this issue when implementing the frontend for a headless cms.
I have two routes as such
Pages
-StandardPage
->_standardPage.vue
-InfoPage
->_InfoPage.vue
My _standardPage.vue and _infoPage.vue has this code (same for now but will change) Basicly they need a guid Id to fetch page information from the Api. The page information contains different blocks that then are dynamicly generated components:
<template>
<div>
<Menu />
<div v-for="block in site.blocks" :key="block.id">
<component :is="block.blockName" :blockData="block"/>
</div>
</div>
</template>
<script>
export default{
data(){
return {
site: Object
}},
methods: {
this.site = result from async method, calling api to get page info by guid id (how do i get a hold of this guid)
}
}
now the problem is that in my menu component im doing a fetch to get the menu display name and corresponding pageId (menu items can be added/removed on a whim so I have no way of knowing beforehand those Ids), When I click a menu item i wish to pass along the page Id to my standard or info page so they can fetch corresponding data but to no success.
I have tried to $emit from child to parent but I have no success in doing that across routes. If i do a $router.push with path and query I get a nasty guid in the url and that does not look very friendly.
What would be the best way to pass my guid from my menu component to my routed pages?
If you do have a button in your parent looking like
<button #click="$router.push({ name: 'details-page', params: { guid: '123' } })">
test
</button>
and this in the child named /pages/details-page.vue
<script>
export default {
name: 'DetailsPage',
async fetch() {
console.log('route', this.$route.params.guid)
await axios...
},
}
</script>
You'll be able to load some data in the children, depending of your guid and without an ugly path.
My goal is to allow the user to dynamically build a form from different components, based on what they choose in a select element. For example, they can choose to add a Heading, then maybe a Paragraph, then another Heading, etc. Each "part" is a separate Component.
I know this sort of thing has been asked before, but I'm only day 2 into Vue and I think I'm 90% of the way there - I'm just missing something. What I believe I'm stuck on is how to add a component to my app's data to allow Vue to render it out.
Here is the relevant markup:
<div id="creator">
<template v-for="part in existingParts">
<component :is="part"></component>
</template>
<select class = "custom-select" id = "new-part-chooser" v-model="newPart" v-on:change="addPart">
<option disabled value = "">Add a new part</option>
<option
v-for="part in possibleParts"
v-bind:value="part.toLowerCase()"
>{{ part }}</option>
</select>
<?php
// These simply bring in the templates for the components
// I know this isn't standard practice but... one thing at a time
include 'component-heading.html';
include 'component-paragraph.html';
?>
</div>
and my javascript file:
Vue.component("part-heading",{
data:function(){
return {
text: ""
}
},
template:"#component-heading-template"
});
Vue.component("part-paragraph",{
data:function(){
return {
text: ""
}
},
template:"#component-paragraph-template"
});
const Creator = new Vue({
el:"#creator",
data:{
newPart:"",
possibleParts:[
"Heading",
"Paragraph"
],
existingParts:[]
},
methods:{
addPart:function(e){
/*** This is where I'm stuck - what do I push here? ***/
this.existingParts.push();
}
}
});
I've read through the docs and Google'd the hell out of the topic, but every setup seems to be just different enough that I can't figure out how to apply it.
I was missing the fact that in the markup, the :is directive causes Vue to create a component that matches the name of the element in existingParts. So push()-ing "part-heading" into existingParts causes Vue to render an instance of the "part-heading" component.
The updated, working code is:
this.existingParts.push('part-'+this.newPart);
I have been doing a lot of VueJS tutorials including the router, event bus, and trying to use fetchival and axios to no avail.
The setup, I want there to be two sections. One where I have buttons and the second section would be updated with html data from html files that varies depending on the button pressed.
I have used event bus to be able to just update the second div with basic, static html
(i.e. <p>got it</p>) but I cannot, for the life of me, use any request to get html from another website or file and load it into the div.
I don't necessarily need anyone to build it for me, but even some guidance and direction would be infinitely appreciated.
Based on your comments above, I think you want to change your thinking from "loading html files" to "showing different parts of the Vue component."
Here's a basic example. I'm going to use Vue single-file component syntax, but it's not hard to refactor for class-based components:
<template>
<div>
<button #click="clickedShowFirst">Show First</button>
<button #click="clickedShowSecond">Show Second</button>
<div v-if="showingFirst">
This is the first section!
</div>
<div v-else>
This is the second section!
</div>
</div>
</template>
<script>
export default {
data: function () {
return {
// We default to showing the first block
showingFirst: true
}
}
methods: {
clickedShowFirst: function () {
this.showingFirst = true
},
clickedShowSecond: function () {
this.showingFirst = false
}
}
}
</script>
You could of course make each of the v-if blocks components of their own that you import (which makes sense if they are complex themselves).
Or as suggested by Phillipe, you can use vue-router and make each of those views a different page with a different URL.
One last recommendation to leave you with, I found Jeffrey Way's Laracasts series on Vue.js amazingly helpful when I was learning. His episode titled "Exercise #3: Tabs" is very similar to what you're asking here.
You could use vue-router (https://router.vuejs.org/en/). In first section put the router-link (https://router.vuejs.org/en/api/router-link.html), your buttons, in second section put the router-view (https://router.vuejs.org/en/api/router-view.html).
I have a component myHello:
<temlate>
<div>
<h2>Hello</h1>
<p>world</p>
</div>
</template>
And main component:
<h1>my hello:</h1>
<my-hello><my-hello>
After rendering shows this:
<h1>my hello:</h1>
<div>
<h2>Hello</h1>
<p>world</p>
</div>
How to delete <div> ?
With VueJS, every component must have only one root element. The upgrade guide talks about this. If it makes you feel better, you are not alone. For what it's worth the components section is a good read.
With the myriad of solutions to your problem, here is one.
component myHello:
<temlate>
<h2>Hello</h1>
</template>
component myWorld:
<temlate>
<p>world</p>
</template>
component main
<h1>my hello:</h1>
<my-hello><my-hello>
<my-world><my-world>
Vue gives you the tools to do so by creating templates or you can do it by having a parent div with two parent divs as children. Reset the data from the data function. Stick with convention (create templates). It's hard to get used to use Vue when you have a jQuery background. Vue is better
Ex.
data () {
message: 'My message'
}
When you click a button to display a new message. Clear the message or just set the message variable with a new value.
ex. this.message = 'New Message'
If you like to show another div. you should used the if - else statement and add a reactive variable. Ex. showDivOne
data () {
message: 'My message'
showDivOne: true,
showDivTwo: false
}
Add this reactive variables to the parent divs that corresponds to the div.
When clicking the button, you should have a function like...
methods: {
buttonClick () {
this.showDivOne = false
this.showDivTwo = true
}
}
I think you can use v-if directive to controll. Add a flag to controll status to show or hide
When should I use a directive vs a component in vue.js? I'm implementing some stuff from Bootstrap and it looks like I could do it either way (I'm starting with the dropdown menu).
I get the feeling that a directive is more for manipulating the dom on a single element, while components are for packaging a bunch of data and/or dom manipulation. Is this a good way to look at it?
This Stack Overflow question is the #1 result to the Google query "vue directive vs component". Saurshaz’s answer is currently the accepted one and it’s very wrong in Vue 2.0. I imagine this is leading a lot of people astray so I'm going to weigh in here.
The answer to “should I use a directive or a component in Vue” is almost always a component.
Do you want to have reusable HTML? I.e. reusable widgets? Then use a component. Do you want two of these widgets to have discrete data? Then use a component. The data of one will NOT override the data of another. Maybe that was true in Vue 1.0, I don't know. But it's absolutely not true in Vue 2.0. In Vue 2.0, your components have a data function that returns a unique set of data. Consider this real-life of a Vue dropdown that has an HTML markup similar to the UI Bootstrap dropdown:
<template>
<span class="dropdown sm-dropdown" #click="toggle" :class="{'open': isOpen}">
<a class="dropdown-toggle">
<span class="special-field">{{ label }}</span>
</a>
<ul class="dropdown-menu">
<li v-for="choice in choices">
<a #click.prevent="click(choice)">{{ choice.label }}</a>
</li>
</ul>
</span>
</template>
<script>
export default {
name: 'Dropdown',
props: ['label', 'options', 'onChange'],
data() {
return {
choices: this.options,
isOpen: false
}
},
methods: {
click(option) {
this.onChange(option);
},
toggle() {
this.isOpen = !this.isOpen;
}
}
}
</script>
Now in a parent component, I can do something like this:
<template>
<div class="container">
<dropdown
label="-- Select --"
:options="ratingChoices"
:onChange="toggleChoice"
>
</dropdown>
<dropdown
label="-- Select --"
:options="ratingChoices"
:onChange="toggleChoice"
>
</dropdown>
</div>
</template>
<script>
import Dropdown from '../dropdown/dropdown.component.vue';
export default {
name: 'main-directive',
components: { Dropdown },
methods: {
toggleChoice(newChoice) {
// Save this state to a store, e.g. Vuex
}
},
computed: {
ratingChoices() {
return [{
value: true,
label: 'Yes'
}, {
value: false,
label: 'No'
}]
}
}
}
</script>
There's a decent amount of code here. What's happening is we're setting up a parent component and inside that parent component we have two dropdowns. In other words, the dropdown component is being called twice. The point I'm trying to make in showing this code is this: when you click on the dropdown, the isOpen for that dropdown changes for that directive and for that directive only. Clicking on one of the dropdowns does not affect the other dropdown in any way.
Don't choose between components or directives based on whether or not you're wanting discrete data. Components allow for discrete data.
So when would you want to choose a directive in Vue?
Here are a couple of guidelines that'll hopefully get you thinking in the right direction.
You want to choose a directive when you're wanting to extend the functionality of HTML components and you suspect that you’re going to need this extendability across multiple components and you don't want your DOM to get deeper as a result. To understand what I mean by this, let's look at the directives that Vue provides out of the box. Take its v-for directive for instance. It allows you to loop through a collection. That's very useful and you need to be able to do that in any component you want, and you don't want the DOM to get any deeper. That's a good example of when a directive is the better choice.[1]
You want to choose a directive when you want a single HTML tag to have multiple functionality. For example, an element that both triggers an Ajax request and that has a custom tooltip. Assuming you want tooltips on elements other than Ajax-triggering elements, it makes sense to split these up into two different things. In this example I would make the tooltip a directive and the Ajax feature driven by a component so I could take advantage of the built-in #click directive that’s available in components.
1 A footnote for the more curious. In theory v-for could have been made as a component, but doing so would have required a deeper-than-necessary DOM every time you wanted to use v-for as well as a more awkward syntax. If Vue had chosen to make a component out of it, instead of this:
<a v-for="link in links" :href="link.href">link.anchor</a>
The syntax would have had to have been this:
<v-for items="link in links">
<a :href="link.href">link.anchor</a>
</v-for>
Not only is this clumsy, but since the component code would have needed to implement the <slot></slot> syntax in order to get the innerHTML, and since slots cannot be immediate children of a <template> declaration (since there's no guarantee that slot markup has a single node of entry at its top level), this means there would have to be a surrounding top-level element in the component definition for v-for. Hence the DOM would get deeper than necessary. Directive was unequivocally the right choice here.
I think of it this way:
Components define widgets - these are sections of html that have behavior associated with them.
Directives modify behavior of sections of html (which may or may not be widgets).
I think this difference is better explained with two examples.
Components: are wrappers that are best suited when you need to insert (or add) your own HTML tags over something to render it. E.g. a widget, a custom button, etc where you would need to add some HTML tags to show it properly.
Directives: don't add tags but rather give you direct access to the HTML tag (to which you have added the directive). This gives you access to modify the attributes of that HTML element directly. E.g. initializing a tooltip, set css styles, bind to an event, etc.
Reusability is a reason for using directives,
While Components are also creating reusable 'widgets', two components in the same html system would overwrite the previous ones 'data', So think of directives in a case like this.
Another point worth thinking of is - Can user be using it via HTML only after some instructions ?