Use data from axios get as prop for another Vue file - javascript

I have some code which resolves the ip address of a computer to a lat / long, like so
ip_resolve.vue
<script>
const axios = require('axios').default;
const ipRegex = /ip=(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})$/gmi
export default {
name: 'ip',
props: {
ip: String,
lat: String,
long: String
},
mounted () {
axios.get('https://www.cloudflare.com/cdn-cgi/trace')
.then(response => (
this.ip = ipRegex.exec(response.data)[1]
)
.then(
axios.get('https://cors-anywhere.herokuapp.com/http://api.ipstack.com/'+this.ip+'?access_key=<key>')
.then( response => (
this.lat = response.data.latitude,
this.long = response.data.longitude
)
)
)
)
}
}
</script>
I want to "return" the lat / long to App.Vue, where it will pass Lat/Long as props to "Weather.js"
App.Vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<ip></ip>
<Weather lat={{lat}} long={{long}} />
</div>
</template>
<script>
import Weather from './components/Weather.vue'
import ip from './components/ip_resolve.vue'
export default {
name: 'App',
components: {
Weather,
ip
}
}
</script>
I've read a little bit about $emit, but I am unfamiliar with the design paradigm and don't know how to implement it. Can someone offer me some best practices here?
Thanks,

In your ip_resolve.vue use this to emit the event after getting the data from axios:
this.$emit('response', {
lat: response.data.latitude,
long: response.data.longitude
}
And then in your App.vue:
<ip #response="onResponse"></ip>
<Weather :lat="lat" :long="long" />
and inside <script> in App.vue:
export default {
name: 'App',
data() {
return {
lat: 0,
long: 0
}
},
components: {
Weather,
ip
},
methods: {
onResponse($event) {
this.lat = $event.lat
this.long = $event.long
}
}
}

You're nearly there.
This is how you can emit the data on your ip component:
<script>
const axios = require('axios').default;
const ipRegex = /ip=(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})$/gmi
export default {
name: 'ip',
props : {
ip: String
},
mounted () {
axios.get('https://www.cloudflare.com/cdn-cgi/trace')
.then(function(response) {
this.ip = ipRegex.exec(response.data)[1]
return axios.get('https://cors-anywhere.herokuapp.com/http://api.ipstack.com/'+this.ip+'?access_key=<key>')
})
.then(function(response) {
this.$emit('change', {
lat : response.data.latitude,
long : response.data.longitude
})
})
}
}
</script>
And then how you receive and push it to your weather component:
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<ip #update="updateCoords"></ip>
<weather :lat="lat" :long="long" />
</div>
</template>
<script>
import Weather from './components/Weather.vue'
import ip from './components/ip_resolve.vue'
export default {
name: 'App',
components: {
Weather,
ip
},
data : () => ({
lat : null,
long : null,
}),
methods : {
updateCoords (coords) {
this.lat = coords.lat
this.long = coords.long
}
}
}
</script>

Related

How to fix parse error during Vue app deployment?

