Nuxt.js - Unable to output nested array elements in Nuxt.js array - vue.js

I have json object but I am not able to output the nested arrays and really need some help!
Here is my script tag:
<script>
export default {
data() {
return {
mountains: []
}
},
async fetch() {
this.mountains = await fetch(
'https://api.knack.com/v1/objects/object_2/records'
, {
method: "GET",
headers: {
"X-Knack-Application-Id": "appIDhere",
"X-Knack-REST-API-KEY": "keyIDhere",
},
}).then(res => res.json())
// .then((res) => {
// console.log(res);
// })
}
}
</script>
and it gives me in my console:
{
"id": "60f648317f54bf001f516226",
"field_77_raw": {
"id": "60f6479f6bcca0001e7ebf28",
"application_id": "60da978576b89a001fbc7e36",
"s3": true,
"type": "image",
"filename": "screenshot20210625at1.38.44pm.png",
"url": "https://s3-ap-southeast-2.amazonaws.com/ap-southeast-2-assets.knack.com/assets/60da978576b89a001fbc7e36/60f6479f6bcca0001e7ebf28/original/screenshot20210625at1.38.44pm.png",
"thumb_url": "https://s3-ap-southeast-2.amazonaws.com/ap-southeast-2-assets.knack.com/assets/60da978576b89a001fbc7e36/60f6479f6bcca0001e7ebf28/thumb/screenshot20210625at1.38.44pm.png",
"size": 2621440,
"field_key": "field_77"
},
but when I do my loops in my template section I can't the thumb_url:
<template>
<div v-for="mountain of mountains.records">
{{mountain.id}}
{{mountain.field_77_raw.url}}
</div>
</template>
I just get a message: Cannot read property 'url' of undefined
If I put {{mountain.field_77_raw}} I get:
{ "id": "60f6479f6bcca0001e7ebf28", "application_id": "60da978576b89a001fbc7e36", "s3": true, "type": "image", "filename": "screenshot20210625at1.38.44pm.png", "url": "https://s3-ap-southeast-2.amazonaws.com/ap-southeast-2-assets.knack.com/assets/60da978576b89a001fbc7e36/60f6479f6bcca0001e7ebf28/original/screenshot20210625at1.38.44pm.png", "thumb_url": "https://s3-ap-southeast-2.amazonaws.com/ap-southeast-2-assets.knack.com/assets/60da978576b89a001fbc7e36/60f6479f6bcca0001e7ebf28/thumb/screenshot20210625at1.38.44pm.png", "size": 2621440, "field_key": "field_77" }
But don't know how to iterate through this nested array. I think it is an object so possibly I just don't know how to handle an object...
Thanks!
Edit:
Here is the whole json output https://codepen.io/MikedNZ/pen/JjNrJgV
Answer thanks #tony19
<template>
<div>
<div v-for="mountain of mountains.records">
{{mountain.id}}
</div>
<div v-if="mountain.field_77_raw">.
{{mountain.field_77_raw.url}}
</div>
</div>
</template>

The JSON response you posted has 8 array elements without field_77_raw, so your template needs to conditionally render that field with v-if.
Also, I don't see the records key in the data. If the API response is just an array, you're assigning the response directly to this.mountains, so the v-for should iterate mountains (not mountains.records).
<div v-for="mountain of mountains">
{{mountain.id}}
<template v-if="mountain.field_77_raw">
{{mountain.field_77_raw.url}}
</template>
</div>
demo

Related

Getuikit 3 sortable element on moved not triggered

For some reason I can't perform actions after the sortable elements are moved. The event UIkit.util.on('#sortable', 'moved', function (item) {}); is not being called/triggered.
The app is made with vue.js, but I get no error and every other uikit functionality works just fine. There are ~600 line of code so, I'll just show the ones that matter (I think).
<template>
<div class="products">
<MainMenu />
<div id="ordering" uk-offcanvas="overlay: true">
<div class="uk-offcanvas-bar" style="width:500px">
<button class="uk-offcanvas-close" type="button" uk-close></button>
<div>Drag to re-arrange the fields order<br><br>
<ul id="sortable" class="uk-grid-small uk-child-width-1-1 uk-text-center" uk-sortable="handle: .uk-card" uk-grid>
<li v-for="(data, idx) in computedData" :key="idx" :id="data.product_field_name">
<div class="uk-card uk-card-default uk-card-body uk-padding-small">{{data.product_field_name}}</div>
</li>
</ul>
</div>
</div>
</div>
<button type="button" class="uk-button uk-button-default" uk-toggle="target: #ordering"><span uk-icon="move"></span> Field Ordering</button>
<!-- REST OF HTML GOES HERE: FORMS, MODALS, ETC. -->
</div>
</template>
<script>
UIkit.notification('test message','danger'); //THIS TRIGGERS OK
//UIKit ordering action - THIS DOES NOT
UIkit.util.on('#sortable', 'moved', function (item) {
console.log('moved triggered');
});
// # is an alias to /src
import MainMenu from "#/components/MainMenuEMVO.vue";
import axios from "axios";
export default {
name: "products",
components: {
MainMenu
},
data() {
return {
add_dependency: {
field: "",
field_selection: ""
},
remove_dependency: {
id: "",
field_name: "",
dependency_field: "",
dependency_rule: "",
dependency_value: "",
enforcing_value: "",
},
productFields: "",
lookupTables: "",
dependencies: "",
departments: "",
search: "",
computedFields: "",
};
},
mounted() {
let stateCheck = setInterval(() => {
if (document.readyState === 'complete') {
clearInterval(stateCheck);
this.getProductsConfig();
}
}, 100);
},
computed: {...},
methods: {
getProductsConfig(){...},
enableEdit(id){...},
cancelEdit(id){...},
submitEdit(id){...},
addRule(id, field_name){...},
removeDependency(fieldName, id){...}
}
};
</script>
As mentioned before there are no errors or even warnings in console, so I really don't event know where to start looking at this.
I managed to resolve the problem, so if anyone has the same issue:
Once I moved the code into the mounted() method of the vue.js it all works fine.
mounted() {
//UIKit ordering action - THIS DOES NOT
UIkit.util.on('#sortable', 'moved', function (item) {
console.log('moved triggered');
});
let stateCheck = setInterval(() => {
if (document.readyState === 'complete') {
clearInterval(stateCheck);
this.getProductsConfig();
}
}, 100);
},
the UIkit.notification works fine, but I guess since the util is attached to event it has to be made available to vue on startup.
If you added UIkit as <script src="./src/js/uikit.min.js"></script> in your index.html then you can't use it inside Vue as expected, instead do:
npm i --save uikit
then in your Component
import UIkit from "uikit";
after that you can use everywhere
methods: {
alertOnClick(msg) {
UIkit.notification(msg, "danger");
}
}
}

