What is the Vue.js equivalent of Express-Handlebars sections? - javascript

I started a project using Express and Handlebars and then was encouraged to look at Vue.js. I am still at the stage of reading the docs but so far can't understand how to have layouts, partials and sections in Vue.js. I think a partial would be a component, but I'm lost how to have a layout with partials and sections that I can inject content into.
This is what I do using npm express-handlebars in a file called baselayout.hbs:
<!doctype html>
<html lang="en">
<head>
{{> global/headcode }} <!-- partial view with code for the head tag. it has stuff like favicon links --->
{{{_sections.pagemeta}}} <!-- page specific metadata injected here. this would be meta keywords description etc for a page/view --->
</head>
<body>
<div>
{{> global/siteheader }} <!--- partial view for the site's header --->
<div id="base-Container">
<main id="base-Content" role="main">
{{{ body }}} <!--- a page's main body content goes here --->
</main>
</div>
</div>
{{> sitefooter }}
{{{_sections.pagescripts}}} <!-- section for page-specific scripts injected here --->
</body>
</html>
How could I setup something like the above in Vue.js that would also work with server-side rendering? I just need a base layout with header/footer components included but also sections into which page-specific content can go.

For SSR, you should look at Nuxt.js, Vapper, or one of the other SSR Vue frameworks.
That said, yes, you would use components for everything. Generally, you would have one component for your main layout, then one for each view, then individual components for each partial/section that you would then import into your views and/or main layout. So, for example, based on the above code:
Your main app layout:
// AppLayout.vue
<template>
<div id="app-layout">
<site-header />
<router-view />
<site-footer />
</div>
</template>
<script>
import SiteHeader from './components/global/SiteHeader.vue'
import SiteFooter from './components/global/SiteFooter.vue'
export default {
name: 'AppLayout',
components: {
SiteHeader,
SiteFooter
},
meta: {
// metatags and other head content can be modified using vue-meta or similar
}
}
</script>
Example 'partial' component:
// BaseContainer.vue
<template>
<main id="base-container" role="main">
<h1 class="title">{{ content.title }}</h1>
<img :src="image" alt="" />
<base-content v-html="content.body" />
</main>
</template>
<script>
import BaseContent from './components/content/BaseContent.vue'
export default {
name: 'BaseContainer',
components: {
BaseContent
},
props: {
content: {
type: Object,
default() {
return {}
}
},
image: {
type: String,
default: ''
}
}
}
</script>
Example view component:
// MyView.vue
<template>
<div id="my-view">
<base-container :content="content" :image="image" />
</div>
</template>
<script>
import BaseContainer from './components/BaseContainer.vue'
import content from './data/myContent.json'
import image from './assets/myImage.jpg'
export default {
name: 'MyView',
components: {
BaseContainer
},
data() {
return {
content,
image
}
}
}
</script>
You would then use vue-router to specify which view component to load based on the current URL.

You probably need to use components and slots within your components.
Yes you need to create a component for each of your partials. Each component would have a template.
Then you will have a main component that will put all of this together. Using the more granular components you already have (your partials).
Now if the template structure (html for each component) is coming from the server then you can prob use slots https://v2.vuejs.org/v2/guide/components-slots.html which is a way VueJs allow components to receive custom markup when instantiating the components (see the example in the docs)
For the general layout and UI components of your app you may want to look at https://element.eleme.io/#/en-US/component/layout which is a nice alternative to the more popular Vuetify.

Related

Nuxtjs: How to call component inside another component?

I understand paradigm "Page-component" but what if I have a page that renders component, how do I call another component inside this component? Currently nuxtjs does not allow me do it. I can not stick to standart "page-component" scheme as I am bulding cart which calls cart-items.
Say If a cart component which is called by page looks like this, how would it call cart-item component inside it?
<!---- cart component called from index.vue --->
<template>
<div>
<Cart-item></Cart-item> < ---------- This doesn't work.
</div>
</template>
<script>
export default {
props: ['items']
}
</script>
I managed it the standard way:
<template>
<div>
<CartItem></CartItem>
</div>
</template>
<script>
import CartItem from '../components/Cart-item'
export default {
props: ['items']
}
</script>
Since nuxtjs auto-registers all components wonder if there is more graceful way.
EDIT: as promised, here is an example on how to pass some content to a component from another one thanks to slots. This is totally working in any Nuxt page ofc.
NestedContent.vue
<template>
<div>
<p>Here is the NestedContent component and below is a slot passed to ParentWithSlots' component</p>
<hr />
<parent-with-slots>
<!-- <template #default> // this one can be omit since we do use the default slot here -->
<p>This content is inserted into the component ParentWithSlots</p>
<!-- </template> -->
</parent-with-slots>
</div>
</template>
ParentWithSlots.vue
<template>
<div>
<p>xxxxxxxxxxx ParentWithSlots' content before slot xxxxxxxxxxx</p>
<slot>Default content in case none is provided</slot>
<p>xxxxxxxxxxx ParentWithSlots' content after slot xxxxxxxxxxx</p>
</div>
</template>
Here is how it looks
PS: you may also give a try to layouts, it can be useful for overall positioning of some of your components visually.
If your components are in the components directory, you can set components: true in your nuxt.config.js and have access to it pretty much anywhere without any additional step with the <cart-item></cart-item> syntax.
More details here: https://nuxtjs.org/blog/improve-your-developer-experience-with-nuxt-components/

