I using google places-api to get a single or list of places. for each place in the list of places I would like to get an additional data (such as website) - this is done from another api (places details).
The problem is that I can see all the data in google console but not in the DOM - only the data from the the first API is visible ( {{item.website}} property is empty)
here is my code:
<input type="text" class="form-control" id="searchPlace" v-on:keyup.enter="getPlaces()" placeholder="Type a name, address etc..." v-model="placeToSearch">
<div v-for="(item, key) in objPlaces" :key="key">
{{ item.name }} | {{item.place_id}} | {{item.rating}} | {{item.website}}
</div>
<script>
var myObject = new Vue({
el: "#app",
data: {
placeToSearch: "",
objPlaces: null,
},
methods: {
getPlaces() {
let places = null;
axios
.get('#Url.Action("GetPlace")', { params: { name: this.placeToSearch }})
.then((res) => {
places = res.data.results;
})
.finally(() => {
// iterate each place to get its website
places.forEach(function (el) {
axios
.get('#Url.Action("GetPlaceDetails")',{params: { placeId: el.place_id }})
.then((res) => {
el["website"] = res.data.result.website;
});
this.objPlaces = places;
console.log(this.objPlaces); // all the data is displayed
});
});
},
},
please note I am using server side to get the details from google api
You may find it easier to use async/await rather than the callback functions.
const myObject = new Vue({
el: "#app",
data() {
return {
placeToSearch: "",
objPlaces: null,
}
},
methods: {
async getPlaces() {
const res = await axios.get('#Url.Action("GetPlace")', {
params: { name: this.placeToSearch },
});
const places = res.data.results;
this.objPlaces = places.map(async (el) => {
const res = await axios.get('#Url.Action("GetPlaceDetails")', {
params: { placeId: el.place_id },
});
el.website = res.data.results.website;
return el;
});
},
},
});
Note 1: I haven't tested this, but the general idea is there.
Note 2: Missing try/catch to handle errors from the API.
You're missing the return statement inside data function. The data option should always be a function in the context of components which returns a fresh object.
data(){
return {
placeToSearch: "",
objPlaces: null,
}
}
You can read more about it from the documentation: https://v2.vuejs.org/v2/guide/components.html#data-Must-Be-a-Function
Related
I would like to create a simple web apps that can display all json data
and user can filter it?
here it my code
I am using Vue 2
index.html
<div id="app">
<input type="input" id="txt-search" v-model="mySearch">
<select>
<option :value="post.id" v-for="post in searchBook"
>{{post.id}}</option>
</select>
</div>
script.js
var json_data = 'jsonfile.json'
var app = new Vue({
el: '#app',
data: {
posts: null,
mySearch:''
},
created: function () {
this.fetchData()
},
methods: {
fetchData: function () {
var xhr = new XMLHttpRequest()
var self = this
xhr.open('GET', json_data)
xhr.onload = function () {
self.posts = JSON.parse(xhr.responseText)
}
xhr.send()
}
},
computed: {
searchBook() {
return this.mySearch ? this.posts.filter(post => {
return post.title.includes(this.mySearch);
})
: this.posts;
}
}
})
It only filter title data
post.title.includes(this.mySearch)
Is it posible to filter all json data like
post.*.includes(this.mySearch)
function filterArray(arr, keyword) {
if (!keyword) return arr;
return arr.filter(obj =>
Object.values(obj)
.some(value => typeof value === 'string' && value.includes(keyword))
);
}
Usage:
computed: {
searchBook() {
return filterArray(this.posts, this.mySearch);
}
}
It is but is a little complicated. First, you must extract all the values from the object and filter every record to check if it contains the searched phrase.
Example:
const posts = [
{title: 'title1', author:'author1', comment: 'comment1'},
{title: 'foo', author:'author2', comment: 'comment2'},
{title: 'title3', author:'bar', comment: 'comment3'},
]
const serchedPhrase = 'author';
const filteredData = posts.filter((e) => {
const values = Object.keys(e).map(val => e[val]);
const filteredValues = values.filter((el) => el.includes(serchedPhrase));
return filteredValues.length > 0;
});
console.log(filteredData);
I want to create a component based on ajax api response or data which include:
template
data
methods - there may be several methods
Remark: response or data is dynamic and it is not saved in file.
I have tried to generate and return result like :
<script>
Vue.component('test-component14', {
template: '<div><input type="button" v-on:click="changeName" value="Click me 14" /><h1>{{msg}}</h1></div>',
data: function () {
return {
msg: "Test Componet 14 "
}
},
methods: {
changeName: function () {
this.msg = "mouse clicked 14";
},
}
});
</script>
and do compile above code :
axios.get("/api/GetResult")
.then(response => {
comp1 = response.data;
const compiled = Vue.compile(comp1);
Vue.component('result-component', compiled);
})
.catch(error => console.log(error))
I got error on Vue.compile(comp1) -
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 they will not be parsed.
Thanks in advance
Your Api should return a JSON with every property required by a Vue component (name, data, template, methods), note that methods needs to be converted into an actual js function (check docs about that)
Vue.config.productionTip = false;
Vue.config.devtools = false;
new Vue({
el: '#app',
data() {
return {
apiComponent: { template: '<div>Loading!</div>' }
};
},
methods: {
loadApiComponent() {
setTimeout(() => {
this.buildApiComponent(JSON.parse('{"name":"test-component14","template":"<div><input type=\\\"button\\\" v-on:click=\\\"changeName\\\" value=\\\"Click me 14\\\" /><h1>{{msg}}</h1></div>","data":{"msg":"Test Componet 14 "},"methods":[{"name":"changeName","body":"{this.msg = \\\"mouse clicked 14\\\";}"}]}'));
}, 2000);
},
buildApiComponent(compObject) {
const {
name,
template,
data,
methods
} = compObject;
const compiledTemplate = Vue.compile(template);
this.apiComponent = {
...compiledTemplate,
name,
data() {
return { ...data
}
},
methods: methods.reduce((c, n) => {
c[n.name] = new Function(n.body);
return c;
}, {})
};
}
},
mounted() {
this.loadApiComponent();
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<component :is="apiComponent" />
</div>
After reading many examples and the documentation from Vue, Vuex and Vue-Router I did this project: https://jsfiddle.net/junihh/30wda1em/5/
Even all goes fine when I try to load a row from Post section, the value come empty from the Vuex store. Here the component:
const PostContent = Vue.component('postcontent', {
data() {
return {
post: this.$store.state.currentPost
// post: {
// title: 'The title',
// content: 'The content for test.'
// }
}
},
template: getTemplate('#tpl-postcontent')
});
Here the component that update the state.currentPost value and call the "postcontent" component.
const Posts = Vue.component('posts', {
data() {
return {
url_path: '/posts/content',
rows: this.$store.state.dataAll
}
},
methods: {
openPost: function(e)
{
let rowID = e.currentTarget.getAttribute('data-rowid');
let dataAll = this.$store.state.dataAll;
let currentPost = dataAll.filter(row => (row.id == rowID))[0];
this.$store.state.currentPost = currentPost;
}
},
template: getTemplate('#tpl-posts')
});
Any help here? I'm stuck on that issue.
You need to use a computed property to gather the information from your store with reactivity:
const PostContent = Vue.component('postcontent', {
computed: {
post() {
return this.$store.state.currentPost
}
},
template: getTemplate('#tpl-postcontent')
});
Also try to avoid mutating state outside mutation handler. You can add a mutation to set your currentPost like this:
<template id="tpl-posts">
...
<li v-for="row in rows" :key="row.id">
<router-link :to="url_path" #click.native="openPost(row.id)">
{{ row.title }}
</router-link>
</li>
...
</template>
const Posts = Vue.component('posts', {
//...
methods: {
openPost: function(id)
{
this.$store.commit('SET_CURRENT_POST', id)
}
},
template: getTemplate('#tpl-posts')
});
const store = new Vuex.Store({
state: {
dataAll: {},
currentPost: {}
},
mutations: {
SET_CURRENT_POST: function(state, id) {
let post = state.dataAll.find(data => data.id === id)
state.currentPost = post
}
}
});
fiddle
I have a simple search input:
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.js" type="text/javascript"></script>
<div id="app">
<input type="text" v-model="search">
</div>
When you enter a value it will query a remote API to fetch and then display the data:
...
new Vue({
el: '#app',
data: {
people: []
search: ''
},
watch: {
search() {
// Rate limit
this.debouncedSearch()
}
},
methods: {
debouncedSearch: _.debounce(() => {
console.log(this)
// self = this
// io.socket.put('/search', this.search, (people, jwres) => {
// self.people = people
// })
}, 500)
},
created(){
this.people = locals.people
}
})
The problem here is that console.log(this) returns undefined.
I have used this in another application and it works so battling to understand why not here.
Is there something that I have done wrong there, seems to be correct but no matter what I try I cannot access the scope of the Vue application in that debouncedSearch method?
To solve the issue just replace the arrow function with function() {}
Replace this:
methods: {
debouncedSearch: _.debounce(() => {
console.log(this) // undefined
}, 500)
},
With This:
methods: {
debouncedSearch: _.debounce(function () {
console.log(this) // not undefined
}, 500)
},
Hope to help others took me a lot of time to figure it out.
Pretty sure your problem is the use of the this-preserving function style. You need a way to refer to the Vue object you're creating (it's not this at the point your debounce function is defined). One way would be to do
const vm = new Vue({
el: '#app',
...
methods: {
debouncedSearch: _.debounce(() => {
console.log(vm)
// io.socket.put('/search', vm.search, (people, jwres) => {
// vm.people = response
// })
}, 500)
},
So I have a template .vue file:
<template>
<div id="app">
<textarea v-model="input" :value="input" #input="update"></textarea>
<div v-html="compiledMarkdown"></div>
</div>
</template>
<script>
var markdown = require('markdown').markdown;
export default {
name: 'app',
data() {
return {
input: '# Some default data'
}
},
mounted: function () {
this.$nextTick(function () {
this.$http.get(window.location.pathname + '/data').then((response) => {
this.input = response.body.markdown;
}) })
},
computed: {
compiledMarkdown: function() {
this.$http.post(window.location.pathname, {
"html": markdown.toHTML(this.input)}).then(function() {
},function() {
});
return markdown.toHTML(this.input);
}
},
methods: {
update: function(e) {
this.input = e.target.value
}
}
}
</script>
In the mounted function I am trying to set input equal to the response of an HTTP request, but when you view this file this.input is still the same as it was initially declared. How can I change this.input inside the compiledMarkdown function to be this.input in the mounted function. What other approaches might I take?
You can not call a async method from a computed property, you can use method or watcher to run asynchronous code, from docs
This is most useful when you want to perform asynchronous or expensive operations in response to changing data.
You have to ran that relevant code when input changes, like following:
var app = new Vue({
el: '#app',
data: {
input: '# Some default data',
markdown : ''
},
methods: {
fetchSchoolData: function (schoolId) {
var url = this.buildApiUrl('/api/school-detail?schoolId=' + schoolId);
this.$http.get(url).then(response => {
this.schoolsListData = response.data;
}).catch(function (error) {
console.log(error);
});
},
},
mounted: function () {
this.$nextTick(function () {
this.$http.get(window.location.pathname + '/data').then((response) => {
this.input = response.body.markdown;
})
})
},
watch: {
// whenever input changes, this function will run
input: function (newInput) {
this.$http.post(window.location.pathname, {
"html": markdown.toHTML(this.input)}).then(function() {
},function() {
this.markdown = markdown.toHTML(this.input);
});
}
},
Have a look at my similar answer here.