Vue.js: Scroll down using an anchor and a query - javascript

I receive a notification from API with these parameters:
{
"id": 12345,
"created_at": "2018-01-15 11:40:30 +0000",
"message": "Hi!",
"object_id": 12,
"anchor": "1543",
"notification_type": "comment_created",
"viewed": true
}
When a user clicks on the notification, he/she must be addressed to the post page, then this page must be scrolled down to the new added comment. I need to use an anchor from API and a query parameter to do this but I don't quite understand how.
Here is the component:
<template>
<div>
<span>
<i class="g-font-size-18 g-color-gray-light-v1"></i>
</span>
<router-link :to="linkFromNotification(item)
#click.native="linkFromNotification(item.notification_type)">
<p>
<span v-html="item.message"></span>
</p>
</router-link>
</div>
</template>
<script>
import {mapActions, mapGetters} from 'vuex'
export default {
props: ['item'],
computed: {
...mapGetters([
'getNotifications'
])
},
methods: {
...mapActions([
'readNotification'
]),
linkFromNotification (item) {
if (item.notification_type === 'user_subscribed') {
return {name: 'person', params: {id: item.object_id}}
} else if (['comment_created', 'answer_selected', 'answer_created'].includes(item.notification_type)) {
return {name: 'show_post', params: {id: item.object_id}}
} else if (item.notification_type === 'user_coauthored') {
return {name: 'show_post', params: {id: item.object_id}}
}
}
}
}
</script>
I assume that I can add query parameter inside linkFromNotification where I return a post, and pass a comment.id or an anchor. Then I can add a watcher to see if comments are rendered to the page. Please help me to develop this thought and add query to the function linkFromNotification which construct the URL.

Related

VUEX - can´t access to an object returned by GETTER

