vue.js 2.0 load component dynamically in laravel 5.3 - laravel

Is it possible to load a component dynamically when clicking a list item in vuejs 2.0 and laravel 5.3? I have a standard Laravel app but on one section of the site (shows reports) I want to turn this page into a single page application using vuejs. I load the default report in using a vue component and all works well, like so:
<div class="col-md-3 col-sm-6">
<div class="card-box">
<h2>Reports</h2><br>
<ul>
<li>Daily</li>
<li>Weekly</li>
<li>Season</li>
<li>Label</li>
<li></li>
</ul>
</div>
</div>
<div class="col-md-9 col-sm-6">
<div class="card-box main">
<reports-homepage></reports-homepage>
</div>
</div>
What I'm trying to achieve is when one of the li items is clicked the <reports-homepage></reports-homepage> is changed dynamically according to which li is pressed. So it could become <reports-daily></reports-daily> for example.
I've tried putting a #click on the li component and catching that in a script below in the blade section but that gives me the following error:
[Vue warn]: Templates should only be responsible for mapping the state to the UI. Avoid placing tags with side-effects in your templates, such as <script>.

As pointed in the official documentation you can use dynamic components. In your case it could look something like this:
HTML
<div id="app">
<div class="col-md-3 col-sm-6">
<div class="card-box">
<h2>Reports</h2><br>
<ul>
<li #click="setComponent('daily')">Daily</li>
<li #click="setComponent('weekly')">Weekly</li>
<li #click="setComponent('season')">Season</li>
<li #click="setComponent('label')">Label</li>
</ul>
</div>
</div>
<div class="col-md-9 col-sm-6">
<div class="card-box main">
<component v-bind:is="currentComponent"></component>
</div>
</div>
</div>
JS
var vm = new Vue({
el: '#app',
data: {
currentComponent: 'daily'
},
components: {
'daily': { /* component object */ },
'weekly': { /* component object */ },
'season': { /* component object */ },
'label': { /* component object */ }
},
methods: {
setComponent: function (component) {
this.currentComponent = component;
}
}
})
And that should do what you are trying to achieve. Let me know if it helped!

Related

Vue.js 3 - Add list items from component dynamically

I am new to vue, I found some examples but I could not reproduce in my situation.
I have some inputs user will fill out and once it clicks the button, the information will populate a list of items. I am trying to crete < li > with a OrderListItem component. But I am getting this error:
runtime-core.esm-bundler.js?5c40:217 Uncaught ReferenceError: machines is not defined
at eval (OrderListItem.vue?fb66:14)
Here is the form
<template>
<div>
<div class="flex flex-col mb-4 md:w-full m-1">
<label for="planter_model_id">Planter</label>
<select v-model="registration.planter_model_id" name="planter_model_id">
<option disabled selected>Planter</option>
<option v-for="planter in planters" :key="planter" :value="planter.id">{{planter.brand.name}} {{planter.name }}</option>
</select>
</div>
<div class="flex flex-col mb-4 md:w-full m-1">
<label class=" for="lines">Lines</label>
<Input v-model="registration.lines" type="number" name="lines" />
</div>
<Button type="button" #click="addItemOrder">Add</Button>
</div>
<div>
<ul>
<OrderListItem :machines="machines" />
</ul>
<div>
</template>
Here is my code
export default {
components: {
OrderListItem,
Button,
},
props: {
planters:{
type:Array,
required: true
},
},
data() {
return {
machines: [], //this what I wish to be passed to OrderListItem component
registration:{
lines:null,
planter_model_id:null,
},
}
},
methods:{
addItemOrder(){
let item = {}
item.planter_model_id = this.registration.planter_model_id
item.lines = this.registration.lines
this.machines.push(item)
}
}
};
And here is my OrderListItem component that I created on another file:
<template>
<li v-for="machine in machines" :key="machine">
<div class="flex-initial w-3/5">
<p>Planter: {{machine.planter_model_id}}</p>
<p>Lines: {{machine.lines}}</p>
</div>
</li>
</template>
<script>
export default {
name: 'OrderListItem',
props:{
machines,
}
}
</script>
I don’t know if it is relevant but I am using vue3 and laravel.
I am very newbie here and from what I learned I think if machines array was a prop I would be able to access it on my component. But I will dynamically add a and remove itens from machines array and I think props receive data from server so It should not be declared there, that why I declared inside data().
Thanks =)
You should define the prop by adding its type and default value :
export default {
name: 'OrderListItem',
props:{
machines:{type:Array,default:()=>[]},
}
}

Don't see a div into a vue component

I have a vue component with a div and a button and into another div I have two components
<script>
export default {
name: 'excursion-backend-component',
methods:{
doRedirection: function () {
window.location = APP_URL+"/excursiones/create";
}
},
mounted() {
console.log(APP_URL+"/excursiones/create");
console.log("aaaa");
}
}
</script>
<template>
<div class="container">
<h3>Adicionar excursión</h3>
<div style="text-align: right">
<a class="btn btn-primary" :href="doRedirection"><i class="fa fa-plus"></i> Adicionar</a>
</div>
<br>
<div>
<excursion-list-component></excursion-list-component>
<excursion-add-component></excursion-add-component>
</div>
</div>
</template>
But I don't see the button on the navigator.
What is wrong?
Here is how I see the page
:href="" expecting string with url to navigation, but you using method.
Use #click instead :href, if you want to use method.
<a #click="doRedirect">... </a>
Or, may be, move it to computed block
computed: {
excursionesUrl () {
return APP_URL+"/excursiones/create";
}
}

Vuejs preserve element attributes

