GeoLocation can't pass coordinates to another variable - vue.js

So i would like to pass latitude and longitude to width and height variables in "data()", instead of just writing it in html.
I tried to pass variable through all these functions to have access to variable but this doesn't worked in function showLocation(location).
<template>
<div class="app">
<div class="app-Inner">
</div>
<button #click="getLocation">VIEW YOUR LOCATION</button>
<div>{{ width }}</div>
<div class="location"></div>
<div class="map"></div>
</div>
</template>
<script>
import Home from './components/Home.vue';
import MainValues from './components/MainValues.vue';
import Form from './components/Form.vue';
import More from './components/More.vue';
export default {
name: 'App',
data() {
return {
width: 0,
height: 0,
};
},
methods: {
getLocation() {
const geo = navigator.geolocation;
if (geo) {
// eslint-disable-next-line
geo.getCurrentPosition(showLocation);
} else {
const divLocation2 = document.querySelector('.location');
divLocation2.innerHTML = 'error';
}
function showLocation(location) {
// edit: !!!
// here instead of sending location cords through innerHTML i
// want to assign them into variables "width" and "height" which
// are defined in "data()" function above.
// tried: this.width = location.coords.latitude, and
// also : this.length = location.coords.longitude
// !!!
const divLocation = document.querySelector('.location');
divLocation.innerHTML = `${location.coords.latitude} ${location.coords.longitude}`;
}
},
},
};
</script>

Related

VueJS test-utils can't find element inside child component

I'm trying to use findComponent with find method to find a child component's element and set it's value. But every time I run test, it gives me Cannot call setValue on an empty DOMWrapper. error.
Test file
import { mount } from '#vue/test-utils';
import Create from './Create.vue';
// import State from '#/components/State.vue';
describe('it tests Create component', () => {
test('it emits create event and resets the form when form is valid and create button is clicked', async () => {
const div = document.createElement('div');
div.id = 'root';
document.body.append(div);
const expectedNameValue = 'TODO_NAME';
const expectedStateValue = 'Pending';
const wrapper = mount(Create, {
attachTo: '#root',
});
await wrapper.find(`input`).setValue(expectedNameValue);
await wrapper.findComponent({ ref: 'state-component' }).find('select').setValue(expectedStateValue);
await wrapper.find(`form`).trigger('submit');
expect(wrapper.emitted().create).toBeTruthy();
expect(wrapper.emitted().create[0]).toEqual([expectedNameValue]);
expect(wrapper.emitted().create[1]).toEqual(['Pending']);
expect(wrapper.find(`input[name='name']`).element.value).toEqual('');
expect(wrapper.find(`input[name='state']`).element.value).toEqual('Pending');
});
});
Create component
<template>
<form #submit.prevent="createTodo" class="flex gap-2 w-full">
<input class="flex-1 shadow rounded-md p-2 focus:ring-2 focus:ring-blue-900 focus:outline-none" type="text" placeholder="Todo Name" name="name" required/>
<State ref="state-component"/>
<button type="submit" class="rounded-md shadow text-white bg-blue-700 py-2 px-6">Create</button>
</form>
</template>
<script>
import State from '#/components/State.vue';
export default {
components: { State },
emits: ['create'],
methods: {
createTodo(event) {
const elems = event.target.elements;
const todo = { name: elems.name.value, state: elems.state.value };
this.$emit('create', todo);
elems.name.value = '';
elems.state.value = 'Pending';
}
}
}
</script>
<style scoped>
</style>
State component
<template>
<select id="state-select" class="rounded-md bg-green-200 text-white" name="state">
<option
v-for="(state, index) in states"
:selected="isSelected(state)"
:key="index"
>
{{ state }}
</option>
</select>
</template>
<script>
export default {
props: ["todo", "index"],
data() {
return {
name: "",
state: "",
states: ["Pending", "In Progress", "Done"],
};
},
created() {
if(!this.todo) return true;
this.state = this.todo.state;
this.name = this.todo.name;
},
methods: {
isSelected(equivalent){
return equivalent === this.state;
}
}
};
</script>
<style scoped></style>
I'm fairly new to VueJS so I'm open to all tips and tricks, thanks.
Some issues to fix:
You don't need to attach the component to the document, so remove that:
// ❌
// const div = document.createElement('div');
// div.id = 'root';
// document.body.append(div);
// const wrapper = mount(Create, { attachTo: '#root' });
// ✅
const wrapper = mount(Create);
The template ref to the State component would be the component's root element, so no need to find() the <select>:
// ❌
// await wrapper.findComponent({ ref: 'state-component' }).find('select').setValue(expectedStateValue);
^^^^^^^^^^^^^^^
// ✅
await wrapper.findComponent({ ref: 'state-component' }).setValue(expectedStateValue);
The emitted() object key is the event name, and the value is an array of of arrays, containing emitted data. You can verify the first create-event data contains another object with toMatchObject(object):
// ❌
// expect(wrapper.emitted().create[0]).toEqual([expectedNameValue]);
// expect(wrapper.emitted().create[1]).toEqual(['Pending']);
// ✅
expect(wrapper.emitted().create[0][0]).toMatchObject({ name: expectedNameValue, state: 'Pending' });
The last assertion tries to find input[name='state'], but that's actually a <select>, not an <input>:
// ❌
// expect(wrapper.find(`input[name='state']`).element.value).toEqual('Pending')
^^^^^
// ✅
expect(wrapper.find(`select[name='state']`).element.value).toEqual('Pending')
demo