I need to access to a property of the object "articulos" on the view "Pop.vue".
This object is return by a Getter.
I can see the whole object "articulos" on the view, like this:
{"id": "1", "nombre": "Bolso1", "categoria": "Bolso"}
but I can´t display just one of the properties. I have tried with
{{obtenerArticuloPorId.nombre}}
Error: TypeError: Cannot read properties of undefined (reading 'nombre')
This is my code (I have commented code in relation with this in order to make reading easier):
index.js (Store)
import { createStore } from 'vuex'
export default createStore({
state: {
categoriaArticulos: [
"lamina", "bolso"
],
articulos: [
{id: '0', nombre: "Imagen1", categoria: "lamina"},
{id: '1', nombre: "Imagen2", categoria: "lamina"},
{id: '2', nombre: "Bolso1", categoria: "bolso"},
{id: '3', nombre: "Bolso2", categoria: "bolso"}
],
id: ''
},
mutations: {
obtenerId(state, payload){
state.id=payload
}
},
actions:{
obtenerId({commit}, id){
commit('obtenerId', id)
}
},
getters: {
obtenerArticulosPorCategoria: (state) => (id) => {
return state.articulos.filter(art=> art.categoria==id)
},
obtenerArticuloPorId(state){
const articulo=state.articulos.find(art=> art.id===state.id)
return articulo
}
}
Pop.vue
<template>
<div class="popup">
{{obtenerArticuloPorId}}
</div>
</template>
<script>
import {mapActions, mapGetters} from 'vuex'
export default {
methods:{
...mapActions(['obtenerId'])
},
computed: {
...mapGetters(['obtenerArticuloPorId'])
}
}
</script>
Array.prototype.find() returns undefined if nothing matches the predicate.
Since your id state property is initially an empty string and none of the articulos elements have an empty string id, you get no matches and thus obtenerArticuloPorId.nombre generates an error
Cannot read properties of undefined (reading 'nombre')
You can work around this by making access to the potentially undefined object safe using optional chaining
<div class="popup">
{{ obtenerArticuloPorId?.nombre }}
</div>
This will show an empty value until you commit a valid id to your store. Alternately, use conditional rendering to wait until you have a valid object
<div class="popup" v-if="obtenerArticuloPorId">
{{ obtenerArticuloPorId.nombre }}
</div>

Vue component methods not firing

I'm trying out Vue and I ran into the issue of a component's "mounted" method not firing, I honestly can't see any reason why it won't work, there are no errors or warnings, I checked every single line at least 4 times now, and I just can't figure out what's wrong, I tried "console-logging" something when the method fires in a Post component and it worked, but it didn't when I tried doing the same thing in a Comment component, here is all the code you should need:
The Post component:
<template>
<div class="blog-post">
<h2>{{ title }}</h2>
<p>{{ body }}</p>
<div class="comment-section">
<Comment
v-for="comment in comments"
v-bind:key="comment.id"
:name="comment.name"
:email="comment.email"
:body="comment.body"
/>
</div>
</div>
</template>
<script>
import axios from "axios";
import Comment from "./Comment";
export default {
name: "Post",
props: {
title: String,
body: String,
postId: Number,
},
components: {
Comment,
},
data() {
return {
comments: [
{
name: "comment name",
email: "comment email",
body: "comment body",
postId: 1,
id: 1,
},
],
};
},
methods: {
async getPostData() {
const url = `https://jsonplaceholder.typicode.com/comments`;
const response = await axios.get(url);
const data = await response.data;
this.comments = data.map((comment) => ({
name: comment.name,
email: comment.email,
body: comment.body,
postId: comment.postId,
id: comment.id,
}));
},
mounted() {
this.getPostData();
},
},
};
</script>
And the Comment component:
<template>
<div class="comment">
<h4>{{ name }}</h4>
<h5>{{ email }}</h5>
<p>{{ body }}</p>
</div>
</template>
<script>
export default {
name: "Comment",
props: {
name: String,
email: String,
body: String,
},
data() {
return {};
},
};
</script>
The comments render properly when I put that placeholder data myself into the comments array, so apparently the mount() and the getPostData() methods aren't firing (or one of them at least), considering I also tried console-logging as I've said before. I can't see what the issue here is at all and can't really google stuff like this since it's so specific. So far what I know is that, the API I'm fetching data from works, the URL is correct, the comments do display on the page, meaning it's not a problem with rendering, and as I said I've tried console-logging something in the getPostData and it didn't work, whereas in Blog component it did (which does exactly the same Post should do, except fetches Posts instead of Comments). In any case, any help would be appreciated, I hope I gave all the info you might need, if not, please ask.
Your mounted function is inside your methods object.
Move it out like this:
<template>
<div class="blog-post">
<h2>{{ title }}</h2>
<p>{{ body }}</p>
<div class="comment-section">
<Comment
v-for="comment in comments"
v-bind:key="comment.id"
:name="comment.name"
:email="comment.email"
:body="comment.body"
/>
</div>
</div>
</template>
<script>
import axios from "axios";
import Comment from "./Comment";
export default {
name: "Post",
props: {
title: String,
body: String,
postId: Number,
},
components: {
Comment,
},
data() {
return {
comments: [
{
name: "comment name",
email: "comment email",
body: "comment body",
postId: 1,
id: 1,
},
],
};
},
methods: {
async getPostData() {
const url = `https://jsonplaceholder.typicode.com/comments`;
const response = await axios.get(url);
const data = await response.data;
this.comments = data.map((comment) => ({
name: comment.name,
email: comment.email,
body: comment.body,
postId: comment.postId,
id: comment.id,
}));
},
},
mounted() {
this.getPostData();
},
};
</script>

VueJS push a route with params data

I am very new to VueJS. How can I get the deviceId in Device component in vuejs. The deviceId in h1 tag was not printed out in the Device component page.
goForward() {
console.log("go forward");
this.$router.push({ name: "Device", params: { deviceId: "Air-conditioning" } });
},
<template>
<div class="about">
<h1>This is the device page {{ deviceId }}</h1>
</div>
</template>
<script>
export default {
name: "Device",
props: ["deviceId"],
data() {
return {};
},
};
</script>
const routes = [
{
path: '/device',
name: 'Device',
component: Device,
},
]
In order to receive your params as props you need to add the props: true option in the route object.
const routes = [
{
path: "/device",
name: "Device",
component: 'Device',
props: true
}
];
https://router.vuejs.org/guide/essentials/passing-props.html#boolean-mode
It's also worth noting that you can improve the URL scheme a bit by adding a route parameter like so:
{
path: "/device/:deviceId",
...
}
Thus, the URL in the address bar will look cleaner:
https://www.example.com/device/Air-conditioning

Get static JSON single product data in Vue JS by ID

I am creating a Vue JS app which will display a list of products that when clicked on will link through to a dynamic product by its ID (passed via Vue Router params). This bit works fine but what I need to do is once on that dynamic route, display all the data for that product by its ID. I'm still pretty new to Vue and confused by what approach to take. I don't think I need axios as this won't be an online app so no access to APIs. I also don't know if I need to go as far as using vuex. At present I'm trying to use a simple method to grab the data from my JSON file by the ID passed through the routes parameters. Here's what I have so far...
router.js
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
import Products from './views/Products.vue'
import Product from './views/ProductSingle.vue'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/products',
name: 'products',
component: Products
},
{
path: '/product/:id',
name: 'product',
component: Product,
props: true
}
]
})
Slider.vue - this component is called in Views/Products.vue and is what links through to each single product
<template>
<carousel
:items="3"
:loop="true"
:center="true"
:margin="0"
:dots="false"
:nav="true"
>
<div v-for="product in products" :key="product.productId">
<transition name="slide">
<router-link
:to="{
name: 'product',
params: { id: product.productId }
}"
>
<img src="http://lorempixel.com/100/100/city" :alt="product.name" />
<!-- <img
:src="require('../assets/images/sample-product/' + data.image)"
/>-->
</router-link>
</transition>
</div>
</carousel>
</template>
<script>
import json from '#/json/data.json'
import carousel from 'vue-owl-carousel'
export default {
data() {
return {
products: json
}
},
components: {
carousel
}
}
</script>
ProductSingle.vue
<template>
<div>
<p v-for="product in getData($route.params.id)" :key="product.productId">
{{ product }}
</p>
</div>
</template>
<script>
import json from '#/json/data.json'
export default {
data() {
return {
products: json
}
},
methods: {
getData(id) {
let data = this.products
data.filter(item => {
return item.productId == id
})
}
}
}
</script>
What I as expecting here is that my getData method would return the data for the product at the ID denoted through $route.params.id yet nothing is returned. A console.log(getData($route.params.id)) shows the following:
[{…}]
Which when expanded out shows 0: {__ob__: Observer} and if you expand that observer out there is indeed that data for
image: (...)
name: (...)
productId: (...)
My data.json file looks like the below:
[
{
"productId": 1,
"name": "Test 1",
"image": "sample.jpg"
},
{
"productId": 2,
"name": "Test 2",
"image": "sample.jpg"
},
{
"productId": 3,
"name": "Test 3",
"image": "sample.jpg"
},
{
"productId": 4,
"name": "Test 4",
"image": "sample.jpg"
}
]
Could really do with some guidance on how to approach this problem.
Many thanks in advance
You're missing a return here:
data.filter(item => {
return item.productId == id
})
Try this:
return data.filter(item => {
return item.productId == id
})
If you're not using Api or Vuex then you need to have all the data on a place where they can be fetched from multiple components.
data.json is fine for this.
On your child component, do the following. There is no need for data iteration.
<template>
<div>
{{ singleProduct }}
{{ singleProduct.productId }}
</div>
</template>
<script>
import json from '#/json/data.json'
export default {
name: 'ProductSingle',
data() {
return {
products: json,
singleProduct: undefined
}
},
computed: {
productId () {
return this.$route.params.id // From route
}
},
created () {
this.assignSingleProduct();
},
methods: {
assignSingleProduct() {
let data = this.products
data.filter(item => {
return item.productId == this.productId
})
// Assign to the data
this.singleProduct = data
}
}
}
</script>

