make the title post clickable vue - vue.js

I'm using vue + laravel and i try do display the data from api like this:
<div v-for="article in articles" :key="article.id" class="hieght">
<div class="NewArticle">
<h6 class="NewArticleTitle">{{ article.title.ar }}</h6>
</div>
<div>
<p class="NewArticleBody">{{ article.excerpt.ar }}</p>
</div>
</div>
and the script is:
<script>
export default {
data() {
return {
articles: []
}
},
created() {
this.axios
.get('http://localhost:8000/api/articles/')
.then(response => {
this.articles = response.data;
});
}
}
But I want the post to open when I click on the title, I don't know how can i do that using vue

You need to create a view component for your post. and make the h6 title tag a router-link tag that takes you to the post (or article) details page.

Related

Unable to Define Variable in Vue

I'm just starting to use VueJS & Tailwind, having never really used anything related to npm before.
I have the below code, making use of Tailwind & Headless UI which through debugging, I know I'm like 99% of the way there... except for the continuous error message
Uncaught ReferenceError: posts is not defined
I know this should be straight forward, but everything I've found either here or with Google hasn't worked. Where am I going wrong?
<template>
<Listbox as="div" v-model="selected">
<ListboxLabel class="">
Country
</ListboxLabel>
<div class="mt-1 relative">
<ListboxButton class="">
<span class="">
<img :src="selected.flag" alt="" class="" />
<span class="">{{ selected.name }}</span>
</span>
<span class="">
<SelectorIcon class="" aria-hidden="true" />
</span>
</ListboxButton>
<transition leave-active-class="" leave-from-class="opacity-100" leave-to-class="opacity-0">
<ListboxOptions class="">
<ListboxOption as="template" v-for="country in posts" :key="country" :value="country" v-slot="{ active, selected }">
<li :class="">
<div class="">
<img :src="country.flag" alt="" class="" />
<span :class="[selected ? 'font-semibold' : 'font-normal', 'ml-3 block truncate']">
{{ country.latin }}
</span>
</div>
<span v-if="selected" :class="">
<CheckIcon class="" aria-hidden="true" />
</span>
</li>
</ListboxOption>
</ListboxOptions>
</transition>
</div>
</Listbox>
</template>
<script>
import { ref } from 'vue'
import { Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions } from '#headlessui/vue'
import { CheckIcon, SelectorIcon } from '#heroicons/vue/solid'
import axios from 'axios'
export default {
data() {
return {
response: null,
posts: undefined,
};
},
components: {
Listbox,
ListboxButton,
ListboxLabel,
ListboxOption,
ListboxOptions,
CheckIcon,
SelectorIcon,
},
mounted: function() {
axios.get('http://localhost')
.then(response => {
this.posts = response.data;
});
},
setup() {
const selected = ref(posts[30])
return {
selected,
}
},
}
</script>
The offending line is const selected = ref(posts[30]) which I know I need to somehow define posts, but I don't get how?
CAUSE OF YOUR ERROR:
You are trying to access an array element before the array is populated. Thus the undefined error.
EXPLANATION
You are using a mix of composition api and options api. Stick to one.
I am writing this answer assuming you will pick the composition api.
Follow the comments in the below snippet;
<script>
// IMPORT ONMOUNTED HOOK
import { ref, onMounted } from 'vue'
import { Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions } from '#headlessui/vue'
import { CheckIcon, SelectorIcon } from '#heroicons/vue/solid'
import axios from 'axios'
export default {
// YOU DO NOT NEED TO DEFINE THE DATA PROPERTY WHEN USING COMPOSITION API
/*data() {
return {
response: null,
posts: undefined,
};
},*/
components: {
Listbox,
ListboxButton,
ListboxLabel,
ListboxOption,
ListboxOptions,
CheckIcon,
SelectorIcon,
},
// YOU DO NOT NEED THESE LIFE CYCLE HOOKS; COMPOSITION API PROVIDES ITS OWN LIFECYCLE HOOKS
/*mounted: function() {
axios.get('http://localhost')
.then(response => {
this.posts = response.data;
});
},*/
setup() {
// YOU ARE TRYING TO ACCESS AN ELEMENT BEFORE THE ARRAY IS POPULATED; THUS THE ERROR
//const selected = ref(posts[30])
const posts = ref(undefined);
const selected = ref(undefined);
onMounted(()=>{
// CALL THE AXIOS METHOD FROM WITHIN THE LIFECYCLE HOOK AND HANDLE THE PROMISE LIKE A BOSS
axios.get('http://localhost')
.then((res) => {
selected.value = res[30];
});
});
return {
selected,
}
},
}
</script>
According to your comment; you should first check if the “selected != null” before using ‘selected’ inside the template. You can use a shorthand version like this
<img :src=“selected?.flag” />