vue js slots is not appearing data for template layout

I am using slots first time in vue.js for layout. as per reference I included the component and used the slot tag in the proper place where I needed it, but still, the slot tag is not able to render my tags or data I want to display on my layout page. I found no error in the console log, my header component, and footer components are included successfully, and appearing on the web page. but the slot tag is not working.
Here is my code. please help me to find my mistake.
SlotTest.vue
<template>
<div>
<test-component>
<h1>Testing Slots</h1>
</test-component>
</div>
</template>
<script>
import TestComponent from "./Test";
export default {
components: { TestComponent },
};
</script>
Test.vue
<template>
<div>
<header-content> </header-content>
<slot> </slot>
<footer-content> </footer-content>
</div>
</template>
<script>
import HeaderContent from "./Header";
import FooterContent from "./Footer";
export default {
components: { HeaderContent, FooterContent },
};
</script>

What's the appropriate way to replace my wrong design?

I have a question about nuxt.js project,
as we know the nuxt.js generate the routes automatically.
I have a page structure like this:
In the header there is navigator in it, I want use it to switch pages(switch the main body's content).
but the nuxt.js do not like vue-router have <router-view/>
<route-link></route-link>
<router-view/>
it only has
<nuxt-link></nuxt-link>
So I write code structure like this:
<template>
<div >
<Header></Header> <!-- the main body written in the Header -->
<Footer></Footer>
</div>
</template>
the Header component:
<navigator-component></navigator-component>
<div>
<Home v-show="$store.state.page_name == 'home' "></Home>
<Search v-show="$store.state.page_name == 'search' "></Search>
<Aboutus v-show="$store.state.page_name == 'about_us' "></Aboutus>
<Contactus v-show="$store.state.page_name == 'contact_us' ">
</Contactus>
</div>
but there is a problem, the URL will not switch, it will stay in the root URL.
So, what's the appropriate way to solve my problem? how to design the
code structure?
Nuxt has a "router-view". It is just: <nuxt />.
The way to go here, is to use layouts.
There is a default file in the layouts directory. Every page has this layout by default.
You can simply write in this default layout file your page structure.
For example:
<template>
<Header />
<nuxt />
<Footer />
</template>
<script lang="ts">
import Vue from 'vue';
import Header from '..';
import Footer from '..';
export default {
components: {
Header,
Footer,
},
};
</script>

Load Google Maps one time with multiple components

I'm using Google API to display a map on my application and I'm using multiple dynamic components which each of them loads one map. Is it possible to load this map only one time in the parent and display it in each child component ?
Here are my components structure (I've removed everything which was important for my problem) :
Parent
<template>
<div>
<div v-for="tab in getTabs" :key="tab.index">
<keep-alive>
<component v-if="selectedTab === tab.index" v-bind:is="selected"/>
</keep-alive>
</div>
</div>
</template>
<script>
import Tab from './Tab';
export default {
name: 'Dispatcher',
components: {
Tab
},
data() {
return {
selected: '',
selectedTab: '',
tabIds: []
}
}
}
</script>
Child
<template>
<div class="row">
<div class="col-md-12" style="height:80vh">
<div class="gmap"></div>
</div>
</div>
</template>
<script>
export default {
name: 'Tab'
}
</script>
index.blade.php
<script src="map.js" type="text/javascript"></script>
<div id="dispatcher-body">
{{ csrf_field() }}
<dispatcher></dispatcher>
</div>
The script map.js in my blade is doing everything to load Google Maps on a div which has gmap class, such as in my child component. The map is loading great but it creates a new map for each child component.
I tried using v-slot but it doesn't work with my map.js and my script says that it doesn't find any gmap class (because I have no child component when I refresh the page).
Thanks in advance,
Maybe you should try vue-google-maps
I strongly recommend to not use vanilla libraries with frameworks such a vue, react or angular because they have their workflow and it creates conflicts between them

Nothing can go in between custom tags - <myTag_vue> ... </> in vue js?

The structure of my vue app is this:
root.vue:
<template>
<header-yo> <h1> visible? or not? </h1> </header-yo> //content btw tags is invisible
<footer> </footer>
</template>
<script>
import Footer from "./components/footer";
import Header from "./components/header";
export default {
components: {
"footer": Footer,
"header-yo": Header // header-yo is "custom tag" now!
}
};
</script>
<style> ...</style>
header:
<template>
<h1>rrrrr</h1> //this is visible
</template>
<script> </script>
<style scoped> </style>
When we create this custom tag "" - it only serves as declarator of a component?
Nothing can be added in btw custom tags?
Because I can only see on the webpage what's inside my actual header.vue file, not inside my own custom tags. Is this the vue-idiomatic style?
the root.vue that has all other components is only there to put things together, and nothing can go in btw custom tags - they only show what components\other vue files are there? If I want to add something in header- it should go in corresponding separate header.vue file?
The js fiddle is here
What your trying to do is comparable to AngularJS transclusion mechanism. You need to actually tell vuejs where the content will be inserted in the template by using a <slot></slot> element:
<template>
<h1>rrrrr</h1> // this is visible
<slot></slot> // this is where "<h1> visible? or not? </h1>" would be inserted
</template>
You can find more about this in vuejs component guide or the dedicated Slot documentation page.

Categories