Hide Navbar component when it reaches Footer component in VueJS

I have a fixed Navbar at the bottom and I would like to make it fade out when it meets the Footer element.
I followed this answer: Fade out Div / Section in vue.js when reaching target section
<template>
<div id="app">
<transition name="fade">
<Navbar :nav-links="navLinks" v-show="show"></Navbar>
</transition>
<router-view />
<Footer ref="footer" :nav-links="navLinks"></Footer>
</div>
</template>
<script>
import Navbar from "./components/Navbar";
import Footer from "./components/Footer";
export default {
name: "App",
components: {
Navbar,
Footer,
},
data() {
return {
show: true,
};
},
mounted() {
// this.listener = () => {
// let rect = this.$refs.footer.getBoundingClientRect();
// let top = window.innerHeight - rect.top;
// this.show = top < 0;
// };
// window.addEventListener("scroll", this.listener);
// with Intersection Observer API
this.observer = new IntersectionObserver(() => {
let rect = this.$refs.footer.getBoundingClientRect()
let top = window.innerHeight - rect.top
this.show = top < 0
})
this.observer.observe(this.$refs.footer)
},
destroyed() {
/* window.removeEventListener('scroll', this.listener) */
this.observer.unobserve(this.$refs.footer);
},
};
</script>
and it doesn't work at all.
this.listener method returns Uncaught TypeError: _this.$refs.footer.getBoundingClientRect is not a function
and
this.observer method returns TypeError: Failed to execute 'observe' on 'IntersectionObserver': parameter 1 is not of type 'Element'.
I have tried wrapping them inside $nextTick but I still get the above errors:
mounted() {
this.$nextTick(() => {
// methods here
})
},
What do you think ?

vue.js nuxt.js Creating dynamical pages and pagination

