vue js not displaying nested json - javascript

My vue.js code is unable to access a reference in a returned json object.
The vue.js code
new Vue({
el: '#axios',
data: function() {
return {
bikes: null,
baseurl: "https://" + document.querySelector('#axios').dataset.hostportname + "/",
}
},
mounted () {
axios({
method: 'get',
url: 'api/v1/bicycles',
baseURL: this.baseurl
})
.then(response => {
this.bikes = response.data
console.log("now bikes are " + JSON.stringify(this.bikes[0].owner.userName));
})
.catch(error => console.log("There is an error getting bikes: " + error))
}
})
If the HTML file (just a part) is
<div id="axios" th:data-hostportname="${hostportname}">
<li v-for="bike in bikes" :key="bike.id">
{{ bike.make }} ------- {{ bike.owner }}
</li>
</div>
Then the html output is
dawes -------- { "id": 1, "userName": "user1"}
whyte -------- { "id": 2, "userName": "user2"}
whyte -------- { "id": 3, "userName": "user3"}
And the console.log output is
now bikes are "user1"
if I try to output the owner id alone
<div id="axios" th:data-hostportname="${hostportname}">
<li v-for="bike in bikes" :key="bike.id">
{{ bike.make }} ------ {{ bike.owner.id }}
</li>
</div>
no output with a console error of
TypeError: "bike.owner is undefined"
So the Axios code is returning the correct data. Each object in the array is accessible. But the nested object within each array member is not accessible at a field level.
Just to make clear, if I ask for {{ bike.owner }} then I get a displayed the oener record that is referenced by the bike record. If I ask for {{ bile.owner.id }} then I get the console.log error of bike.owner is undefined and nothing is displayed. So I don't see how this is a loading problem unless bike.owner.id takes longer to retrieve than bike.owner, even though the latter displays.
Can someone explain what I am misunderstanding?
Regards.

So I found the root cause and it had nothing to do with vue.
Basically I have two models - owners and bicycles. Bicycles have a ref to a single owner. And owners have ref to an array of bicycles. I added #JsonIdentityInfo to both. And that was my problem.
Changing this to having #JsonIdentityInfo only in owner then allowed it to work.
Many thanks for all of the suggestions.

I think the response you are getting is as follows where owner is also a string.
So you might have to parse it before assigning it to bikes.
[{
make: 'one',
owner: '{ "id": 1, "userName": "user1"}'
}, {
make: 'two',
owner: '{ "id": 2, "userName": "user2"}'
}, {
make: 'three',
owner: '{ "id": 3, "userName": "user2"}'
}]
Hope this helps.
Update 1
Can you try adding a loading flag?
https://v2.vuejs.org/v2/cookbook/using-axios-to-consume-apis.html
new Vue({
el: '#axios',
data: function() {
return {
bikes: null,
loading: true
baseurl: "https://" + document.querySelector('#axios').dataset.hostportname + "/",
}
},
mounted () {
axios({
method: 'get',
url: 'api/v1/bicycles',
baseURL: this.baseurl
})
.then(response => {
this.bikes = response.data
console.log("now bikes are " + JSON.stringify(this.bikes[0].owner.userName));
})
.catch(error => console.log("There is an error getting bikes: " + error)).finally(() => this.loading = false)
}
})
And add v-if to your block
<div id="axios" v-if="!loading" th:data-hostportname="${hostportname}">
<li v-for="bike in bikes" :key="bike.id">
{{ bike.make }} ------ {{ bike.owner.id }}
</li>
</div>

Related

Why do I get a blank page when I click on my textarea zone with react-wysiwyg?

