Astronomy picture of the day Vue application - javascript

I am new to vue and I am trying to call the APOD (Astronomy picture of the day) from NASA so that I can display a new picture every day. I am making a new Vue component called Picture.vue where I do all of the accessing of the APOD api. I have been able to get the proper url for the picture I want to display from the response payload (and store in a variable called 'apod') but simply put I don't know how to put the "apod" variable as a 'src' value into either a regular HTML 'img' tag or a vuetify 'v-img' tag. I have a feeling this could be solved with v-bind but like I said I am new to Vue so any tips or guidance would be most appreciated.
Picture.vue
<section class="picture">
<h1>picture Component</h1>
<v-img src="{{apod}}"></v-img>
</section>
</template>
<script lang="js">
const url = "https://api.nasa.gov/planetary/apod?api_key=" + process.env.VUE_APP_KEY;
const axios = require('axios');
export default {
name: 'picture',
props: [],
async created () {
console.log("https://api.nasa.gov/planetary/apod?api_key=" + process.env.VUE_APP_KEY);
// axios.get(url)
// .then(response => this.apod = response.data.url);
const response = await axios.get(url);
this.apod = response.data.url;
console.log(response);
},
data () {
return {
apod: null
}
},
methods: {
},
computed: {
},
state () {
},
}
</script>
<style scoped lang="scss">
.picture {
}
</style>
App.vue
<template>
<v-app>
<Picture></Picture>
</v-app>
</template>
<script>
import Picture from './components/Picture'
export default {
name: 'App',
components: {
Picture,
},
data() {
return {
}
}
};
</script>
In summary my question is how can I put the 'apod' variable as the value of 'src' in an image tag (Vuetify or HTML)?
Thanks very much everyone happy easter!

Use :src and remove the {{ }}
like this
<v-img :src="this.apod" />

Related

Why does boostrap vue toaster dissapears immediately?