Can't get nuxt-link to work when trying to render _slug.vue page with Nuxt, Apollo and WPGraphQL

I have a nuxt/vue app using apollo to query WPGraphQL on Wordpress. Im having hard time setting up my nuxt-link on my index page to route to my _slug.vue page. If I manually enter the url on the browser using the post's slug, I am able to render the data I want. In my index page, how do I use the post.slug with params to get my _slug.vue page to render?
This is my GraphQL Query:
post(id: $slug, idType: SLUG) {
title
slug
date
id
content(format: RENDERED)
author {
node {
firstName
lastName
}
}
}
}
My /blog/index.vue page has the list of blog posts and I am trying to use nuxt-link to link each post to render _slug.vue:
<template>
<div class="blog">
<h1 class="blog__title">Blog</h1>
<nuxt-link
v-for="post in posts.nodes"
:key="post.slug"
:to="'blog/' + { params: { slug: post.slug } }"
class="blog__post"
>
<h3 class="blog__post-title">
{{ post.title }}
</h3>
<div class="blog__post-content" v-html="post.content" />
</nuxt-link>
</div>
</template>
<script>
import getPostByID from '~/apollo/queries/GetPostByID'
export default {
data() {
return {
posts: [],
query: ''
}
},
apollo: {
posts: {
prefetch: true,
query: getPostByID,
update: (data) => data.post
}
}
</script>
With my _slug.vue file below, It uses the same query as my blog page and is able to render if I type the proper url with slug on the browser:
<template>
<article class="post">
<h1>{{ post.title }}</h1>
<div class="blog__post-content" v-html="post.content" />
</article>
</template>
<script>
import GetPostByID from '~/apollo/queries/GetPostById'
export default {
data() {
return {
post: []
}
},
apollo: {
post: {
prefetch: true,
query: GetPostByID,
variables() {
return { slug: this.$route.params.slug }
}
}
}
}
</script>
And what exactly does ".slug" refer to from "this.$route.params.slug"?
If your index page is correctly displaying the list of posts, then you just need to adjust the url slightly.
<nuxt-link
v-for="post in posts.nodes"
:key="post.slug"
:to="'blog/' + { params: { slug: post.slug } }"
class="blog__post"
>
Should be:
<nuxt-link
v-for="post in posts.nodes"
:key="post.slug"
:to="`blog/${post.slug}`"
class="blog__post"
>
this.$route.params.slug refers to the url parameter you named by creating the dynamic file _slug.vue. So if you have pages/blog/_slug.vue and navigate to your-app.com/blog/my-first-post, my-first-post is the parameter string you get back when accessing this.$route.params.slug.
Slug isn’t a magical keyword, it could be anything instead and depends on the file name you create in your blog directory. Given the same url and pages/blog/_unicorn.vue, you would call this.$route.params.unicorn to return my-first-post.

VueJS Axios API, trying to get only 1 result, not many

I'm trying to get only 1 response from the Nasa images API using VueJS and Axios. I've followed the tutorial here(https://www.youtube.com/watch?v=GiIQce7Rx4Y&t=939s) and the app is working just like shown. But this tutorial shows how to get the complete list of images available through this API, I only want the first image but can't figure out how.
Here's the code for the api component:
<template>
<div class="search">
<section id="sectionB">
<h2>Type in your search term</h2>
<form v-on:submit.prevent="getResult(query)">
<input type="text" placeholder="Type in your search" v-model="query" />
</form>
<br/>
<div v-if="results">
<div v-for="result in results" v-bind:key="result">
<img v-bind:src="result.links[0].href" class="imgContainer" />
</div>
</div>
</section>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "search",
data() {
return {
msg: "Search",
query: "",
results: "",
};
},
methods: {
getResult(query) {
axios.get(
"https://images-api.nasa.gov/search?q=" + query + "&media_type=image"
)
.then((response) => {
console.log(response.data.collection.items);
this.results = response.data.collection.items;
});
},
},
};
</script>
I tried removing f-for to stop the loop but that removed all images, not just everyone after the first.
You should refer to the documentation of the API on the website of Nasa: https://images.nasa.gov/docs/images.nasa.gov_api_docs.pdf. The only endpoint they have that returns images is the search endpoint that you are using.
However, reading your question again.. You can just show the first of the list (array) of results: results[0], like below:
<div v-if="results">
<img v-bind:src="results[0].links[0].href" class="imgContainer" />
</div>

Axios setting response data in vue component variable but variable not displaying data on browser

Axios setting response data in Vue component variable but variable not displaying data on the browser. We can see data on console but when I display, Browser shows me nothing. Data are in JSON Format.
Snapshot:
Output:
<template>
<div class="card">
<div class="card-header">Subject</div>
<div class="card-body" >
<!-- <ul class="list-group">
<li class="list-group-item" :key = "message.id" v-for = "message in messages"> {{ message.subject}}</li>
</ul> -->
{{messages}}
</div>
</div>
</template>
<script>
export default{
name: 'subject-component',
data () {
return {
messages:{}
}
},
mounted () {
},
created(){
axios.get('http://127.0.0.1:8000/subject/get')
.then(response => {this.messages = response.data})
.catch((error) => console.log(error));
}
}
</script>
By setting up the project again, this issue has been resolved. And again I used the same approach for axios request like.
axios.get('http://127.0.0.1:8000/get/subjects')
.then((response)=>(this.subjects = response.data))
.catch(function(error){console.log(error)});
Thank you all for your effort.

VueJS - Ajax communication between templates

I'm very new to VueJS and i'm having a difficult to share a result from Two template, that come from AJAX Request.
This is the home page:
<div>
<search-bar></search-bar>
<tracking-results></tracking-results>
</div>
This is the search-bar component, where i have a text input field and after press the button, it will do an Ajax Request:
<template>
<div class="row">
<div class="col-lg-8 col-lg-offset-3">
<div class="col-lg-5">
<div class="input-group">
<input type="text" class="form-control" placeholder="Numero Spedizione" v-model="trackingNumber">
<span class="input-group-btn">
<button class="btn btn-default"
type="button"
#click.prevent="search">Ricerca</button>
</span>
</div><!-- /input-group -->
</div><!-- /.col-lg-3 -->
</div>
</div><!-- /.row -->
</template>
<script>
export default {
data() {
return {
trackingNumber: '',
}
},
methods: {
search() {
Vue.http.options.emulateJSON = true;
this.$http.post('/endpoint').then(function (response) {
var parsedResponse = JSON.parse(response.data) || undefined;
/* HERE I WANT TO SEND THE RESPONSE TO ANOTHER COMPONENT */
}, function (err) {
console.log('ERROR', err);
});
}
}
}
</script>
I did tried with $broadcast, but my components arent child, are sibling.
I did see a way can be Vuex, but my application will not be written entirely with Vue. I will use this framework just to "simplify some Javascript process".
The only alternative i did find is to "merge" the search-bar and tracking-result in a single component. In this way the data will be "shared", and i can communicate with the state.
[Update: sync is removed in Vue 2, so you would need to follow the standard props-down, events-up design pattern]
You can have the parent viewmodel pass a prop to each of the components, using sync for the search bar. The search bar would populate the value in the ajax call, it would sync up to the parent and down to the tracking-results.
Some example code:
Vue.component('child1', {
props: ['ajaxData'],
methods: {
loadData: function () {
this.ajaxData = 'Some data is loaded';
}
},
template: '<div>Child1: {{ajaxData}} <button v-on:click="loadData">Load data</button></div>'
});
Vue.component('child2', {
props: ['ajaxData'],
template: '<div>Child2: {{ajaxData}}</div>'
});
new Vue({
el: 'body',
data: {
hi: 'Hello Vue.js!'
}
})
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<child1 :ajax-data.sync='hi'></child1>
<child2 :ajax-data='hi'></child2>
Ideally, you can send data to the parent, then the parent send data to the component via props. The parent handles the communication between the siblings.
Another way of doing it is using state management or vuex. But that depends on the complexity of your project. If it's a simple thing, I suggest to let the parent handle the communication.