Vuejs Sibling Component data passing with method - laravel

I have a Vuejs Laravel page with Parent and 2 child
In my Parent template I have
<template>
<div>
<h4>DATA ANALYSIS</h4>
<dataAnalysisGraph />
<br>
<hr>
<dataAnalysisTable />
</div>
and in my dataAnalysisGraph child component i have a method that send request everytime the dropdown changes.
//dataAnalysisGraph
<template>
<div>
<div>
<select class="form-control select" name="" #change="GenerateChart" v-model='chartType' >
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
</div>
<br><br><br>
<highcharts class="hc" :options="chartOptions" ref="chart"></highcharts>
</div>
</template>
<script>
export default {
data() {
return {
data: [],
}
},
methods: {
GenerateChart(){
this.axios
.get('api/'+this.chartType)
.then(response => {
this.data = response.data;
});
},
},
created() {
this.GenerateChart();
},
};
</script>
In my dataAnalysisTable child component. I want to get the this.data from dataAnalysisGraph component and pass it to the dataAnalysisTable and updates it every time the dropdown form dataAnalysisGraph component changes.
this is my dataAnalysisTable component currently
<template>
<div>
{{data}}
</div>
</template>
<script>
export default {
data() {
return {
data: [],
};
},
methods: {
},
created() {
},
};
</script>

You can emit an event inside dataAnalysisGraph returning this.data to the parent and connect this value using v-model to the dataAnalysisTable component. You can read more in the vuejs guide specifically in the "Usage with v-model" section.

Related

How to use the selection from my vue component in my blade file

