I am building a small application on VueJS where I am having a api which gives information about the components to be loaded while rendering the page.
I have setup vue-router as:
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
export const router = new VueRouter({
routes:
[
{
path: '/',
component: Vue.component('welcome', () => import('../Components/Admin/Login.vue')),
name: 'login'
},
{
path: '/:page',
component: Vue.component('dashboard', () => import('../Components/Admin/Dashboard/Dashboard.vue')),
name: 'dashboard'
},
]
});
Now this dashboard component asynchronously calls the component to be loaded, something like this:
<template>
<div>
<header>
<nav class="navbar has-shadow">
<div class="container">
<div class="navbar-brand">
<a class="nav-item is-centered">
<img src="nits-assets/images/logo.png" alt="NitsEditor Logo" width="170" height="50">
</a>
</div>
<div class="navbar-menu">
<div class="navbar-end is-centered">
<div class="navbar-item">
<p class="control"><a class="button is-primary is-outlined is-rounded">Log out</a></p>
</div>
</div>
</div>
</div>
</nav>
</header>
<div class="section" id="dashboard">
<div class="columns">
<aside class="column is-2 menu-section">
<nav class="menu is-centered">
// Menu section
</nav>
</aside>
<main class="column" id="main-section">
<page-title :title="title"></page-title>
<div class="columns is-multiline">
<div class="column">
<top-seller-widget></top-seller-widget>
</div>
</main>
</div>
</div>
</div>
</template>
<script>
export default {
name: "dashboard",
data() {
return {
pageComponents: '',
title: '',
description: ''
}
},
components: {
'PageTitle': () => import('./PageTitle'),
'TopSellerWidget': () => import('./TopSellerWidget')
},
created() {
axios.get('/api/page-components?slug='+this.$route.params.page).then(response => {
if(response.status === 200)
{
this.title = response.data.page.title
console.log(response.data)
}
})
}
}
</script>
<style scoped>
// Some styling
</style>
As you can see I am trying to import components in component section asynchronously:
components: {
'PageTitle': () => import('./PageTitle'),
'TopSellerWidget': () => import('./TopSellerWidget')
},
And calling the components inside the template:
<div class="section" id="dashboard">
<div class="columns">
<aside class="column is-2 menu-section">
<nav class="menu is-centered">
// Menu section
</nav>
</aside>
<main class="column" id="main-section">
<page-title :title="title"></page-title>
<div class="columns is-multiline">
<div class="column">
<top-seller-widget></top-seller-widget>
</div>
</main>
</div>
</div>
Now while getting the axios call data:
I get in this format:
{
components: [
{name: 'PageTitle', location:'./PageTitle', 'data': {title: 'Dashboard'}},
{name: 'TopSellerWidget', location:'./TopSellerWidget', 'data': NULL}
]
}
Then I checked whether I can load my components when we get a response:
axios.get('/api/page-components?slug='+this.$route.params.page).then(response => {
if(response.status === 200)
{
this.title = response.data.page.title
if(response.data.page.content)
{
Vue.component('PageTitle', () => import('./PageTitle'))
Vue.component('TopSellerWidget', () => import('./TopSellerWidget'))
}
console.log(response.data)
}
})
It worked fine, but when I'm trying to make it dynamic:
axios.get('/api/page-components?slug='+this.$route.params.page).then(response => {
if(response.status === 200)
{
this.title = response.data.page.title
if(response.data.page.content)
{
response.data.page.content.components.forEach(function (val, index) {
Vue.component(val.name, () => import(val.location))
})
}
console.log(response.data)
}
})
I am getting errors:
And in console I get:
What can I do in such condition?
I think you almost got the answer yourself. The error log probably says that import(val.location) is importing an dependency but the dependency is an expression. So you should replace it with a name instead, like you did in your experiment.
Also, I am not sure if it is necessary to asynchronously import the two components (PageTitle and TopSellerWidget) inside Dashboard. Dashboard itself is already loaded asynchronously, so any of its dependencies, such as PageTitle is also loaded only when you request Dashboard. And since you need both PageTitle and TopSellerWidget to properly render your Dashboard, you may as well get them along with Dashboard, rather than spending one more round trip on the wire.
Related
I'm completely new to VueJS and am trying to create a project tile list with different data values per tile. I use Vue CLI. I created a component which is my template for one project tile.
component TheProjectTile.vue :
<template>
<router-link to="{{ project.url }}">
<div
class="row project-container"
style="background-color: {{ project.backgroundcolor }};"
v-scrollanimation
>
<div class="column column-60">
<h2>{{ project.title }}</h2>
<div class="button">view</div>
</div>
<div class="column">
<img src="#/assets/img/{{ project.image }}" alt="{{ project.title }}" />
</div>
</div>
</router-link>
</template>
<script type="text/javascript">
export default {
props: { project: Object },
data() {
return {}
}
}
</script>
Then I have my View on my Vue CLI application where I want to render the project tiles and where I want to give the data to the tiles:
View where the projects should be shown
<template>
<div id="projekte">
<section class="container">
<div id="projects">
<projectlist v-for="project in projects" :project="project" />
</div>
</section>
</div>
</template>
<script>
import TheProjectTile from './components/TheProjectTile'
export default {
components: {
projectlist: TheProjectTile
},
data() {
return {
projects: [
{
url: '/projekte/client1',
backgroundcolor: '#005ca9',
title: 'Website client 1',
img: 'client1.png'
},
{
url: '/projekte/client2',
backgroundcolor: '#c10b25',
title: 'Website client 2',
img: 'client2.png'
}
]
}
}
}
</script>
What do I need to change that it works? :/
Please take a look at following snippet:
You need to bind data with v-bind or :,
in v-for loop you need key
Vue.component('projectList', {
template: `
<a :to="project.url">
<div
class="row project-container"
:style="{'background-color': project.backgroundcolor}"
>
<div class="column column-60">
<h2>{{ project.title }}</h2>
<div class="button">view</div>
</div>
<div class="column">
<img :src="'#/assets/img/'+project.image" :alt="project.title" />
</div>
</div>
</a>
`,
props: { project: Object },
})
new Vue({
el: '#demo',
data() {
return {
projects: [{url: '/projekte/client1', backgroundcolor: '#005ca9', title: 'Website client 1', img: 'client1.png'}, {url: '/projekte/client2', backgroundcolor: '#c10b25', title: 'Website client 2', img: 'client2.png'}]
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<div id="projekte">
<section class="container">
<div id="projects">
<!-- 👇 key -->
<project-list v-for="(project, index) in projects" :key="index" :project="project" />
</div>
</section>
</div>
</div>
I'm fetching Data from an API using Axios and i'm displaying them as Cards in a Component movie cards, the thing is that i want to be able to click on a single movie card and the router take me to another page(singlepage.vue) with that movie id from the API and displaying the Book specific data but the problem is that I do not know how to send the ID of the same movie card I entered to the API and then return its data (I have to use the post method for request and put my post image below).
this is postman image. click here.
firstPage.vue
this is same component that i have movie cards.
<template>
<div class="articles">
<h2>featured articles</h2>
</div>
<div class="article-container">
<div class="article-grid" v-for="data3 in datas3" :key="data3.ContentID" >
<router-link to="/content" >
<img :src="data3.LandscapeImage">
<div class="article-title">
<p>{{data3.Properties[5].Value}}</p>
<h3>{{data3.Title}}</h3>
</div>
</router-link>
</div>
</div>
<div class="number-container">
<a class="previous" href="">« <span>previous</span></a>
1
2
3
4
...
10
<a class="next" href="#"> <span>next</span> »</a>
</div>
</template>
<script>
export default {
name : "ThirdSection",
props : ['datas3'],
}
</script>
singlepage.vue
this is same component i want show data each cart after i click on card.
<template>
<div class="content">
<div class="content-sidebar">
<SideBar />
</div>
<section class="main-content">
<div class="content-container">
<div class="content-img"> <img src="" alt=""></div>
<div class="content-text" >
<h2></h2>
<p></p>
</div>
</div>
</section>
</div>
</template>
<script>
import axios from 'axios'
import SideBar from '../../components/Sidebar.vue'
export default {
components :{
SideBar
},
//I have a problem here//
setup() {
const request = { RequestID : "16460"}
function getContent (){
axios.post('request url', request ,
{ headers : {
'Content-Type' : 'application/json',
}}
)
.then(function(response){
console.log(response);
})
}
getContent();
return {};
}
}
</script>
What you need is to create a route for the single page that has params in it, and parse them in the component to use them in the request.
See: https://router.vuejs.org/guide/essentials/passing-props.html
Register your routes with the desired component and specify a parameter:
{ path: '/user/:book_id', component: require("<singlepage.vue>"), props: true }
singlepage.vue:
<template>
<div class="content">
<div class="content-sidebar">
<SideBar />
</div>
<section class="main-content">
<div class="content-container">
<div class="content-img"> <img src="" alt=""></div>
<div class="content-text" >
<h2></h2>
<p></p>
</div>
</div>
</section>
</div>
</template>
<script>
import axios from 'axios'
import SideBar from '../../components/Sidebar.vue'
export default {
components :{
SideBar
},
props: ["book_id"],
//I have a problem here//
setup() {
const request = { RequestID : "16460"}
function getContent (){
axios.post('request url', request ,
{ headers : {
'Content-Type' : 'application/json',
}}
)
.then(function(response){
console.log(response);
})
}
getContent();
return {};
}
}
</script>
I'm trying to share data stored in a variable favorite_count within the Favorites component in Favorites.vue file. I want to share that data with the App Component in the App.vue file but I'm not able to. I would want that if I change the value of favorite_count in the Favorites component, it changes in the App Component. Done quite some research on the web but no success yet. Any ideas on what I could be doing wrong?
Favorites.vue file
<template>
<div class="row m-5">
<h3 class="col-8">Your Favorites</h3>
<div class="col-4">
<p class="pull-right m-1">Picks
<span >{{ favorite_count }}</span> / 5</p>
</div>
<hr>
</div>
</template>
<script>
export default {
name: 'favorites',
data() {
return {
favorite_count: 5,
}
},
methods: {
changeFavoriteCount() {
this.favorite_count = this.favorite_count + 2;
},
emitToParent (event) {
this.$emit('childToParent', this.favorite_count)
}
}
}
</script>
App.vue file
<template>
<div class="navbar navbar-expand-md navbar-dark bg-primary">
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav">
<li class="nav-item">
<router-link to="/favorites" class="btn btn-info">
Favorites ( {{ favorite_count }} )
</router-link>
</li>
</ul>
</div>
</div>
</template>
<script>
import Favorites from './components/Favorites.vue'
export default {
name: 'App',
components: {
Favorites
},
data () {
return {
favorite_count: 0,
}
}
}
</script>
If you are going to use <router-view> later in your application, I would suggest this solution
If you were going to include Favorites inside <template> in App.vue, you can use props:
1. Declare your 'shared' variable in the parent component (App.vue)
data () {
return {
favorite_count: 0,
}
},
2. Define props in your child component (Favorites.vue)
export default {
props: { favorite_count: Number },
...
}
3. Pass favorite_count as prop to Favorites
<template>
...
<Favorites :favorite_count="favorite_count" ... />
</template>
If you will need to update favorite_count - emit an event to parent component. More about it in Vue docs
Edit: Just to clarify: If you are going to update favorite_count from Favorites.vue, you need to emit an event to App.vue to avoid mutating props.
That also means you need to move your changeFavoriteCount() function to App.vue and apply a listener to your child component which will call this function:
// App.vue
<template>
...
<Favorites
#your-update-event="changeFavoriteCount"
:favorite_count="favorite_count" ...
/>
</template>
...
changeFavoriteCount(newVal) {
this.favorite_count = newVal;
},
change your Favourite.vue file like this
<template>
<div class="row m-5">
<h3 class="col-8">Your Favorites</h3>
<div class="col-4">
<p class="pull-right m-1">
Picks <span>{{ favorite_count }}</span> / 5
<button #click="changeFavoriteCount">Click me to change favorite_count</button>
</p>
</div>
<hr />
</div>
</template>
<script>
export default {
name: "favorites",
data() {
return {
favorite_count: 5,
};
},
methods: {
changeFavoriteCount() {
this.favorite_count = this.favorite_count + 2;
this.emitToParent();
},
emitToParent() {
this.$emit("childToParent", this.favorite_count);
},
},
};
</script>
and the App.vue file like this
<template>
<div class="navbar navbar-expand-md navbar-dark bg-primary">
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav">
<li class="nav-item">
<router-link to="/favorites" class="btn btn-info">
<Favorites #childToParent="updateFavorite" />
Favorites ( {{ favorite_count }} )
</router-link>
</li>
</ul>
</div>
</div>
</template>
<script>
import Favorites from './components/Favorites.vue'
export default {
name: 'App',
components: {
Favorites
},
data () {
return {
favorite_count: 0,
}
},
methods: {
updateFavorite(data) {
this.favorite_count = data;
},
},
}
</script>
I have Three components I want to trigger the show() method that is located in overlay.vue component, from the about.vue component
I am a beginner so I wish if I found some help from you guys
first is overlay.vue
<template>
<div class="overlay"></div>
</template>
<script>
export default {
name: "overlay",
methods: {
show() {
document.querySelector(".overlay").style.display = "block";
}
}
};
</script>
second is about.vue
<template>
<section class="about">
<div class="container">
<div class="row video">
<div class="col-lg-6 order-lg-1 order-2">
<img
class="dots img-fluid"
src="../assets/images/dots.svg"
alt="dots"
/>
<div class="video-wrapper">
<div class="video-img">
<img
class="img-fluid"
src="../assets/images/video.png"
alt="#"
/>
</div>
<div class="video-i">
<!-- ***************************************************************
***** this is where i want to trigger the method using this below a tag*****
*************************************************** -->
<i class="fas fa-play"></i>
</div>
</div>
</div>
</div>
</div>
</section>
</template>
The third which is the parent app.vue where I import about.vue and overlay.vue
<template>
<div id="app">
<overlay></overlay>
<about></about>
</div>
</template>
<script>
import overlay from "./components/overlay";
import about from "./components/about";
export default {
name: "App",
components: {
about,
overlay
}
};
</script>
You can do something like this, in your app.vue set a variable to show your overlay.vue or not. And then in the overlay.vue set a props that change the style of your component. This solution is not using a dom manipulation.
app.vue
<template>
<div id="app">
<overlay :show="show_overlay"></overlay>
<about></about>
</div>
</template>
<script>
import overlay from "./components/overlay";
import about from "./components/about";
export default {
name: "App",
data(){
return {
show_overlay:false
}
},
components: {
about,
overlay
}
};
</script>
overlay.vue
<template>
<div class="overlay" :style="[(this.show) ? 'display:block' : 'display:none']"></div>
</template>
<script>
export default {
name: "overlay",
props:{
show:{
default:false,
type:Boolean
}
}
};
</script>
because the two components are siblings and not imported in each other I coulden't use $ref and the best choice is to use Store
this is my store code
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
showOverlay: false
},
mutations: {
//showing the overlay
TOGGLE_OVERLAY(state, payload) {
state.showOverlay = payload;
}
},
actions: {
//showing the overlay
toggleOverlay(context, payload) {
context.commit("TOGGLE_OVERLAY", payload);
}
},
getters: {
getStatus: state => state.showOverlay
}
});
and here where i dispatch it. I dispatch it from about.vue or any other component as well
<a #click.prevent="toggleOverlay" href="#"><i class="fas fa-play"></i></a>
methods: {
toggleOverlay() {
this.$store.state.showOverlay = !this.$store.state.showOverlay;
this.$store.dispatch(
"toggleOverlay",
this.$store.state.showOverlay
);
}
}
I'have multiple auth laravel dashboard. When i login the default page redirect me to the client dashboard blade. When i write something in client blade.It does not show anything.I am using vuejs routers. Everything is working perfect. I tried to call component in that blade but it's still showing blank .I want to redirect to dashboard component.
Controller:
I tried it with the return url('url here') but still not working for me.
public function index()
{
return view('client');
}
Client Blade:
#extends('layouts.master')
#section('content')
<template>
<div class="container-fluid">
<client_details></client_details>
</div>
</template>
<script>
import clientdetails_header from "./clientdetails_header.vue";
export default {
data() {
return {
}
},
components: {
'client_details': clientdetails_header
},
mounted() {
console.log('Component mounted.')
}
}
</script>
#endsection
Master Blade:
<ul>
<li>
<router-link to="/clientdashboard" title="dashboard">
<span class="nav-link-text"><iclass="fal fa-user"></i>DashBoard</span>
</router-link>
</li>
</ul>
<div class="page-wrapper" id="app">
<div class="page-content">
<router-view>
</router-view>
</div>
</div>
App.js
let routes = [
{path: '/clientdashboard', component: require('./components/clientdashboard.vue').default},
]
const app = new Vue({
el: '#app',
router,
});
const router = new VueRouter({
mode: "history",
routes
})
ClientHeader:
<template>
<div class="row">
<div class="col-xl-12">
<!-- Collapse -->
<div id="panel-6" class="panel">
<div class="panel-hdr">
<h2>
Client Details
</h2>
</div>
<div class="panel-container show">
<div class="panel-content">
<div class="row">
<div class="col-md-2">
<span><b>Company name:</b></span> <br>
<span><b>Company ABN:</b></span> <br>
<span><b>Company address:</b></span> <br>
<span><b>Company phone:</b></span> <br>
<span><b>Company email:</b></span> <br>
</div>
<div class="col-md-10">
<ul style="list-style: none;" class="list-group list-group-flush">
<li style="text-decoration: none" v-for="todo in clientData">{{todo}}</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
user_id: this.$userId,
clientData: {}
}
},
methods: {
getClientDetails() {
axios.post(this.$path + 'api/getClientDetails', null,
{
params: {
'client_id': this.user_id,
}
})
.then(data => (
this.clientData = data.data));
}
},
mounted() {
this.getClientDetails();
console.log('Component mounted.')
}
}
</script>
I don't think it's a good idea trying to put Vue code directly inside blade.
I would suggest you create a Client.vue and put your code in it instead. Register it in your routes in app.js.
...
{path: '/client', component: require('./components/Client.vue')},
Then you can use the component in your Client blade.
<client></client>
I believe that would be a first step towards resolving this issue.
Load your app.js in <head> section of your blade.php
<script src="{{ asset('js/app.js') }}" async></script>\
Also create a div with id app there.
<body>
<div id="app"></div>
</body>
Create App.vue
<template>
<div>
<router-view />
</div>
</template>
<style scoped lang="scss">
</style>
Go to resources/js/app.js file, you will see laravel already put code to instantiate vue in there. Register your route etc.
But for me, I create a new folder resources/js/vue, put all vue related files (components, routes, filters, vuex, mixins, directives) there, create index.js and moved the code to initiate vue inside index.js.
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
import components from './components'
import directives from './directives'
import mixins from './mixins'
import filters from './filters'
Vue.use(router)
Vue.use(store)
Vue.use(components)
Vue.use(directives)
Vue.use(mixins)
Vue.use(filters)
new Vue({ router, store, render: h => h(App) }).$mount('#app')
export default {}
resources/js/app.js
require('./vue')
// I also initiate service-worker, firebase, and other non vue related
// javascripts in this file