I have several scenes that I want to include in my nuxt project.
The project should have pages, and each page has 4 scenes.
I'm currently 'coding' each page and import the scenes manually.
This is my pages tree:
pages tree
This is my first page
<template>
<main class="main">
<Scene1 />
<Scene2 />
<Scene3 />
<!-- <Scene4 /> -->
</main>
</template>
<script>
import Scene1 from './1/Scene1.vue'
import Scene2 from './1/Scene2.vue'
import Scene3 from './1/Scene3.vue'
// import Scene4 from './1/Scene4.vue'
export default {
components: {
Scene1,
Scene2,
Scene3,
// Scene4
}
}
</script>
<style scoped>
.main{
display: grid;
grid-template-columns: 50% 50%;
}
</style>
And this is how a scene looks like:
<template>
<div class="three-item">
<div class="item-title">
Color Rectangle
</div>
<p>Vertex</p>
<pre><code id="vertexShader">
void main() {
gl_Position = vec4( position, 1.0 );
}
</code></pre>
<p>Fragment</p>
<pre><code id="fragmentShader">
#ifdef GL_ES
precision mediump float;
#endif
uniform float u_time;
void main() {
// Magenta (1,0,1)
gl_FragColor = vec4(1.0,0.0,1.0,1.0);
}
</code></pre>
<div id="scene1" class="scene">
</div>
</div>
</template>
<script>
import * as Three from 'three'
import hljs from 'highlight.js'
import glsl from 'highlight.js/lib/languages/glsl';
export default{
name: 'Scene1',
data() {
return {
camera: null,
scene: null,
renderer: null,
mesh: null
}
},
methods: {
init: function() {
this.container = document.getElementById('scene1');
this.camera = new Three.PerspectiveCamera(70, this.container.clientWidth/this.container.clientHeight, 0.01, 10);
this.camera.position.z = 1;
this.scene = new Three.Scene();
this.uniforms = {
u_time: { type: "f", value: 1.0 },
u_resolution: { type: "v2", value: new Three.Vector2() },
u_mouse: { type: "v2", value: new Three.Vector2() }
};
let material = new Three.ShaderMaterial( {
uniforms: this.uniforms,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent
} );
let geometry = new Three.BoxBufferGeometry(0.2, 0.2, 0.2);
// let material = new Three.MeshNormalMaterial();
this.mesh = new Three.Mesh(geometry, material);
this.scene.add(this.mesh);
this.renderer = new Three.WebGLRenderer({alpha:true});
this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
this.container.appendChild(this.renderer.domElement);
window.addEventListener( 'resize', this.onWindowResize, false );
hljs.registerLanguage('glsl', glsl);
document.querySelectorAll('pre code').forEach((block) => {
hljs.highlightBlock(block);
});
},
animate: function() {
requestAnimationFrame(this.animate);
this.mesh.rotation.x += 0.01;
this.mesh.rotation.y += 0.02;
this.uniforms.u_time.value += 0.05;
this.renderer.render(this.scene, this.camera);
},
onWindowResize: function() {
this.camera.aspect = this.container.clientWidth / this.container.clientHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
}
},
mounted() {
this.init();
this.animate();
this.onWindowResize();
}
}
</script>
To be honest, I don't get behind the dynamical created pages with nuxt. Is there a way to have a template that fetches 4 scenes, puts them into a page and does it all over again? It could be that I want to add scenes in the middle of the pages. That would destroy my current setup.
My goal is to have something like this:
dreams tree
the page.vue is crawling through the scenes, picks 4, creates a page called '1', and does it all over again. Can someone lend me a hand with that? 😊In the examples at the nuxt homepage they use .json files. I already set up all three.js scenes, but these have javascript files in it.
Erik

Invalid prop: type check failed error in VueJS