I have build a vue-component which takes a list of objects and two criteria lists as props. The select lists are passed to two select inputs in the template. When either one is changed the list is filtered using the selected criteria. How do I get access to this filtered list in my blade file?
Here is my code.
Blade file:
<subjecttable-select :data-subjecttable="{{$subjectslessons->toJson()}}"
:data-departments="{{$departments->toJson()}}"
:data-subjects="{{$subjects->toJson()}}" #input="selectedsubjects">
</subjecttable-select>
#{{selectedsubjects}}
Vue-component
<template>
<div >
<div class="row mb-2 mx-2">
<form class="form-inline justify-content-between" >
<div class="form-group row mb-1">
<label class="col-auto col-form-label text-md-left" for="department">Leerjaar</label>
<div class="col-auto">
<select id= "department" class="form-control form-control-sm custom-select" v-model="department" #change="select()">
<option v-for="department_item in dataDepartments" :value="department_item['id']">
{{department_item["name"]}}
</option>
</select>
</div>
</div>
<div class="form-group row">
<label class="col-auto col-form-label text-md-leftt" for="subject">Vak</label>
<div class="col-auto">
<select id="subject" class="form-control form-control-sm custom-select" v-model="subject" #change="select()">
<option v-for="subject_item in dataSubjects" :value="subject_item['id']">
{{subject_item["description"]}}
</option>
</select>
</div>
</div>
<button class="btn-outline-primary" #click="reset()">Reset</button>
</form>
</div>
</div>
</template>
<script>
export default {
name:"subjecttable-select",
props: {
dataDepartments: { type: Array, required: true },
dataSubjects:{ type: Array, required: true},
dataSubjecttable: {type: Array, required: true },
value:{},
},
data() {
return {
selected:this.dataSubjecttable,
subject:"",
department:"",
}
},
methods:{
select(){
var item;
console.log(this.subject);
this.selected=[];
for(item of this.dataSubjecttable){
if(//get the subbejctlessons who are in the selected department
(this.department==="" || item["department_id"]===this.department) &&
//whose subject is the selected subject
(this.subject===""|| item["subject_id"]===this.subject)
){
this.selected.push(item);
}
}
this.$emit('input',this.selected);
},
reset(){
this.value = this.dataSubjecttable;
this.subject = "";
this.department="";
},
},
created(){
this.select();
},
filters: {
dateFilter(value){
var isDate = !isNaN(Date.parse(value));
if (isDate ){
var dateValue=new Date(value);
return dateValue.toLocaleDateString();
}
else{
return value;
}
}
},
};
</script>
app.js
Vue.component('subjecttable-select', require('./components/SubjectSelection.vue').default);
/**
* Next, we will create a fresh Vue application instance and attach it to
* the page. Then, you may begin adding components to this application
* or customize the JavaScript scaffolding to fit your unique needs.
*/
const app = new Vue({
el: '#app',
});
As you can see I emit an input event in my component but I have trouble accessing the value in the blade file.
Ideally, I think you want to do is load your SelecttableSelect component within another parent Vue component. This will allow you to trickle your events down to the parent component and use the data much more easily.
I have not tested this, but it's along the lines of what I would do to get started. You would need to format the output to your needs.
Lessons.vue
<template>
<div>
<!-- YOUR SELECT IS NOW DEFINED HERE, NOT IN THE BLADE FILE -->
<!-- Select -->
<subjecttable-select :data-subjecttable="dataSubjecttable"
:data-departments="dataDepartments"
:data-subjects="dataSubjects"
#input="updateResults"
>
</subjecttable-select>
<!-- End Select -->
<!-- Department -->
<div>
<h1>Department</h1>
<div v-if="results.department_id > 0">
<ul>
<li v-for="(value, index) in findElementById(dataDepartments, results.department_id)">
{{ index }} : {{ value }}
</li>
</ul>
</div>
</div>
<!-- End Department -->
<!-- Subject -->
<div>
<h1>Subject</h1>
<div v-if="results.subject_id > 0">
<ul>
<li v-for="(value, index) in findElementById(dataSubjects, results.subject_id)">
{{ index }} : {{ value }}
</li>
</ul>
</div>
</div>
<!-- End Subject -->
</div>
</template>
<script>
// import your select component
import SubjecttableSelect from './SubjecttableSelect';
export default {
components: {
// register the component
SubjecttableSelect,
},
props: {
dataDepartments: { type: Array, required: true },
dataSubjects:{ type: Array, required: true},
dataSubjecttable: {type: Array, required: true },
},
name: "Lessons",
data() {
return {
results: {
subject_id: 0,
department_id: 0,
},
}
},
methods: {
updateResults(data) {
this.results = data;
},
findElementById(element, id) {
return element.find(el => el.id === id);
}
},
}
</script>
<style scoped>
</style>
app.js
// register the new component
Vue.component('lessons', require('./components/Lessons.vue').default);
// subjecttable-select can now be imported within lessons
const app = new Vue({
el: '#app',
});
your.blade.php (please note the single quotes)
<lessons :data-subjecttable='#json($subjectslessons)'
:data-departments='#json($departments)'
:data-subjects='#json($subjects)'>
</lessons>

How to empty input fields from a pop-up window after submitting - Vue - laravel?