I try to deploy Vue app for uploading on GitHub Pages, but got such parse error:
95: </script>
^
96: <template>
97: <Navbar
error during build:
Error: Parse error #:95:10
at parse$b (file:///C:/Users/User/vContact/vContact/node_modules/vite/dist/node/chunks/dep-a713b95d.js:33668:355)
at Object.transform (file:///C:/Users/User/vContact/vContact/node_modules/vite/dist/node/chunks/dep-a713b95d.js:42856:27)
My code:
<script >
import { RouterLink, RouterView } from "vue-router";
import Navbar from "./components/Navbar.vue";
import Contacts from "./components/Contacts.vue";
import Modal from "./components/Modal.vue";
import AddButton from "./components/AddButton.vue";
export default{
components: {
Navbar,
Contacts,
Modal,
AddButton
},
data(){
return {
currentId: 1,
modalOpen: false,
contacts: [],
edit: false,
editContact: {},
search: ''
}
},
created(){
this.getContacts()
},
computed: {
// filterContacts(){
// return this.search ? this.contacts.filter(contact =>
// contact.fullName.toLowerCase().includes(this.search.toLowerCase())) :
this.contacts;
// },
filterContacts(){
return this.search ?
this.contacts.filter(contact => {
for(let key in contact){
if(String(contact[key]).toLowerCase().includes(this.search.toLowerCase())){
return contact;
}
}
})
: this.contacts;
},
// filterContactsCategory(){
// return this.search ? this.contacts.filter(contact =>
contact.category.toLowerCase().includes(this.search.toLowerCase())) : this.contacts;
// }
},
methods: {
openModal(){
this.modalOpen = true
},
closeModal(){
this.modalOpen = false
this.edit = false
},
addContact(item){
this.contacts.push(item)
this.modalOpen = false
},
deleteContact(id){
let index = this.contacts.findIndex(contact => contact.id == id)
this.contacts.splice(index, 1)
},
changeContact(id){
this.edit = this.modalOpen = true
let pickedContact = this.contacts.find(contact => contact.id == id)
this.editContact = pickedContact
},
editedContact(contactEdited){
this.contacts.forEach(contact => {
if(contact.id == contactEdited.id) {
contact.fullName = contactEdited.fullName
contact.phoneNumber = contactEdited.phoneNumber
contact.email = contactEdited.email
contact.category = contactEdited.category
}
})
},
getContacts(){
const localContacts = localStorage.contacts
if(localContacts){
this.contacts = JSON.parse(localContacts)
}
}
},
watch: {
contacts: {
handler(newContacts){
localStorage.contacts = JSON.stringify(this.contacts)
},
deep: true
}
}
}
</script>
<template>
<Navbar
#searchValue="search = $event"
/>
<Contacts
:contacts="filterContacts"
#delContact="deleteContact"
#changeContact="changeContact"
:search="search"
/>
<Modal
:edit="edit"
:editContact="editContact"
#addContact="addContact"
:currentId="currentId"
v-show="modalOpen"
#closeModal="closeModal"
#editedContact="editedContact"
/>
<AddButton
#openModal="openModal"
/>
<RouterView />
</template>
did you put
import vue from '#vitejs/plugin-vue';
in your vite.config.js file, after (of course) installing it via npm?
Expanding on Stephan Franks answer, I had the exact same error as OP, and putting this in vite.config.ts did the trick for me.
import { defineConfig } from 'vite'
import vue from '#vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()], // <-- This is where the magic happens
})
For more information, please have a look at the documentation for #vitejs/plugin-vue.

How to render an asynchronous result (array) in a component in react?

I have been doing js for about a month now, and I am writing this program where I am using clarifai API to see which celebrity a person on the photo resembles the most.
I want to pass the output as props to Rank component to render it, but
I get the
Type error: clarifaiResults.map is not a function at App.transformResponse
Basically, the response I want to pass as props is the
const clarifaiResults = response.outputs[0].data.regions[0].data.concepts[0].name;
part that I get in console.log now
I am assuming it's because there is no output yet when the app tries to render the component, but I can't figure out what's wrong with the code. Thank you!
App.js
import React, { Component } from 'react';
import './App.css';
import SignIn from './Component/SignIn/SignIn.js';
import Register from './Component/Register/Register.js';
import Particles from 'react-particles-js';
import Logo from './Component/Logo/Logo.js';
import Navigation from './Component/Navi/Navigation.js';
import ImageLinkForm from './Component/Form/ImageLinkForm.js';
import Rank from './Component/Rank/Rank.js'
import Clarifai from 'clarifai';
import FaceRecognition from './Component/Face/FaceRecognition.js';
import FaceComparison from './Component/Comparison/FaceComparison.js';
const app = new Clarifai.App({
apiKey: 'MYSUPERSECRETKEY'
});
const initialState = {
input: "",
imageUrl: "",
results: [],
route: "SignIn",
user: {
id: "",
name: "",
email: "",
entries: 0,
joined: "",
},
};
const particleOptions = {
particles: {
number: {
value: 40,
density: {
enable: true,
value_area: 800,
},
}
}
}
class App extends Component{
constructor() {
super();
this.state = initialState;
}
transformResponse = (response) => {
const clarifaiResults = response.outputs[0].data.regions[0].data.concepts[0].name;
const results = clarifaiResults.map((ingredient) => ({
ingredients: ingredient.name,
probablitiy: ingredient.value,
}));
this.setState({results: results.celebrityName});
return {results: []};
};
onInputChange = (event) => {
this.setState({input: event.target.value});
}
onSubmit = () => {
this.setState({imageUrl: this.state.input});
app.models
.predict(
Clarifai.CELEBRITY_MODEL,
this.state.input)
.then(response => {
console.log(response.outputs[0].data.regions[0].data.concepts[0].name)
if (response) {
fetch ('http://loclhost:3000', {
method: 'post',
headers: {'Conent-Type' : 'application/json'},
body: JSON.stringify({
input: this.state.user.input
})
})
.then((response) => response.json())
.then(count => {
this.setState(Object.assign(this.state.user, {entries:count}))
})
}
this.transformResponse(response);
})
.catch(err => console.log(err));
};
;
onRouteChange = (route) => {
if (route === 'signout'){
this.setState({isSignedIn: false})
} else if (route ==='home'){
this.setState({isSignedIn: true})
}
this.setState({route: route});
}
render() {
let { isSignedIn, imageUrl, route, results} = this.state;
return (
<div className="App">
<Particles className='particles'
params={particleOptions}
/>
<Navigation isSignedIn={isSignedIn} onRouteChange={this.onRouteChange}/>
{ route ==='home'
? <div>
<Logo />
<Rank
results = {results}/>
<ImageLinkForm
onInputChange={this.onInputChange}
onSubmit={this.onSubmit}
/>
<FaceRecognition
imageUrl={imageUrl}
/>
<FaceComparison
results = {results}
/>
</div>
: (
route === 'SignIn'
? <SignIn onRouteChange={this.onRouteChange}/>
: <Register />
)
}
</div>
);
};
}
export default App;
Rank.js
import React from 'react';
const Rank = ({results}) => {
const prediction = results.map((result) => {
const {ingredients} = result;
return (
<div>
<li className="celebrityName">{ingredients}</li>
</div>
);
});
if (prediction && prediction.length>1) {
return (
<div>
<div className='white f3'>
You look a lot like...
</div>
<div className='white f1'>
{results}
</div>
</div>
);
} else {
return (
<div>
</div>
)
}
};
export default Rank;

