vue2 call a parent function using $emit from component - methods

i'm trying to call a parent methods from child component, but it seems not working.. here the code:
index.html
<div class="percorso"v-on:removeall="pathlengthTozero()">
</div>
component
Vue.component('lista-percorso', {
template:`
<div class="col-lg-2 col-xs-2">
<div class="removeall pull-right" v-on:click="removeall()"></div>
</div>`,
methods:{
removeall : function(){
this.listaSelezionati = [];
this.$emit('removeall');
}
}
parent method
pathlengthTozero : function(){
il_tuo_percorso = [''];
}
seems that "pathlengthTozero" is not called on emit which is the correct way to use it?

You need to put this v-on:removeall="pathlengthTozero" to the component <lista-percorso> like below,
<lista-percorso v-on:removeall="pathlengthTozero"></lista-percorso>
and this.$emit will able to fire the parent method.
Sample Demo:
Vue.component('lista-percorso', {
template:`
<div class="col-lg-2 col-xs-2">
<div class="removeall pull-right" v-on:click="removeall()">xxxxxxxxxx</div>
</div>`,
methods:{
removeall : function(){
this.listaSelezionati = [];
this.$emit('removeall');
}
}
})
var App = new Vue({
el: '#app',
methods:{
pathlengthTozero : function(){
alert('hello');
il_tuo_percorso = [''];
}
}
});
<script src="https://unpkg.com/vue"></script>
<div id="app">
<div class="percorso" ></div>
<lista-percorso v-on:removeall="pathlengthTozero"></lista-percorso>
</div>

you should put the event listener on the child conponent where it is used
<div class="percorso">
<lista-percorso v-on:removeall="pathlengthTozero"></lista-percorso>
</div>

Related

Data not showing in my console.log in vue

In my vue app I have 2 methods, one method gets some data from my laravel backend and the second one needs to be able to grab it so that I can use it in that method.
What I'm struggling with is that the second method isn't grabbing the data.
Here is my code
<template>
<app-layout>
<div class="content-wrapper" style="margin-left: 0;">
<div class="content">
<div class="container">
<div class="row pt-5">
<div class="col-lg-12">
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-lg-12">
Some data will show here
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</app-layout>
</template>
<script>
import AppLayout from '#/Layouts/AppLayout'
export default {
components: {
AppLayout,
},
data() {
return {
testData: ''
}
},
methods: {
firstMethod() {
axios.get('/api/get-data').then(response => {
this.testData = response.data;
});
},
secondMethod(){
console.log(this.testData);
}
},
mounted() {
this.firstMethod();
this.secondMethod();
}
}
</script>
your running both function in mount function so both run at same time and secondMethod() executed 1st at that time your this.testData is not set so you can use async and await to wait to finish firstMethod() then run secondMethod()
which will be like below code
<template>
<app-layout>
<div class="content-wrapper" style="margin-left: 0">
<div class="content">
<div class="container">
<div class="row pt-5">
<div class="col-lg-12">
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-lg-12">
Some data will show here
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</app-layout>
</template>
<script>
import AppLayout from "#/Layouts/AppLayout";
export default {
components: {
AppLayout,
},
data() {
return {
testData: "",
};
},
methods: {
async firstMethod() {
const { data } = await axios.get("/api/get-data");
this.testData = data;
},
secondMethod() {
console.log(this.testData);
},
},
async mounted() {
await this.firstMethod();
this.secondMethod();
},
};
</script>
You can try calling firstMethod in created() hook instead of mounted. In my opinion you do not need a method for modifying incoming data. Use watch instead:
watch: {
// whenever question changes, this function will run
testData: function (newValue) {
// do what transformation you need here
}
}
Watch hooks run when value of variable changes, so it should run when it is assigned.
The problem that you dont see the console log is because even if you execute first the first method, it's actually executed after the second method because takes more time to be resolved.
Try the below please, inside first method i added after then to execute the second method which means that that the first method would be resolved, thus we will have the api response.
In case you continue seeing nothing from console log then there is an issue with the api endpoint.
mounted() {
this.firstMethod();
},
methods: {
firstMethod() {
axios.get('/api/get-data').then(response => {
this.testData = response.data;
this.secondMethod();
});
},
secondMethod(){
console.log(this.testData);
}
},

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>

Attempting to load a Vue component and populate it with Ajax json content in Laravel

I am new to Laravel and a complete noob to Vue. I searched many other Laravel/Vue posts but none seemed to be similar enough to get me to a solution. When attempting to load this component on my view I receive the following error
app.js:32654 [Vue warn]: Property or method "features" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
Please let me know what I am missing.
chrome.google.com/webstore/detail/vuejs-devtools/ shows that Vue is loaded
I would like to load data from an ajax call to my vue component. that can be updated on the fly by event handler
App.js
window.Vue = require('vue');
Vue.component('Example', require('./components/Example.vue'));
const app = new Vue({
el : '#app',
});
$(document).ready(function() {
$.ajaxSetup({
headers : {
'X-CSRF-TOKEN' : $('meta[name="csrf-token"]').attr('content')
}
});
})
Example.vue
<template>
<div class="container projects-container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Example Component</div>
<div class="panel-body">
<h1>I'm an example component!</h1>
<ul class="list-group">
<li class="list-group-item" v-for="feature in features">
{{ feature.properties.name }}
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
console.log('Component mounted.')
},
}
</script>
bladefile
<head>
<meta name="csrf-token" content="INMA4kLlG32gfhf4Z3BBGIFxitrVCWzzqgqPfooEj">
// and yes Vue is loaded
</head>
<body>
<div id="app">
<example></example>
</div>
...
<script>
Vue.component('example',{
template: 'Example',
})
//returns the JSON listed below
someOtherObject.addListener('click', function(e) {
$.ajax({
url:json,
method:'GET',
success:function(msg){
app.data = JSON.parse(msg);
}
})
})
</script>
JSON
{
"type":"FeatureCollection",
"features":[
{
"type":"Feature",
"geometry":{
"type":"Point",
"coordinates":[
-117.155083,
33.569672
]
},
"properties":{
"heading":null,
"face":"South",
"status":"1",
"name":"MEADOWLARK ",
"type":"Single Family Home",
"client_id":"26",
"client_name":"Pulte Homes",
"city_name":"French Valley"
}
},
{
"type":"Feature",
"geometry":{
"type":"Point",
"coordinates":[
-117.151390,
33.543981
]
},
"properties":{
"heading":null,
"face":"South",
"status":"1",
"name":"Testing Project",
"type":"Single Family Home",
"client_id":"83",
"client_name":"Testing Account",
"city_name":"Temecula Valley"
}
},
{
"type":"Feature",
"geometry":{
"type":"Point",
"coordinates":[
-117.223720,
33.571522
]
},
"properties":{
"heading":null,
"face":"South",
"status":"1",
"name":"Oak Ridge",
"type":"Single Family Home",
"client_id":"98",
"client_name":"Woodside 05S LP",
"city_name":"Beaumont"
}
}
]
}
The features array must be declared either as a prop or part of the component data.
As a prop:
<template>
<div class="container projects-container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Example Component</div>
<div class="panel-body">
<h1>I'm an example component!</h1>
<ul class="list-group">
<li class="list-group-item" v-for="feature in features">
{{ feature.properties.name }}
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
console.log('Component mounted.')
},
props: ['features']
}
</script>
As component data:
<template>
<div class="container projects-container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Example Component</div>
<div class="panel-body">
<h1>I'm an example component!</h1>
<ul class="list-group">
<li class="list-group-item" v-for="feature in features">
{{ feature.properties.name }}
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
console.log('Component mounted.')
},
data: function() {
return {features: []}
}
}
</script>
If you use a prop, you'll need to bind the prop in the template. If you use component data, you'll need to update the success callback function in the AJAX request to correctly update the data of the component rather than the app as a whole.
Also, beware of the reactivity caveats for loading new data, as outlined in the docs.
EDIT: Full example solution using component data
Here's a working example using a simplified version of your example component. Note that to do this I used the ref attribute so that the component could be addressed directly. Using refs is described in the docs as an "escape hatch" that should only be used when strictly necessary. A better pattern might be an external store, such as Vuex.
Vue.component( "example", {
template: '<div><h1>Example Component!</h1><ul v-if="features.length"><li v-for="feature in features">{{ feature.name }}</li></ul></div>',
data: function() {
return { features : [] }
}
});
var app = new Vue( { el: '#app' });
//A function to represent the AJAX callback
var doMockAjax = function() {
var mockData = [{name: 'One'},{name: 'Two'},{name: 'Three'}];
app.$refs.example.features = mockData;
}
document.getElementById('load-data').addEventListener('click', function(e) {
//This simulates the ajax callback by populating the array asynchronously
setTimeout(doMockAjax,1000);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<example ref="example"></example>
</div>
<button id="load-data">Load</button>

Vuejs 2 display template twice IE and Safari laravel with blade

I made an autocomplete component and it works well in Chrome, but not in IE and Safari.
It displays the template twice in IE and Safari. It works but it shows the template in addition to the rendered HTML. See the image.
What did I do wrong?
<div id="main">
<autocomplete></autocomplete>
</div>
<template id="autocomplete">
<div>
<div class="col">
<section class="box clr1">
<div>
<div>
<input type="text" placeholder="Welk product zoekt U?" v-model="query" v-on:keyup="autoComplete" class="form-control">
<div class="panel-footer" v-if="results.length">
<ul class="list-group">
<li class="list-group-item" v-for="result in results">
<span style="text-decoration: underline;cursor:pointer" v-on:click="showDetail(result.id)">#{{ result.title }}</span>...
<div class="col">
<section class="box clr1">
<div>
<div v-for="detail in resultdetail">
<h1>#{{ detail.title }}</h1>
<h2>#{{ detail.page_title }}</h2>
<p v-html="detail.description"></p>...
Vue.component('autocomplete', {
template: '#autocomplete',
data: function () {
return {
show: false,
query: '',
results: [],
resultdetail: []
}
},
methods: {
autoComplete: function () {
this.results = [];
if (this.query.length > 1) {
axios.get('/getproductjson/' + this.query + '/0')
.then(function (response) {
this.results = response.data
}.bind(this))...
showDetail: function (productId) {
if (productId > 0) {
this.show = true;
this.resultdetail = [];
axios.get('/getproductjson/loremipsumdipsum/'+productId)
.then(function (response) {
this.resultdetail = response.data
}.bind(this))...
}
});
new Vue({
el: '#main'
});
Result:
Internet explorer does not support the template tag.
What you're seeing in Internet Explorer is your instantiated Vue, and, since IE doesn't implement template it just shows your template on screen.
In IE if you want to store your template in the DOM you typically have to use the a script template.
<script type="text/x-template" id="autocomplete">
...
</script>

How to make Ember controller handle server validation errors?

I use RESTAdapter with Ember Data. How to make Ember controller handle server validation errors? I think this code should be in the line "console.log(todo.errors);" but I have no idea how to achieve rendaring needed template...
App.TodosRoute = App.AuthenticatedRoute.extend({
model: function () {
return App.Todo.find();
}
});
App.TodosController = Ember.ArrayController.extend({
createTodo: function(todo) {
var data = this.getProperties('title', 'priority', 'due_date');
var todo = App.Todo.createRecord(data);
var self = this;
todo.on('becameInvalid', function(todo) {
// show errors on the form. code goes here
console.log(todo.errors);
});
todo.on('didCreate', function() {
// render list. code goes here
self.set('title', '');
self.set('priority', '');
self.set('due_date', '');
});
todo.save();
}
});
<script type="text/x-handlebars" data-template-name='todo/_edit'>
{{input type='number' class="input" placeholder="Priority" value=priority}}
{{input class="input" placeholder="What needs to be done?" value=title}}
{{input type='date' class="input" placeholder="Due date" value=due_date}}
</script>
<script type="text/x-handlebars" data-template-name="todos">
<section id="todoapp">
<header id="header">
{{partial 'todo/edit'}}
<button {{action "createTodo"}}>Save</button>
</header>
<section id="main">
<ul id="todo-list" class="sortable">
{{#each controller itemController='todo'}}
{{#unless isNew}}
<li {{bindAttr class="isEditing:editing"}} data-id="{{unbound id}}">
{{#if isEditing}}
{{partial 'todo/edit'}}
<button {{action "saveTodo"}} class="save-button">Save</button>
{{else}}
<label>{{priority}}</label>
<label>{{title}}</label>
<label>{{date due_date}}</label>
<button {{action "editTodo"}} class="edit-button">Edit</button>
<button {{action "removeTodo"}} class="destroy">Delete</button>
{{/if}}
</li>
{{/unless}}
{{/each}}
</ul>
</section>
</section>
</script>
Finally I got code like this:
App.TodosNewController = Ember.ObjectController.extend({
create: function() {
var data = this.getProperties('title', 'priority', 'due_date');
var todo = App.Todo.createRecord(data);
var self = this;
todo.on('becameInvalid', function(todo) {
self.set('model', todo);
});
todo.on('didCreate', function() {
self.set('priority', '');
self.set('title', '');
self.set('due_date', '');
self.transitionToRoute('todos');
});
todo.save();
}
});
Hope, it will help somebody

Resources