How get the data from an array like this with axiosjs

I connected to a URL using Axios using this:
getUsers: function() {
axios.get(urlUsers).then(response => {
this.lists = response.data
});
and get this data:
"lists": [
{
"name": "Destacados",
"tags": [
"Aguila"
],
"isRoot": true,
"products": [
{
"name": "Coors",
"code": "139017",
And tryng to list products.
How?
You can use v-for to render lists. In your case you have a nested array so you would need to do it twice. However I think you should change your data element 'lists' and remove all the excess quotes ("") so it looks like this:
lists: [
{
name: "Destacados",
tags: [
"Aguila",
],
isRoot: true,
products: [
{
name: "Coors",
code: 139017,
},
{
name: "Bud",
code: 139019,
}
],
}
]
and then run your v-for loops:
<template>
<div v-for="list in lists" :key="list.name">
<ul v-for="product in list.products :key="product.name">
<li>{{product.name}}</li>
<li>{{product.code}}</li>
</ul>
</div>
</template>

Vue v-for list does not update after data changed

The idea is simple:
The Vue instance loads groups from an API.
This groups are shown using a v-for syntax.
The groupdata is formatted properly, and added to app.groups using .push function, which should update the v-for list.
However, the v-for list will not update.
When I change v-for="group in groups" (loaded externally) to v-for="group in people" (already in the app), it does give output.
HTML
<div id="app" class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Realtime Socket Chat</div>
<div class="panel-body" v-if="dataReady" v-for="group in groups"> #{{ group.name }}
<a v-bind:href="group.link" class="pull-right">
<div class="btn btn-xs btn-primary">
Join
</div>
</a>
</div>
</div>
</div>
</div>
Vue
var app = new Vue({
el: "#app",
data: {
groups: [],
people: [{name: 'hi'}, {name: 'lol'}]
},
mounted: function() {
this.load()
},
methods: {
load: function() {
axios.get('http://example.com/groups')
.then(function (response) {
console.log(response.data.groups);
response.data.groups.forEach(function(group) {
app.groups.push(group);
});
// app.groups.forEach(function (group) {
// group.link = '/g/' + group.id + '/join';
// });
// console.log(response.data.groups);
console.log(this.groups); //outputs [{...}, {...}] so this.groups is good
})
.catch(function (error) {
this.errors.push(error);
console.log(error);
})
}
}
});
API
{
"groups": [
{
"id": 1,
"name": "Photography Chat",
"created_at": "2017-11-26 08:50:16",
"updated_at": "2017-11-26 08:50:16"
},
{
"id": 2,
"name": "Media Chat",
"created_at": "2017-11-26 08:50:16",
"updated_at": "2017-11-26 08:50:16"
}
]
}
It seems like app is undefined when your load function is executed. So, using ES6 arrow function syntax, your load function should look like this:
load: function() {
axios.get('http://example.com/groups')
.then(response => {
let groups = response.data.groups || []
groups.forEach(group => {
this.groups.push(group)
})
console.log(this.groups)
})
.catch(error => {
this.errors.push(error)
console.log(error)
})
}
A fact I left out in the question (because I assumed it would not matter) was that I am working within the Laravel framework, Laravel automatically imports Vuejs within main.js. Which does not play nice when Vue is loaded in additionally. I removed main.js and it works.

Data from Vuex storage are not displayed in the vue component

The Vuex storage has a getter which returns object by id.
But I can't use the data from the getter.
When i use computed: data come from getter after the component was rendered, and DOM still empty (until I'm not clicking on component in vue devtools. If i click, computed run again, and element are redrawn and filled with the data from data())
if I call the getter directly during the definition of data() objects, {{ cardData.content }} in DOM can be seen immediately, but this.cardData.title returns an error Cannot read property 'title' of undefined. Why?
Vuex storage
export const store = new Vuex.Store({
state: {
"cards": [
{
"position": null,
"title": "Head Text",
"id": 132,
"content": "LoremIpsum",
"type": "text",
},
{
"position": null,
"title": "Head Text 2",
"id": 138,
"content": "LoremIpsumLoremIpsum",
"type": "text",
}
]
},
getters: {
cards: (state) => state.cards
}
});
vue component,
<template>
<div>
<h1>{{ cardTitle }}{{ cardTitle2 }}</h1>
<div>{{ cardData.content }}</div>
</div>
</template>
<script>
export default {
props: ['id'],
data() {
return {
cardData: this.$store.getters.cards.find((card) => card.id == this.id),
cardTitle: this.cardData.title,
cardData2: null,
cardTitle2: null
}
},
computed: {
GetData: function() {
this.cardData2 = this.$store.getters.cards.find((card) => card.id == this.current);
this.cardTitle2 = this.cardData2.title;
},
}
}
</script>
Why not just have a computed property which returns desired title, it can be just following as documented here:
computed: {
cardTitle: function() {
var card = this.$store.getters.cards.find((card) => card.id == this.current)
return card.title
},
}
Now you can use {{this.cardTitle}} in the DOM.
Getting Vuex State into Vue Components
So how do we display state inside the store in our Vue components?
Since Vuex stores are reactive, the simplest way to "retrieve" state
from it is simply returning some store state from within a computed
property
In your case you have card array in vuex state, and you use data method to return array. That is not proper way to handle this case. You need to use computed method as a getter. Whenever this.$store.state.card changes, it will cause the computed property to re-evaluate, and trigger associated DOM updates:
computed: {
cardData() {
return this.$store.getters.cards
}
}
And in your component template you can display properties from cardData array, whether it needs to be title, content, type, id...etc. You don't need to create cardTitle, cardId, cardType variables for every property in array, just add property after cardData:
<template>
<div class="card">
<h1>{{ cardData.title }}</h1>
<p>{{ cardData.content }}</p>
</div>
</template>
Here is jsFiddle example for your case, filtered cardData array by id.
It is more efficient if you loop through the array using v-for as indicated in the vue.js documentation homepage.
computed: {
cards () {
return this.$store.getters.cardData
}
}
this.$store.getters.cardData in computed being what you have initialised in getters: i.e
getters: {
cardData: (state) => state.cards
}
and state like so:
state: {
cards: []
}
then in your template tag, you can loop through the dataset array like so:
<template id="insert id here">
<ul v-for"card in cards" :key="card.id">
<li> {{ card.title }}</li>
<li> {{ card.content }}</li>
</ul>
</template>

Passing data into a Vue template

I am fairly new to vue and can't figure out how to add data values within a template. I am trying to build a very basic form builder. If I click on a button it should add another array of data into a components variable. This is working. The I am doing a v-for to add input fields where some of the attributes are apart of the array for that component. I get it so it will add the input but no values are being passed into the input.
I have created a jsfiddle with where I am stuck at. https://jsfiddle.net/a9koj9gv/2/
<div id="app">
<button #click="add_text_input">New Text Input Field</button>
<my-component v-for="comp in components"></my-component>
<pre>{{ $data | json }}</pre>
</div>
new Vue({
el: "#app",
data: function() {
return {
components: [{
name: "first_name",
showname: "First Name",
type: "text",
required: "false",
fee: "0"
}]
}
},
components: {
'my-component': {
template: '<div>{{ showname }}: <input v-bind:name="name" v-bind:type="type"></div>',
props: ['showname', 'type', 'name']
}
},
methods: {
add_text_input: function() {
var array = {
name: "last_name",
showname: "Last Name",
type: "text",
required: "false",
fee: "0"
};
this.components.push(array);
}
}
})
I appreciate any help as I know I am just missing something obvious.
Thanks
Use props to pass data into the component.
Currently you have <my-component v-for="comp in components"></my-component>, which doesn't bind any props to the component.
Instead, do:
<my-component :showname="comp.showname"
:type="comp.type"
:name="comp.name"
v-for="comp in components"
></my-component>
Here is a fork of your fiddle with the change.
while asemahle got it right, here is a boiled down version on how to expose data to the child component. SFC:
async created() {
await this.setTemplate();
},
methods: {
async setTemplate() {
// const templateString = await axios.get..
this.t = {
template: templateString,
props: ['foo'],
}
},
},
data() {
return {
foo: 'bar',
t: null
}
}
};
</script>
<template>
<component :is="t" :foo="foo"></component>
It pulls a template string that is compiled/transpiled into a js-render-function. In this case with Vite with esm to have a client-side compiler available:
resolve: {
alias: {
// https://github.com/vuejs/core/tree/main/packages/vue#with-a-bundler
vue: "vue/dist/vue.esm-bundler.js",
the index.js bundle size increases by few kb, in my case 30kb (which is minimal)
You could now add some what-if-fail and show-while-loading with defineasynccomponent