The right way to draw a Map when data is ready

I need to render a map using Mapbox only when data is ready.
I have the following code in my Vuex store:
/store/index.js
import Vue from "vue";
import Vuex from "vuex";
import _ from "lodash";
import { backendCaller } from "src/core/speakers/backend";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
// Activity
activity: [],
geoIps: [],
},
mutations: {
// Activity
setActivity: (state, value) => {
state.activity = value;
},
setGeoIp: (state, value) => {
state.geoIps.push(value);
},
},
actions: {
// Activity
async FETCH_ACTIVITY({ commit, state }, force = false) {
if (!state.activity.length || force) {
await backendCaller.get("activity").then((response) => {
commit("setActivity", response.data.data);
});
}
},
async FETCH_GEO_IPS({ commit, getters }) {
const geoIpsPromises = getters.activityIps.map(async (activityIp) => {
return await Vue.prototype.$axios
.get(
`http://api.ipstack.com/${activityIp}?access_key=${process.env.IP_STACK_API_KEY}`
)
.then((response) => {
return response.data;
});
});
geoIpsPromises.map((geoIp) => {
return geoIp.then((result) => {
commit("setGeoIp", result);
});
});
},
},
getters: {
activityIps: (state) => {
return _.uniq(state.activity.map((activityRow) => activityRow.ip));
},
},
strict: process.env.DEV,
});
In my App.vue I fetch all APIs requests using an async created method.
App.vue:
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
export default {
name: "App",
async created() {
await this.$store.dispatch("FETCH_ACTIVITY");
await this.$store.dispatch("FETCH_GEO_IPS");
},
};
</script>
In my Dashboard component I have a conditional rendering to draw the maps component only when geoIps.length > 0
Dashboard.vue:
<template>
<div v-if="geoIps.length > 0">
<maps-geo-ips-card />
</div>
</template>
<script>
import mapsGeoIpsCard from "components/cards/mapsGeoIpsCard";
export default {
name: "dashboard",
components: {
mapsGeoIpsCard,
},
computed: {
activity() {
return this.$store.state.activity;
},
activityIps() {
return this.$store.getters.activityIps;
},
geoIps() {
return this.$store.state.geoIps;
},
};
</script>
Then I load the Maps component.
<template>
<q-card class="bg-primary APP__card APP__card-highlight">
<q-card-section class="no-padding no-margin">
<div id="map"></div>
</q-card-section>
</q-card>
</template>
<script>
import "mapbox-gl/dist/mapbox-gl.css";
import mapboxgl from "mapbox-gl/dist/mapbox-gl";
export default {
name: "maps-geo-ips-card",
computed: {
geoIps() {
return this.$store.state.geoIps;
},
},
created() {
mapboxgl.accessToken = process.env.MAPBOX_API_KEY;
},
mounted() {
const mapbox = new mapboxgl.Map({
container: "map",
center: [0, 15],
zoom: 1,
});
this.geoIps.map((geoIp) =>
new mapboxgl.Marker()
.setLngLat([geoIp.longitude, geoIp.latitude])
.addTo(mapbox)
);
},
};
</script>
<style>
#map {
height: 500px;
width: 100%;
border-radius: 25px;
overflow: hidden;
}
</style>
The problem is that when the function resolves the first IP address, the map is drawn showing only one address and not all the others like this:
What is the best way to only draw the map when my FETCH_GEO_IPS function has finished?
Thanks in advance
I think the answer lies in this bit of code:
geoIpsPromises.map((geoIp) => {
return geoIp.then((result) => {
commit("setGeoIp", result);
});
});
Your map function loops through every element of the array and commits each IP one by one. So when the first one is committed, your v-if="geoIps.length > 0" is true.
A workaround would be to set a flag only when the IPs are set.
This is a proposed solution:
import Vue from "vue";
import Vuex from "vuex";
import _ from "lodash";
import { backendCaller } from "src/core/speakers/backend";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
// Activity
activity: [],
geoIps: [],
isReady: false
},
mutations: {
// Activity
setActivity: (state, value) => {
state.activity = value;
},
setGeoIp: (state, value) => {
state.geoIps.push(value);
},
setIsReady: (state, value) => {
state.isReady = value;
}
},
actions: {
// Activity
async FETCH_ACTIVITY({ commit, state }, force = false) {
if (!state.activity.length || force) {
await backendCaller.get("activity").then((response) => {
commit("setActivity", response.data.data);
});
}
},
async FETCH_GEO_IPS({ commit, getters }) {
let tofetch = getters.activityIps.length; // get the number of fetch to do
const geoIpsPromises = getters.activityIps.map(async (activityIp) => {
return await Vue.prototype.$axios
.get(
`http://api.ipstack.com/${activityIp}?access_key=${process.env.IP_STACK_API_KEY}`
)
.then((response) => {
return response.data;
});
});
geoIpsPromises.map((geoIp) => {
return geoIp.then((result) => {
commit("setGeoIp", result);
toFetch -= 1; // decrement after each commit
if (toFetch === 0) {
commit("setIsReady", true); // all commits are done
}
});
});
},
},
getters: {
activityIps: (state) => {
return _.uniq(state.activity.map((activityRow) => activityRow.ip));
},
},
strict: process.env.DEV,
});
And in your view:
<template>
<div v-if="isReady">
<maps-geo-ips-card />
</div>
</template>
<script>
import mapsGeoIpsCard from "components/cards/mapsGeoIpsCard";
export default {
name: "dashboard",
components: {
mapsGeoIpsCard,
},
computed: {
activity() {
return this.$store.state.activity;
},
activityIps() {
return this.$store.getters.activityIps;
},
isReady() {
return this.$store.state.isReady;
},
};
</script>

How to integrate leaflet-search plugin to my vue2-leaflet project?

I’m new to Vue and I’m developing a map application with vue2-leaflet. I would like to add a search box to my application to locate markers on my map, I found this leaflet plugin leaflet-search which is just the functionality I’m looking for, but I don’t know how to integrate it into my vue cli project.
After installing the plugin, how do I import it into my vue component? and, where in my <script> should I add the code?
This is what the component where I want to add the search box looks like:
<template>
<body>
<l-map class="map" ref="map" :min-zoom="minZoom" :crs="crs">
<l-tile-layer :url="url"></l-tile-layer>
<l-grid-layer class="grid" :tile-component="tileComponent"></l-grid-layer>
<l-marker v-for="(newCoords, i) in InvertedCoords" :key="i" :lat-lng="newCoords">
<div v-if="stars[i].status === 'ALLY'">
<l-icon ></l-icon>
</div>
<div v-else>
<l-icon></l-icon>
</div>
<l-popup class="popup">
<em class="popup-bold">Name: </em>{{ stars[i].name }}<br />
<em class="popup-bold">Longitud: </em>{{ stars[i].lng }}<br />
<em class="popup-bold">Latitud: </em>{{ stars[i].lat }}<br />
</l-popup>
</l-marker>
</l-map>
</body>
</template>
<script>
import L from "leaflet";
import { CRS } from "leaflet";
import GridTemplate from './GridTemplate.vue';
import { eventBus } from '../main.js'
import {
LMap,
LTileLayer,
LMarker,
LPopup,
LPolyline,
LIcon,
} from "vue2-leaflet";
export default {
name: "Map",
components: {
LMap,
LTileLayer,
LMarker,
LImageOverlay,
LPopup,
LIcon,
},
props: {
msg: {
type: String
}
},
data() {
return {
url: "",
bounds: [ [-2600, -2700], [1000, 3000] ],
minZoom: 0,
crs: L.CRS.Simple,
stars: [],
messageList: [],
tileComponent: GridTemplate
};
},
computed: {
InvertedCoords() {
var newArraw = [];
for (let i = 0; i < this.stars.length; i++) {
newArraw[i] = {
id: i + 2,
lat: this.stars[i].lat * -1,
lng: this.stars[i].lng * -1
};
}
return newArraw;
console.log(newArraw);
}
},
watch: {
msg: function() {
this.messageList.push(this.msg);
}
},
mounted() {
this.$refs.map.mapObject.setView([552, 40], 1);
this.$http.get("url")
.then(response => {
return response.json();
})
.then(data => {
const resultArray = [];
for (let key in data) {
resultArray.push(data[key]);
}
this.stars = resultArray;
});
methods: {
inverted() {
for (let i = 0; i < this.newArraw.length; i++) {
console.log(this.newArraw[i]);
return this.newArraw[i];
}
},
updateStars(text) {
this.$http.get("url")
.then(response => {
return response.json();
})
.then(data => {
const resultArray = [];
for (let key in data) {
resultArray.push(data[key]);
}
this.stars = resultArray;
});
},
StarsData() {
eventBus.$emit('i-got-clicked', this.stars)
},
}
};
</script>
<style scoped>
</style>
I don't know your Plugin but I have used gesearch.
Here is what I did to register the leaflet plugin.
import { OpenStreetMapProvider } from "leaflet-geosearch";
import "leaflet-geosearch/assets/css/leaflet.css";
import { GeoSearchControl } from "leaflet-geosearch";
Then in mounted() I can register it to leaflet. You can look at the docs of how to register controls.
mounted() {
const map = this.$refs.map.mapObject;
const searchControl = new GeoSearchControl({
provider,
// ... some more options
});
map.addControl(searchControl);
}
Perhaps this helps you.

Vue.js - Return array from axios

I am trying to get an array out of an axios call:
so that I can access the data for a component. I'm aware that i could use some thing like
return {
a: []
}
}
getTags(index) {
axios.get('http://localhost:8080/user/tag?imageIndex=' + index)
.then(response => {
this.a = response.data
})
},
But the Problem is, that i have for each image one array and the number of images are dynamic. So i would like to just give a array back
Is there a opportunity to do as I want?
I could live with generating all the arrays in data() if there is a way to do that dynamically. Or can axios return it?
Here my Code that does not work:
<template>
<div id="SingleFile">
<button
id="refreshbtn"
class="btn btn-primary btn-margin"
#click="updateFileList">
refresh
</button>
<gallery :images="images" :index="index" #close="index = null"></gallery>
<div
:key="imageIndex"
:style="{ backgroundImage: 'url(' + image + ')', width: '300px', height: '200px' }"
#click="index = imageIndex"
class="image"
v-for="(image, imageIndex) in images"
>
<div>
<vue-tags-input
v-model="tag"
:tags="getTags(imageIndex)"
#tags-changed="newTags => tags = newTags"
/>
</div>
</div>
<div class="upload">
<upload-image url="http://localhost:8080/user" name="files" max_files="100"></upload-image>
</div>
</div>
</template>
<script>
import VueGallery from 'vue-gallery';
import axios from 'axios';
import auth from './../service/AuthService'
import router from './../router'
import UploadImage from 'vue-upload-image';
import VueTagsInput from '#johmun/vue-tags-input';
export default {
components: {
'gallery': VueGallery,
'upload-image': UploadImage,
VueTagsInput
},
data() {
return {
images: [],
index: null,
tag: '',
};
},
created() {
this.checkAuth()
},
methods: {
checkAuth() {
if (auth.isAuthenticated()) {
this.updateFileList()
} else {
router.replace('/')
}
},
updateFileList() {
axios.get('http://localhost:8080/user')
.then(response => {
this.images = response.data
})
},
getTags(index) {
return axios.get('http://localhost:8080/user/tag?imageIndex=' + index)
.then(response => {
return response.data
})
},
},
};
</script>
The best way is to return the data using axios in mounted hook or by calling a method after firing some event :
mounted(){
//return all your images using valid url
axios.get('http://localhost:8080/user/tag')
.then(response => {
this.a = response.data
})
}
and your method should be like as :
methods:{
getTags(i){
return this.a[i];
}
}

Categories