how to redirect from vue router-link using query?

How to add a query to the router-link so the user will be redirected to the comment under a post by clicking on the link from the notification?
API gives us an anchor of the notification and id of the comment. When the DOM renders a page, it loads a post firstly, then comments.
Here is a component of the notification:
<template>
<div>
<span>
<i class="g-font-size-18 g-color-gray-light-v1"></i>
</span>
<router-link :to="linkFromNotification(item)
#click.native="linkFromNotification(item.notification_type)">
<p>
<span v-html="item.message"></span>
</p>
</router-link>
</div>
</template>
<script>
import {mapActions, mapGetters} from 'vuex'
export default {
props: ['item'],
computed: {
...mapGetters([
'getNotifications'
])
},
methods: {
...mapActions([
'readNotification'
]),
linkFromNotification (item) {
if (item.notification_type === 'user_subscribed') {
return {name: 'person', params: {id: item.object_id}}
} else if (['comment_created', 'answer_selected', 'answer_created'].includes(item.notification_type)) {
return {name: 'show_post', params: {id: item.object_id}}
} else if (item.notification_type === 'user_coauthored') {
return {name: 'show_post', params: {id: item.object_id}}
}
}
}
}
</script>
If you mean url queries you can use key query in the object you are returning,
If you mean a hash "#" to be added to the link you can use the key hash. for example:
{name: 'person', params: {id: item.object_id}, query:{name:"Mohd"}, hash:"214"}
Reference

Categories