After getting the API response inside MenuItemDetail, I set it to the responses state, which is then passed down to Tabs, then TabContent. Here, I want to read correctly its resp and name props then display whatever they contain in the two corresponding editors i.e respEditor and nameEditor (resp and name fields in my json from API).
I can retrieve my resp and name from my API, but when I try to click on the textarea zone in order to add or modify the content I got a blank page, and get this error in the console:
Uncaught TypeError: contentState.getBlockMap is not a function
i've check this question on the forum : DraftJS - contentState.getBlockMap is not a function
I really really don't know why
const TabContent = ({ onChange, resp, , status }) => {
const [respEditor, setRespEditor] = useState(
EditorState.createWithContent(convertFromHTML(resp !== null ? resp : ""))
);
function respChange(state) {
setRespEditor(state);
onChange({
resp: convertToRaw(state.getCurrentContent()),
status
});
}
let handle = async (e) => {
e.preventDefault();
try {
let res = await fetch("", {
method: "POST",
body: JSON.stringify({
resp: convertToHTML(respEditor.getCurrentContent()),
status: status
})
});
} catch (err) {
console.log(err);
}
};
return (
<form onSubmit={handle}>
<Editor
editorState={respEditor}
onEditorStateChange={respChange}
wrapperclassName="wrapperclassName"
editorclassName="editorclassName"
toolbar={{
options: ["inline"],
inline: { inDropdown: false }
}}
/>
<input
type="radio"
value="B"
onChange={(e) =>
onChange({
resp,
status: e.target.value
})
}
<input
</span>
</div>
</form>
);
};
Here is my json from api for menuId:1:
[
{
"menuId": 1,
"name": "Menu1",
"trust":1,
"dishes": {
"meat": "N/A",
"vegetables": "pea"
},
"list": [
{
"resp": "resp1",
"question": "question1",
"link": "",
"name": "Name1",
"status": "Finished"
},
{
"resp": "resp2",
"question": "question2",
"link": "http://mylink.com",
"name": "Name2",
"status": "Saved"
}
]
}
]
Solution Codesanbox: https://codesandbox.io/s/react-wysiwyg-draft-js-ecmpth?file=/src/TabContent.js
Edited
You can use convertFromHTML from draft-convert to parse HTML string to EditorContent. Install draft-convert, which is the recommended way in the official example. (reference: https://stackoverflow.com/a/36904924/9744063)
npm install draft-convert
import { convertFromHTML } from ‘draft-convert’;
const [respState, setRespState] = useState(EditorState.createWithContent(convertFromHTML(resp)));
const [nameState, setNameState] = useState(EditorState.createWithContent(convertFromHTML(name)));

Vue.js: Return image with method after axios.get

<li v-for="people in projectData.employees" :key="people._id">
<b-img :src="colleagueImages(people)"
</li>
async colleagueImages(people) {
console.log(people); // => max#stackoverflow.com
let profileImage = await axios.get("http://myapilink.com/image?id=" + people + "&s=200&def=avatar", {
headers: {
'accept': 'image/jpeg'
}
});
console.log(profileImage);
return 'data:image/jpeg;base64,' + btoa(
new Uint8Array(profileImage.data)
.reduce((data, byte) => data + String.fromCharCode(byte), '')
);
}
The console.log(profileImage) returns the following:
The API I am using is returning a Base64 Image.
With my current code I only get the following error in my browser console:
[Vue warn]: Invalid prop: type check failed for prop "src". Expected String, got Promise.
Since you don't have all the data you need to render in the first place, you have to change attributes afterwards. First, you need to use Vue components for your items, so your "src" attribute will be reactive; second, you start the requests for your items after you rendered your app. Please see this mockup.
Vue.component('todo-item', {
template: `
<li>
<label>
<input type="checkbox"
v-on:change="toggle()"
v-bind:checked="done">
<del v-if="done">
{{ text }}
</del>
<span v-else>
{{ text }}
</span>
<span v-if="like">
♥ {{like}}
</span>
</label>
</li>
`,
props: ['id', 'text', 'done', 'like'],
methods: {
toggle: function(){
this.done = !this.done
}
}
})
let todos = [
{id: 0, text: "Learn JavaScript", done: false, like: null },
{id: 1, text: "Learn Vue", done: false, like: null },
{id: 2, text: "Play around in JSFiddle", done: true, like: null },
{id: 3, text: "Build something awesome", done: true, like: null }
]
const v = new Vue({
el: "#app",
data: {
todos: todos
}
})
todos.forEach((item) => {
// This is just a mock for an actual network request
window.setTimeout(() => {
item.like = Math.ceil(Math.random() * 100)
}, Math.random() * 2000)
})
https://jsfiddle.net/willywongi/gsLqda2y/20/
In this example I have the basic todo-list app with a fake "like" count for each item, which is calculated asynchronously. After setting up my app, I wait for the "like" attribute values (in my example I just wait a random value of milliseconds).

Trying to bind the query to the json string for every request in to the api in Vue.js

I'm trying to get results from an api based on the user search box. When the user enters a value 'en' or 'de'. They should get the result from that search. I need to bind the user input into my query string. This works when I manually code the country into the template, but not when I bind the value into the string after the user inputs a value for the second time. The 'get' request that uses the user input value 'query' works fine. But not when I bind this a second time
I want to be fit to access
results[i].query.name
But '.query' is not working when I query the data unless I enter the value manually '.en'
I have a json file that looks like the following
[
{
"en": {
"name": "testone",
"id": 5363289,
"location": "messages_en.properties1"
},
"de": {
"name": "testonede",
"id": 5363289,
"location": "messages_en.properties2"
}
},
{
"en": {
"name": "test2",
"id": 5363289,
"location": "messages_en.properties3"
},
"de": {
"name": "test2de",
"id": 5363289,
"location": "messages_en.properties4"
}
}
]
Below is my index.html vue.js template
<div id=#app>
<input type="text" v-model="query" placeholder="Choose Language" />
<div class="medium-6 columns">
<a #click="getResult(query)" class="button expanded">Retrieve</a>
</div>
<template v-for="(result, i) in results">
<div class="card" style="width: 20rem; display:inline-block;">
<div class="card-block"></div>
<p> {{results[i].query}} </p>
<!-- works when I manually code in the 'en' query but when ran with 'query' it returns an error 'Cannot read property 'name' of undefined"' second time it returns that the value is -->
<!-- <p> {{results[i].en.name}} </p> -->
<!-- <p> {{results[i].query.name}} </p> -->
</div>
</template>
</div>
Vue.js
el: '#app',
data () {
return {
search: '',
query: 'en',
results: '',
title: '',
items: '',
section: ''
}
},
methods: {
getResult(query) {
axios.get('http://localhost:3000/api/country?country=' + query + '&blank=true').then(response => {
this.results = response.data;
console.log(this.results);
});
},
You need to use bracket notation to access a property using a param, so:
results[i][query].name
The second issue is that results[i][query] will be undefined until the async call has completed, so you will need to check that the property is not undefined or use a boolean flag. So, to check that it is not undefined you could do something like:
<p v-if="!!results[i][query]">{{results[i][query].name}}</p>
<p v-else>Loading...</p>
Here's a simplified JSFiddle for that: https://jsfiddle.net/4w3dxm22/
Or you could just use a dataLoaded flag:
new Vue({
el: '#app',
methods:{
getResult(query) {
this.dataLoaded = false; // set dataLoaded to false
axios.get('http://localhost:3000/api/country?country=' + query + '&blank=true').then(response => {
this.results = response.data;
this.dataLoaded = true; // Data has loaded so set dataLoaded to true
});
},
data: {
dataLoaded: false
}
})
Then you can do:
<span v-if="dataLoaded">{{results[i][query].name}}</span>
<span v-else>Loading Data...</span>
Here's the simplified JSFiddle for that: https://jsfiddle.net/99ydx82u/

Show json result with vue.js

Hi i am try to show json file result with vue.js the target is that result will be showed on value.
this is my code:
data () {
return {
fetchData: function () {
var self = this;
self .$http.get( "/api/casetotalactivation", function( data ) {
self.items = data;
});
},
statsCards: [
{
type: 'warning',
icon: 'ti-server',
title: 'Cases',
value: this.items,
footerText: 'Updated now',
footerIcon: 'ti-reload'
}
],
use this code:
<div id="vueapp">
<textarea v-model="jsonstr" rows="8" cols="40"></textarea>
<pre>{{ jsonstr | pretty }}</pre>
</div>
and JS:
new Vue({
el: '#vueapp',
data: {
jsonstr: '{"id":1,"name":"A green door","price":12.50,"tags":["home","green"]}'
},
filters: {
pretty: function(value) {
return JSON.stringify(JSON.parse(value), null, 2);
}
}
})
HTML and JS have this built into the language. Try...
<pre>{{ yourObject }}</pre>
This gives the default indent, to specify a custom indent provide it as the third argument to JSON.stringify(...).
// replace 2 with '\t' to do tab indentation
<pre>{{ JSON.stringify(yourObject, null, 2) }}</pre>
If you're outside of Vue then using some combo of the above snipped will do.
just use <pre>
<pre>{{json}}</pre>
The following code shows how to show json result with Vue 3
Display a stringified json object inside a <textarea> using v-model
Display object properties with <li v-for="">
<template>
<div class="hello">
<textarea v-model="listDataString" rows="20" cols="80"></textarea>
<ul id="items">
<li v-for="(item, index) in listData" :key="index">
{{ `${item.text} [${item.id}]` }}
</li>
</ul>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "RenderList",
props: {
msg: String,
},
data() {
return {
listDataString: String,
listData: [], // placeholder
};
},
mounted() {
axios
.get("=== [API_ENDPOINT] ===")
.then((response) => {
this.listDataString = JSON.stringify(response.data, null, "\t");
this.listData = response.data;
console.log(this.listDataString);
return response; // multiline arrow function must return
});
},
};
</script>
If the /api is only on the dev server you can create a vue.config.js file in the app root folder.
module.exports = {
devServer: {
before: function(app, server) {
app.get('/api', function(req, res) {
const result = [{
type: 'warning',
icon: 'ti-server',
title: 'Cases',
value: this.items,
footerText: 'Updated now',
footerIcon: 'ti-reload'}];
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify(result));
});
}
}
}
With this files when I run npm run serve, I get the json object when navigating to /api and my regular app otherwise.
Just use this:
<pre v-html="JSON.stringify(objectJs, null, 2)"></pre>

Handlebars won't show one out of 6 values

So I'm linking in some values to an .hbs-file and most of the values is returned. But for some reason, one of the values is not returning anything. The .hbs:
<nav>
<h2>Working Files</h2>
{{#if snippets }}
<ul>
{{#each snippets}}
<li class={{ this.active }}>{{ this.fileType }}</span>
</li>
{{/each}}
</ul>
{{/if}}
</nav>
And I'm sending them in like this:
router.route("/home/:id")
.get(restrict, function(req, res) {
User.findOne({ user: req.session.Auth.username }, function(error, data) {
Snippet.find({ postedBy: data._id}).exec()
.then(function(data) {
Snippet.find({ _id: req.params.id}).exec()
.then((snippetID) => {
// This is what I send it -------------------------------------------------
let context = {
snippets: data.map(function(snippet) { // Gets the snippet info for nav
return {
name: snippet.title,
fileType: snippet.fileName,
id: "/home/" + snippet._id
};
}),
text: snippetID[0].snippet[0].text, // Gets the snippet-text, and writes it
sessionId: snippetID[0]._id, // For the CRUD to get the id
active: "active"
};
res.render("../views/home", context);
// ------------------------------------
}).catch((err) => {console.log(err)})
}). catch(function(err) {console.log(err)});
});
});
It's the "this.active" that holds no value at all. I've been scratching my head over and over about this, and I can't understand why that value won't follow. All the other values do follow. I've even tried to set the "active"-key to the same value as "id" or "text", but no luck.
Do anyone know what the problem is?

Categories

Resources