My page exist of a table where I can add new rows. If you want to add a new row a pop-up window appear where the new values can be added.
This new data is then saved to the database after submitting. If I again want to add a new row the input fields, they should be cleared.
The method I use, is working but isn't very clear.
Note: My code shows only a part of the input fields, to make it more clear. My pop-up window actually contains 20 input fields.
I would like to clear them all at once instead of clearing them one by one (like I am doing now).
Because I am already doing this for defining the v-model, pushing the new data to the database directly on the page and via post axios request.
Is there a cleaner way to do this?
Thanks for any input you could give me.
This is my code:
html part
<div class="col-2 md-2">
<button class="btn btn-success btn-sx" #click="showModal('add')">Add New</button>
<b-modal :ref="'add'" hide-footer title="Add new" size="lg">
<div class="row" >
<div class="col-4">
<b-form-group label="Category">
<b-form-input type="text" v-model="newCategory"></b-form-input>
</b-form-group>
</div>
<div class="col-4">
<b-form-group label="Name">
<b-form-input type="text" v-model="newName" placeholder="cd4"></b-form-input>
</b-form-group>
</div>
<div class="col-4">
<b-form-group label="Amount">
<b-form-input type="number" v-model="newAmount" ></b-form-input>
</b-form-group>
</div>
</div>
<div class="row" >
<div class="col-8">
</div>
<div class="col-4">
<div class="mt-2">
<b-button #click="hideModal('add')">Close</b-button>
<b-button #click="storeAntibody(antibodies.item)" variant="success">Save New Antibody</b-button>
</div>
</div>
</div>
</b-modal>
</div>
js part
<script>
import { async } from 'q';
export default {
props: ['speciedata'],
data() {
return {
species: this.speciedata,
newCategory: '',
newName: '',
newAmount:'',
}
},
computed: {
},
mounted () {
},
methods: {
showModal: function() {
this.$refs["add"].show()
},
hideModal: function(id, expId) {
this.$refs['add'].hide()
},
addRow: function(){
this.species.push({
category: this.newCategory,
name: this.newName,
amount: this.newAmount,
})
},
storeSpecie: async function() {
axios.post('/specie/store', {
category: this.newCategory,
name: this.newName,
amount: this.newAmount,
})
.then(this.addRow())
// Clear input
.then(
this.newName = '',
this.newCategory = '',
this.newAmount = '',
)
.then(this.hideModal('add'))
},
}
}
</script>
in your data of vuejs app , you have to set one object for displaying modal data like modalData then to reset data you can create one function and set default value by checking type of value using loop through modalData object keys
var app = new Vue({
el: '#app',
data: {
message:"Hi there",
modalData:{
key1:"value1",
key2:"value2",
key3:"value3",
key4:5,
key5:true,
key6:"val6"
}
},
methods: {
resetModalData: function(){
let stringDefault="";
let numberDefault=0;
let booleanDefault=false;
Object.keys(this.modalData).forEach(key => {
if(typeof(this.modalData[key])==="number"){
this.modalData[key]=numberDefault;
}else if(typeof(this.modalData[key])==="boolean") {
this.modalData[key]=booleanDefault;
}else{
// default type string
this.modalData[key]=stringDefault;
}
});
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
{{modalData}}
<br/>
<button #click="resetModalData">Reset Modal Data</button>
</div>
update : in your case :
data:{
species: this.speciedata,
modalData:{
newCategory: '',
newName: '',
newAmount:''
}
},
and after storing data :
storeSpecie: async function() {
axios.post('/specie/store', {
category: this.newCategory,
name: this.newName,
amount: this.newAmount,
})
.then(()=>{
this.addRow();
this.resetModalData();
this.hideModal('add')
}
},
In native Javascript you get the reset() method.
Here is how it is used :
document.getElementById("myForm").reset();
It will clear every input in the form.

Vue select onchange prop to child component not working

I have two components. Component 1:
<template>
<div>
<div class="form-group">
<label for="group">Category</label>
<select name="category" v-model="category" #change="setCategory(category)" class="form-control select-group" id="category">
<option value="1">Category 1</option>
<option value="2">Category 2</option>
</select>
</div>
<tags-multiselect :categoryId="category"></tags-multiselect>
</div>
</template>
<script>
export default {
data () {
return {
category: 1,
}
},
methods: {
setCategory(selected) {
this.category = selected;
},
},
}
</script>
Component 2:
<template>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card card-default">
<div class="card-header">Example Component</div>
<div class="card-body">
I'm an example component.
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['categoryId'],
data () {
return {
category_ids: this.categoryId
}
},
created() {
axios.post('/tags', { category_id: this.categoryId })
.then((response) => {
//
})
.catch((err) => {
})
}
}
</script>
When I select option from first component, I not update category_id on second component with other id I get only 1 id. And when I send axios request with category_id I send always only first param 1. How I can resolve this? How I can refresh the second component with new selected data from select field?
props need to be kebab case in the template.
Try <tags-multiselect :category-id="category"></tags-multiselect>
Also, you don't need to redefine props in the data. Get rid of category_ids in component 2, it will just confuse things.
Then, Vue makes it very easy to extract your state from your components. Shared state (categoryId) and operations on shared state ( axios.post(...) ) would be much happier in global scope, according to me !

How can I dynamically nest vue components?

I want to add one component inside other when user clicks a button. but how can we render the component in the virtual dom.
I tried using v-html but its not working.
Whats the best way to solve this issue?
export default {
data(){
return{
elements : {
hotel : '<hotel-form></hotel-form>'
},
}
},
methods:{
addHotel(){
console.log('add');
}
}
}
<template>
<div class="container" style="margin-top:300px;">
<div class="row" id="mainform">
<div v-for="item in elements">
<div v-html="item"></div>
</div>
</div>
<button #click="addHotel()">add hotel</button>
</div>
</template>
I would bind an array (hotels) to a <hotel-form> component tag via v-for. This way, no hotel-form components will be initially rendered, and then you can push an object (with any data to want bound to the hotel-form component) to the hotels array and the DOM will automatically render a new corresponding hotel-form component.
Here's a simple example:
Vue.component('hotel-form', {
template: '#hotel-form',
props: { id: Number, name: String },
});
new Vue({
el: '#app',
data() {
return { hotels: [], count: 0 }
},
methods: {
addHotel() {
this.hotels.push({ name: 'foo', id: this.count++ })
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.min.js"></script>
<div id="app">
<div id="mainform">
<hotel-form v-for="hotel in hotels" :key="hotel.id" v-bind="hotel">
</hotel-form>
</div>
<button #click="addHotel">add hotel</button>
</div>
<template id="hotel-form">
<div>
<h4>Hotel Form #{{ id }}</h4>
<div>{{ name }}</div>
</div>
</template>

Parsing dynamically loaded directives in Vue

I have a Vue component that makes a post request, and then outputs the returned html.
Sometimes, the html that is returned by the post contains Vue directives.
Is there a way to have Vue parse the returned html before it is output?
(In the longer term, I will rewrite this as a pure Vue solution, with the post request returning data rather than html. I'm after a short term solution if its possible).
EDIT:
Here's my stab based on thanksd's suggestion but I'm not sure how to bind the new Vue instance to an html element.
<template>
<div>
<input type="text" class="form-control" v-model="value" #change="getResults" ></input>
<div>
<template v-bind="results"></template>
</div>
</div>
</template>
<script>
import{eventHub} from '../utils/event.js'
export default {
data : function(){
return {
value : '',
results : {}
}
},
methods:{
getResults(){
if(this.value.length < 3){return;}
this.$http.post('/ajax/search',{search:this.value}).then((response)=>{
this.results = Vue({template:response.body});
});
},
},
}
After the post request returns you could create a new Vue instance, passing the html as the template and binding it to an element in your current Vue instance's template:
<template>
<div>
<input type="text" class="form-control" v-model="value" #change="getResults" ></input>
<div>
<div id="results"></div>
</div>
</div>
</template>
<script>
export default {
data() {
return { value: '' }
},
methods: {
getResults() {
if (this.value.length < 3) {
return;
}
this.$http.post('/ajax/search', { search: this.value }).then((response) => {
new Vue({ el: '#results', template: response.body });
});
}
}
}
</script>
Or as #Bert pointed out, you could add a <component> tag to your template and pass its definition via the is prop:
<template>
<div>
<input type="text" class="form-control" v-model="value" #change="getResults" ></input>
<component :is="results"/>
</div>
</template>
<script>
export default {
data() {
return {
value: '',
results: null
}
},
methods: {
getResults() {
if (this.value.length < 3) {
return;
}
this.$http.post('/ajax/search', { search: this.value }).then((response) => {
this.results = { template: response.body };
});
}
}
}
</script>

Resources