My case looks like this:
Laravel template:
<div class="block-id" is="Header">
<span>ID #3265872</span>
<ul class="tools">
<li>History</li>
<li>TOP</li>
</ul>
<button>Check</button>
</div>
The vuejs component looks just the same
<template>
<div class="block-id">
<span>ID #{{ids.id}}</span>
<ul class="tools">
<li><a>History</a></li>
<li><a>TOP</a></li>
</ul>
<button>Check</button>
</div>
</template>
<script>
import {mapGetters} from 'vuex';
export default {
name: "Header",
computed: {
...mapGetters('global', [
'ids'
])
}
}
</script>
The problem is that when I render the component the href attribute is gone. So is there any way to preserve the href attribute in a element?

Passing the Div id to another vue component in laravel

I created a simple real-time chat application using vue js in laravel.
I am having a problem with the automatic scroll of the div when there is a new data.
What I want is the div to automatically scroll down to the bottom of the div when there is a new data.
Here is my code so far.
Chat.vue file
<template>
<div class="panel-block">
<div class="chat" v-if="chats.length != 0" style="height: 400px;" id="myDiv">
<div v-for="chat in chats" style="overflow: auto;" >
<div class="chat-right" v-if="chat.user_id == userid">
{{ chat.chat }}
</div>
<div class="chat-left" v-else>
{{ chat.chat}}
</div>
</div>
</div>
<div v-else class="no-message">
<br><br><br><br><br>
There are no messages
</div>
<chat-composer v-bind:userid="userid" v-bind:chats="chats" v-bind:adminid="adminid"></chat-composer>
</div>
</template>
<script>
export default {
props: ['chats','userid','adminid'],
}
</script>
ChatComposer.vue file
<template>
<div class="panel-block field">
<div class="input-group">
<input type="text" class="form-control" v-on:keyup.enter="sendChat" v-model="chat">
<span class="input-group-btn">
<button class="btn btn-primary" type="button" v-on:click="sendChat">Send Chat</button>
</span>
</div>
</div>
</template>
<script>
export default{
props: ['chats','userid','adminid'],
data() {
return{
chat: ''
}
},
methods: {
sendChat: function(e) {
if(this.chat != ''){
var data = {
chat: this.chat,
admin_id: this.adminid,
user_id: this.userid
}
this.chat = '';
axios.post('/chat/sendChat', data).then((response) => {
this.chats.push(data)
})
this.scrollToEnd();
}
},
scrollToEnd: function() {
var container = this.$el.querySelector("#myDiv");
container.scrollTop = container.scrollHeight;
}
}
}
</script>
I am passing a div id from the Chat.vue file to the ChatComposer.vue file.
As you can see in the ChatComposer.vue file there is a function called scrollToEnd where in it gets the height of the div id from Chat.vue file.
When the sendchat function is triggered i called the scrollToEnd function.
I guess hes not getting the value from the div id because I am getting an error - Cannot read property 'scrollHeight' of null.
Any help would be appreciated.
Thanks in advance.
the scope of this.$el.querySelector will be limited to only ChatComposer.vue hence child component can not able to access div of parent component #myDiv .
You can trigger event as below in ChatComposer
this.$emit('scroll');
In parent component write ScollToEnd method and use $ref to assign new height
<chat-composer v-bind:userid="userid" v-bind:chats="chats" v-bind:adminid="adminid" #scroll="ScollToEnd"></chat-composer>
..

Display data in a Vue component with ajax

I seem to be misunderstanding how to pass data to a Vue.js component with an ajax call.
My understanding of how this should work:
I need to create an empty object called campaigns in the data section of my component.
Then call method "fetchCampaigns" on page ready to replace the data object.
fetchCampaign method completes an AJAX call and inside of the success callback use this.$set('campaigns', campaigns) to replace the empty campaign object with the newly returned campaign object
Use v-for on the template to iterate through the campaign object and access values with #{{campaign.type}}
My html (I am use vue router, vue resource and laravel blade) :
<router-view></router-view>
<template id="campaignBlock" v-for="campaign in campaigns">
<div class="row">
<div class="block">
<div class="block-title">
<h2>Type: <em>#{{campaign.id}}</em></h2>
</div>
<div class="row"><!-- Grid Content -->
<div class="hidden-sm hidden-xs col-md-4 col-lg-4">
<h2 class="sub-header">#{{campaign.title}}</h2>
</div>
</div>
</div><!-- END Grid Content -->
</template>
Vue component
Vue.component('app-page', {
template: '#campaignBlock',
data: function() {
return{
campaigns: []
}
},
ready: function () {
this.fetchCampaigns();
},
methods: {
fetchCampaigns: function () {
var campaigns = [];
this.$http.get('/retention/getCampaigns')
.success(function (campaigns) {
this.$set('campaigns', campaigns);
})
.error(function (err) {
campaigns.log(err);
});
},
}
})
This is the result of my ajax call from console:
{"campaigns":[{"id":1,"user_id":2,"target_id":1,"name":"Test Campaign","description":"This is a test Campaign","target":"Onboarding","created_at":"-0001-11-30 00:00:00","updated_at":"-0001-11-30 00:00:00","deleted_at":null}]}
I'm not sure why I can't get my vue component to recognize the new data. Anyone see what I'm missing? TIA
Turns out that v-for="campaign in campaigns" should not go on the template tag, but inside of it.
So this:
<template id="campaignBlock" v-for="campaign in campaigns">
<div class="row">
Should be changed to this:
<template id="campaignBlock">
<div class="row" v-for="campaign in campaigns">

Resources