I am trying to use a component named CardRenderer.vue which renders card using array of Objects. I am using the same component again & again to render the data. I am having this error "[Vue warn]: Invalid prop: type check failed for prop "renderData" when I tried passing prop from component.
I tried passing different values and different types but it did'nt work.
Here is the code:
CardRenderer.vue
<template lang="html">
<div>
<b-container class="bv-example-row">
<b-row v-for="(row, i) of rows" v-bind:key="i">
<b-col v-for="(item, j) of row" v-bind:key="j" >
<!-- you card -->
<b-card
:title="item.title"
img-src="item.icon"
img-alt="Image"
img-top
tag="article"
style="max-width: 20rem;"
class="mb-2"
>
<b-card-text>
<h1>{{item.name}}</h1>
<pre>{{item.description}}</pre>
</b-card-text>
<b-button :href="'/dashboard/'+item.name" variant="primary">More</b-button>
</b-card>
</b-col>
</b-row>
</b-container>
</div>
</template>
<script lang="js">
export default {
name: 'CardRenderer',
props: {
renderData: []
},
data() {
return {
rows: null
}
},
mounted() {
const itemsPerRow = 3
let rowss = []
let arr = this.renderData
// eslint-disable-next-line
// console.log(this.renderData)
for (let i = 0; i < arr.length; i += itemsPerRow){
let row = []
for (let z = 0; z < itemsPerRow; z++) {
row.push(arr[z])
}
rowss.push(row)
}
this.rows = rowss
// eslint-disable-next-line
console.log(this.rows)
},
methods: {
},
computed: {
// rows() {
// }
}
}
</script>
<style scoped>
</style>
CardGrouper.vue:
<template lang="html">
<div class = "full" >
<div class="h-50" style=" background-color: #C8544F">
<h1 align="center">{{$store.getters.responseAPI.title}} </h1>
<CardRenderer :renderData=this.$store.getters.responseAPI.apps />
</div>
</div>
</template>
<script>
import CardRenderer from "./CardRenderer.vue"
/* eslint-disable */
export default {
name: 'CardGrouper',
components: {
CardRenderer
},
props: [],
mounted() {
},
data() {
return {
}
},
methods: {
},
computed: {
}
}
</script>
<style scoped >
.full{
width: 100vw;
height: 90vh;
background: linear-gradient(to bottom, Red 30%, white 50%);
}
</style>
Something.vue
<template lang="html">
<!-- <h1>Something</h1> -->
<CardRenderer :renderData=valObj />
</template>
<script lang="js">
import CardRenderer from './CardRenderer'
export default {
name: 'something',
components: {
CardRenderer
},
props: [],
data() {
return {
valObj: []
}
},
mounted() {
let key = this.findUrl()
let value = this.$store.getters.responseAPI.apps.filter((elem) => {
if(elem.name == key) return elem.apps
})
if (value.length > 0)
this.valObj = value[0].apps
//eslint-disable-next-line
console.log(this.valObj)
},
methods: {
findUrl() {
let url = window.location.pathname.split("/").slice(-1)[0];
return url
}
},
computed: {
}
}
</script>
<style scoped >
.something {
}
</style>
I am having this error.
It looks like this.
This is the data I am passing as a prop from Something.vue
This is how value looks like
Error is being generated somewhere from Something.vue.
I am passing array of objects as a prop.
How do i rectify this error, to make it work.
Set the renderData type as Array and default value to []:
props: {
renderData: {
type: Array,
deafult: () => []
}
}
It appears that you are defining your renderData prop as an array [] but probably are passing an object to it or something.
Either simplify it and do...
props: ['renderData']
Or if you are passing an object to it do..
props: {
renderData: {
type: Object,
}
}
If it is an array of objects do..
props: {
renderData: {
type: Array,
default: () => [{}];
}
just for doc.
// Object with a default value
propE: {
type: Object,
// Object or array defaults must be returned from
// a factory function
default: function () {
return { message: 'hello' }
}
},
This is in vue prop documentation

Vue: Variable not safed, when other component is shown

I have two components. One of it gives the value height to the other one, when "submit" is clicked. When "submit" is clicked the first component should be hidden and the second one visible.
It works so far, but it seems like height is not safed in the second component.
Thanks a lot!!
without the v-if it works perfect!
//ComponentOne
<template>
<body>
<div id="aside">
<footer>
<b-button v-on:click="submit">Submit</b-button>
</footer>
</div>
</body>
</template>
<script>
import { EventBus } from '#/main.js'
export default {
data() {
return {
submitp1: false,
height: 5,
width: 6,
}
},
methods: {
submit: function () {
this.submitp1 = !(this.submitp1)
EventBus.$emit('submitp1emit', this.submitp1)
EventBus.$emit('1to2', this.height)
}
},
}
</script>
//ComponentTwo
<template>
<div >
number <br />
height: {{height}}
</div>
</template>
<script>
import { EventBus } from '#/main.js'
export default {
data: function () {
return {
height: '',
}
},
mounted() {
const self = this
EventBus.$on('1to2', function{ height) {
self.height = height
})
}
}
</script>
//main.js
<template>
<div id="app">
<ComponentOne v-if="submitp1 == false" />
<ComponentTwo v-if="submitp1 == true" />
</div>
</template>
<script>
import { EventBus } from '#/main.js'
import ComponentOne from '#/components/p1Comp/ComponentOne.vue'
import ComponentTwo from '#/components/p1Comp/ComponentTwo.vue'
export default {
components: {
ComponentOne,
ComponentTwo
}
data: function () {
return {
submitp1: false
}
},
mounted() {
const self = this
EventBus.$on('submitp1emit', function (submitp1emit) {
self.submitp1 = submitp1emit
})
}
}
</script>
From the Vue documentation:
v-if is “real” conditional rendering because it ensures that event
listeners and child components inside the conditional block are
properly destroyed and re-created during toggles.
https://v2.vuejs.org/v2/guide/conditional.html#v-if-vs-v-show
The toggled component is simply not there. As already mentioned, you can use "v-show" instead.