I am using Laravel + Vue.js to create a SPA. In the SPA, I am creating a form where user can write markdown content in it and click button to submit it. I want to show an error toaster at the bottom right corner of the screen if the user didn't input any content when they clicked the send button.
I am using boostrap vue toast to implement the error toaster.
However, when I clicked the send button, the error toaster will just blink for 1 second and dissapear immediately. Also, the error toaster blinks at top left corner which is different from what I wrote in the code below.
The mixin that contains the method to invoke the toast:
ErrorMessage.vue
# ErrorMessage.vue
<script>
export default {
methods: {
showErrorMessage (msg) {
this.$bvToast.toast(msg, {
title: ["Error!"],
variant: "danger",
toaster: "b-toaster-bottom-right"
});
}
}
};
</script>
I imported the above mixin in this vue component ArticleForm.vue.
ArticleForm.vue
<template>
<form #submit.prevent="passArticleData">
<div id="editor-markdown-editor">
<div id="editor-markdown">
<div
id="editor-markdown-tag-input"
#click="toggleTagModal"
>
<ul v-if="insertedTags.length">
<li v-for="tag in insertedTags"
:key="tag.id"
class="tag"
>
{{ tag.name }}
</li>
</ul>
<span v-else>タグを入力してください</span>
</div>
<div id="editor-markdown-textarea">
<textarea :value="input" #input="update" ref="textarea"></textarea>
<div id="drop-here"></div>
</div>
</div>
<div id="editor-preview">
<article v-html="compiledMarkdown(input)" class="markdown-render" ref="articleHtml"></article>
</div>
</div>
</form>
</template>
<script>
import _ from "lodash";
import ErrorMessage from "./mixins/ErrorMessage";
import { markdownTable } from "markdown-table";
export default {
props: {
article: Object,
tags: Array
},
mixins: [ErrorMessage],
data () {
return {
articleToPass: {
title: "",
body: "",
isDraft: true
},
input: "",
tagsToPass: [],
insertedTags: [],
tagModalOpen: false
};
},
methods: {
update: _.debounce(function (e) {
this.input = e.target.value;
}, 300),
passArticleData (e) {
// Get title from input
try {
this.articleToPass.title = this.$refs.articleHtml.getElementsByTagName("h1").item(0).innerHTML;
} catch (e) {
this.showErrorMessage(["Body md can't be blank"]);
}
// Remove first line(title) from users' input
this.articleToPass.body = this.input.substring(this.input.indexOf("\n") + 1);
// tag id of written article
const tagIds = this.insertedTags.map(obj => obj.id);
this.tagsToPass = tagIds;
this.$emit("handle-new-data", this.articleToPass, this.tagsToPass);
}
}
Parent component of the above vue component:
ArticleCreate.vue
<template>
<div id="content-area">
<header-component></header-component>
<main id="editor-area">
<article-form :article="article" :tags="tags" #handle-new-data="postArticle"></article-form>
</main>
</div>
</template>
<script>
import HeaderComponent from "./Header.vue";
import ArticleForm from "./ArticleForm.vue";
export default {
data () {
return {
article: {
title: "",
body: "",
is_draft: true
},
tags: []
};
},
components: {
HeaderComponent,
ArticleForm
},
methods: {
postArticle (articleObj, tagsData) {
const data = { article: articleObj, tags: tagsData };
axios.post("/api/articles", data)
.then((res) => {
this.$router.push({ name: "article.show", params: { article: res.data } });
});
}
}
};
</script>
I tried:
changed this.$bvToast to this.$root.$bvToast (based on this issue)
downgrade my bootstrap version from 4.6.0 to 4.5.3 (based on this question)
I have spent trying to solve this but failed. Does anyone know why is this happening and how can I make it work? Please let me know if you need any extra information. Thank you in advanced.
As stated in this comment on stackoverflow that is usually a sign of a bootstrap version mismatch.
I was actually able to reproduce that issue and also fix it with rolling back to bootstrap v4
Broken with bootstrap 5
https://codesandbox.io/s/bootstrap-vue-toasting-broken-with-bootstrap-5-bqe2c
Working with bootstrap 4
https://codesandbox.io/s/bootstrap-vue-toasting-working-with-bootstrap-4-jk2jl
I have figured out the problem.
The problem is that bootstrap-vue css is not loaded properly in my project.
Just add
import "bootstrap-vue/dist/bootstrap-vue.css";
to app.js and it works perfectly fine now.
Thank you

vue-pdf unable load to all pages "undefined property"

I am using:
Vue.js, vue-route, Vuetify, Firebase (database here) and vue-pdf
I am trying to load all pdf pages using "vue-pdf" but I get this error when I try to read the pdf.
Uncaught TypeError: Cannot read property 'novel' of undefined
I have an object named novel which is set to null initially. When I get the data from Firebase, it gets assigned to this novel object. I followed the tutorial here to load multiple pdf pages. However, in the tutorial a direct URL is used, but in my case I trying to access a specific value inside my object which is "this.novel.nove_url". I'm not sure what I got wrong here, my code is below. Any help is much appreciated.
<template>
<div>
<Navbar />
<v-container fluid>
<h1>{{novel.title}}</h1>
<div class="novel-display container">
<pdf
v-for="i in numPages"
:key="i"
:src="src" //this should display the loaded pdf file
:page="i"
style="display: inline-block; width: 100%"
#num-pages="pageCount = $event"
#page-loaded="currentPage = $event"
></pdf>
</div>
<div class="text-center">
<v-pagination v-model="currentPage" :length="pageCount" :total-visible="7" circle></v-pagination>
</div>
</v-container>
<Footer />
</div>
</template>
<script>
import Navbar from "./Navbar";
import Footer from "./Footer";
import pdf from "vue-pdf";
import db from "../db";
// var loadPDF = pdf.createLoadingTask(
// "https://cdn.filestackcontent.com/wcrjf9qPTCKXV3hMXDwK"
// );
var loadingTask = pdf.createLoadingTask(this.novel.novel_url); //the file I would like to load
export default {
name: "Read",
components: {
Navbar,
Footer,
pdf,
},
data() {
return {
novel: null, // data from Firebase is saved here
src: loadingTask ,
currentPage: 0,
pageCount: 0,
numPages: undefined,
};
},
mounted() {
this.src.promise.then((pdf) => {
this.numPages = pdf.numPages;
});
},
created() {
let ref = db
.collection("Novels")
.where("novel_slug", "==", this.$route.params.novel_slug);
ref.get().then((snapshot) => {
snapshot.forEach((doc) => {
this.novel = doc.data();
this.novel.id = doc.id;
});
});
},
};
</script>
You cannot use this outside of export default (see here). In addition, the Firebase database fetch is asynchronous, so the novel property is only populated when the promise returned by get() is fulfilled (i.e. in the callback functions passed to the then() method)
You can initialize loadingTask in the created() hook, in get().then(), as follows (untested):
import Footer from "./Footer";
import pdf from "vue-pdf";
import db from "../db";
export default {
name: "Read",
components: {
Navbar,
Footer,
pdf,
},
data() {
return {
novel: null, // data from Firebase is saved here
src: null ,
currentPage: 0,
pageCount: 0,
numPages: undefined,
};
},
mounted() {
this.src.promise.then((pdf) => {
this.numPages = pdf.numPages;
});
},
created() {
let ref = db
.collection("Novels")
.where("novel_slug", "==", this.$route.params.novel_slug);
ref.get().then((snapshot) => {
snapshot.forEach((doc) => {
this.novel = doc.data();
this.novel.id = doc.id;
});
this.src = pdf.createLoadingTask(this.novel.novel_url);
});
},
};
</script>

how to move items to another component by click - vue.js

I was wondering how can I move my items -> book, from one component to another. I took this books from api and I show them in the list, so I have an ID.
<template>
<v-flex v-for="(book, index) in allBooks">
<div>Title: {{ book.title }}</div>
<i #click="markAsFavorite(book)" :class="{isActive: isMark}" class="fas fa-heart"></i>
</template>
//component books
<script>
name: 'Books',
data () {
return {
allBooks: [],
isMark: false,
favouriteBooks: []
}
},
mounted() {
axios.get("https://www.googleapis.com/books/v1/volumes?q=id:" + this.id)
.then(response => {
console.log(response)
this.allBooks = response.data.items.map(item => ({...item, isMark: false}))
console.log(this.allBooks)
})
.catch(error => {
console.log(error)
})
},
methods: {
markAsFavorite(book) {
this.isMark = !this.isMark
let favouriteAllBooks = this.favouriteBooks.push(book => {
book.id = // i dont know what?
})
},
}
</script>
//component favourite
<template>
<div class=showFavouriteBook>
<p></p>
</div>
</template>
I tried to compare this marked book ID to something, and then this array with marked books show in second template favourite. But I have no idea how to do this. Maybe somebody can prompt me something?
You should use a global eventBus for that. An 'eventBus' is another instance of Vue which is used to pass data via components tied to the main application.
At the root script of your application append the following:
const eventBus = new Vue({
data: function() {
return {
some_var: null,
}
}
});
You can use Vue mixin to have your event bus accessible globally easily:
Vue.mixin({
data: function() {
return {
eventBus: eventBus,
}
}
});
Now when you want to pass data between components you can use the bus:
Component 1
// for the sake of demo I'll use mounted method, which is invoked each time component is mounted
<script>
export default {
mounted: function() {
this.eventBus.some_var = 'it works!'
}
}
</script>
Component 2
<template>
<div>
{{ eventBus.some_var }} <!-- it works -->
</div>
</template>
In addition you can use $emit and $on.
Component 1
// for the sake of demo I'll use mounted method, which is invoked each time component is mounted
<script>
export default {
mounted: function() {
// emit 'emittedVarValue' event with parameter 'it works'
this.eventBus.$emit('emittedVarValue', 'it works!')
}
}
</script>
Component 2
<template>
<div>
{{ some_var }} <!-- "it works" once eventBus receives event "emittedVarValue" -->
</div>
</template>
<script>
export default {
data: function() {
return {
some_var: null
}
},
mounted: function() {
// waiting for "emittedVarValue" event
this.eventBus.$on('emittedVarValue', (data)=>{
this.some_var = data;
})
}
}
</script>
Hope this answer helps you.

How to access data property of vue component

I am new to Vue.js and I think its amazing. I have been tasked to start implementing some vue components in our non-greenfield web application and I thought I would start by implementing some self-contained "widgets" that have to deal heavily with state in my work's rails app.
Its working great as a self-contained component but I want to load it with a data attribute so the component know what it needs to deal with. My Vue file looks like (I have redacted parts of this due to IP concerns):
<template>
<div class="card">
<div class="card-body">
${{ b.id }}
</div>
<div class="card-footer bg--blue-sky">
${{ b.amount }}
</div>
</div>
</template>
<script>
export default {
data: function () {
return {
errors: [],
b: {
id: null,
amount: null
}
}
},
// Fetches posts when the component is created.
created: function () {
jQuery.ajax({
url: "/api/b/" + '2' + ".json",
method: 'GET',
dataType: "json"
})
.then(response => {
this.b = response.b
})
.catch(e => {
this.errors.push(e)
});
}
}
</script>
<style scoped>
</style>
The component is registered with:
import FiDis from '../components/fi_dis.vue'
Vue.component('fi_dis', FiDis);
document.addEventListener('turbolinks:load', () => {
const fi_dis = new Vue({
el: '#bs',
components: { FiDis }
})
});
And in my html.erb code I create the components with:
<div id="bs" policy="2">
<fi_dis data-b-id="1"></fi_dis>
<fi_dis data-b-id="2"></fi_dis>
</div>
This all works flawlessly, and does exactly what I want it to do except for one thing. I want to access the data-b-id attribute within the created function of the component (i.e. replace the number '2' in the url of the ajax call above with the value form the attribute). In this way, I hope for the component to handle ANY "fi_dis" I choose, merely by specifying the b-id in the data attribute I want it to handle.
How can I achieve this?
You communicate data values passing props from parent component to child components.
So for example you should define which props your component is allowed to receive:
import FiDis from '../components/fi_dis.vue'
Vue.component('fi_dis', FiDis);
document.addEventListener('turbolinks:load', () => {
const fi_dis = new Vue({
el: '#bs',
components: { FiDis },
props['bId'],
created() { // This is a lifecycle method
this.printPropertyValue();
},
methods: {
// Your custom methods goes here separeted by commas
printPropertyValue() {
console.log(this.bId);
}
}
})
});
And the sintax for passing the data from the component implementation is using v-bind:propertyName or :propertyName (short hand).
<div id="bs" policy="2">
<fi_dis :bId="1"></fi_dis>
<fi_dis :bId="2"></fi_dis>
</div>

VueJS 2 - How to Pass Parameters Using $emit

I am working on a modal component using VueJS 2. Right now, it basically works -- I click on a button and the modal opens, etc.
What I want to do now is create a unique name for the modal and associate the button with that particular button.
This is what I have in mind. The modal has a unique name property:
<modal name='myName'>CONTENT</modal>
And this would be the associate button:
<button #click="showModal('myName')"></button>
What I need to figure out is how to pass the parameter of showModal to the modal component.
Here is the method that I'm using in the root vue instance (i.e, NOT inside my modal component):
methods: {
showModal(name) { this.bus.$emit('showModal'); },
}
What I want to do is to access the name property in the component -- something like this:
created() {
this.bus.$on('showModal', () => alert(this.name));
}
But this shows up as undefined.
So what am I doing wrong? How can I access the name property inside the modal component?
NOTE: If you are wondering what this.bus.$on is, please see the following answer to a previous question that I asked: https://stackoverflow.com/a/42983494/7477670
Pass it as a parameter to $emit.
methods: {
showModal(name) { this.bus.$emit('showModal', name); },
}
created() {
this.bus.$on('showModal', (name) => alert(name));
}
Also, if you want to give the modal a name, you need to accept it as a prop in the modal component.
Vue.component("modal",{
props:["name"],
...
})
Then I assume you will want to do something like,
if (name == this.name)
//show the modal
<!-- File name is dataTable.vue -->
<template>
<div>
<insertForm v-on:emitForm="close"></insertForm>
</div>
</template>
<script>
import InsertForm from "./insertForm";
import Axios from "axios";
export default {
components: {
InsertForm
},
data: () => ({
}),
methods: {
close(res) {
console.log('res = ', res);
}
}
};
</script>
<!-- File name is insertForm.vue -->
<template>
<div>
<v-btn #click.native="sendPrameter">
<v-icon>save</v-icon>
</v-btn>
</div>
</template>
<script>
export default {
data: () => ({
mesage:{
msg:"Saved successfully",
color:'red',
status:1
}
}),
methods: {
sendPrameter: function() {
this.$emit("emitForm", this.mesage);
}
}
};
</script>
https://vuejs.org/v2/guide/components-custom-events.html

Categories

Resources