I have a Svelte application that is supposed to perform CRUD operations with a local JSON file via the Fetch API.
The "GET" operations works as intended but when I tried to create the "POST" function, I got the below error message:
Uncaught (in promise) Error: {#each} only iterates over array-like objects.
at validate_each_argument (index.mjs:1977)
at Object.update [as p] (index.svelte? [sm]:33)
at update (index.mjs:1057)
at flush (index.mjs:1025)
Below is the code in index.svelte:
<script>
import { onMount } from 'svelte';
let data1 = '';
onMount(async function () {
const data = await (await fetch('http://localhost:5000/data1')).json();
data1 = data;
console.log(data);
});
const createData1 = async () => {
const data =
(await fetch('http://localhost:5000/data1'),
{
method: 'Post',
body: JSON.stringify({
id: data1.length + 1,
text: '',
}),
headers: {
'Content-Type': 'application/json'
}
});
data1 = data;
console.log(data);
};
</script>
<div style="display: grid; place-items:center;">
<div class="horizontal">
{#each data1 as d1}
<div contenteditable="true">{d1.text}</div>
{/each}
<button type="submit" on:click|preventDefault={createData1}>+</button>
</div>
</div>
And below is the contents of the JSON file:
{
"data1": [
{
"id": 1,
"text": "blabla",
},
{
"id": 2,
"text": "bla bla",
}
]
}
Why isn't the the object being created? It is inside an array after all.
As your log shows, data is an object, not an array. data.captions is the array you want to iterate over.
So you'll want to slightly modify your createData method near the end:
const createData1 = async () => {
...
// data1 = data;
data1 = data.data1;
console.log(data);
};
It looks to me like you need to adjust your code syntax a bit and make sure you convert your response to JSON and you will be in business. This should work:
const data = (await fetch('http://localhost:5000/data1', {
method: "POST",
body: JSON.stringify({
id: data1.length + 1,
text: ''
}),
headers: {
"content-type": "application/json"
}
})).json();
data1 = data;
The notable adjustments are:
Use the fetch overload that takes first argument as URL and the second argument as the request options object (Looks like this is what you were after and an extra "(" character was throwing it of)
Make sure to convert the result of the POST call to JSON. Your "GET" request does this conversion and you get what you are expecting.
Related
Trying to make a custom async schema validator in mongoose, to check that "tags" for a course being created contains at least one item. (Using SetTimeout() to simulate async). The part of the Schema for tags is :
tags: {
type: Array,
validate: {
isAsync: true,
validator: function (v, cb) {
setTimeout(() => {
//do some async work
const result = v && v.length > 0;
cb(result);
}, 3000);
},
message: "A course should have at least one tag!",
},
},
The code for creating a course is:
async function createCourse() {
const course = new Course({
name: "Node.js Course",
author: "Anon",
category: "web",
tags: [],
isPublished: true,
price: 13,
});
try {
const result = await course.save();
cl("createCourse result", result);
} catch (ex) {
cl("createCourse validate error", ex.message);
}
}
createCourse();
I have an empty array for tags and expected the caught error "A course should have at least one tag". Instead I am getting TypeError: cb is not a function for cb(result), the callback result? Even if I have an item in the tags array it still gives the callback error and in fact it displays the createCourse result BEFORE the schema async completes and then throws the error when it does complete! (If I dont use the async validator but just a plain validator then it works fine).
tags: {
type: Array,
validate: {
validator: function(v) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(v && v.length > 0);
}, 3000);
})
},
message: 'A course should have at least one tag.'
}
},
After trial and error, I came up with the solution below. No changes needed to createCourse(), just to the Schema tags section and added a delay function.
tags: {
type: Array,
validate: {
//isAsync: true,
validator: async function (v) {
await delay(3);
const result = v && v.length > 0;
return result;
},
message: "A Document should have at least one tag!",
},
},
And this calls a delay function, used to simulate a "real" async situation where this data may be being saved to a remote server.
const delay = (n) => {
return new Promise(function (resolve) {
setTimeout(resolve, n * 1000);
});
};
I want to show list product from api but it shows the error:
Uncaught (in promise) TypeError: Cannot read property 'id' of null
at eval (Home.vue?bb51:103)
at renderList (runtime-core.esm-bundler.js?5c40:6635)
at Proxy.render (Home.vue?bb51:2)
at renderComponentRoot (runtime-core.esm-bundler.js?5c40:1166)
at componentEffect (runtime-core.esm-bundler.js?5c40:5265)......
my product like :
[
{
"id": 1,
"name": "chair",
"categoryId": 12,
"unitId": 2,
"price": 66000000,
"salePrice": 0,
"material": "wood",
"size": "x"
},
]
My code here:
Home.vue file
<ProductCard v-for="product in products" :key="product.id" :product="product" />
ProductCard.vue file
<script>
export default {
name: "ProductCard",
props: {
product: {
type: Object,
required: true,
},
},
};
</script>
ProductService.js file
const apiClient = axios.create({
baseURL: 'http://localhost:8888/api/v1',
withCredentials: false,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
})
export default {
getProducts() {
return apiClient.get('/product/get-list-product-by-subcategory')
},
}
When I print out list product in console. It still work.
Does anyone know where is the bug in my code?
Updated:
I try to fix my bug "Cannot read property 'id' of null", Steve's answer although remove my red warning in devtool but not deal my data: my data still not showing up. And I find out my code work by using this.products = response.data.data
ProductService.getProducts()
.then((response) => (this.products = response.data.data))
.catch((error) => console.log("error: " + error));
Explain by myself is:
When console.log(this.products = response)
And I need to use this.products = response.data.data to enter to array
apiClient.get(...)
returns a promise not the actual data from the API call.
You need to add a then. like so
apiClient.get(...).then(response => (this.products = response))
Then when the apiClient.get completes this.products will be populated with the data from the API.
Try this
<ProductCard v-for="product in products" :key="product._id" :product="product" />
I want to create a new document in Firestore using the REST API.
Very good examples here using Axios to send the POST request with some fields:
https://www.jeansnyman.com/posts/google-firestore-rest-api-examples/
axios.post(
"https://firestore.googleapis.com/v1/projects/<PROJECTIDHERE>/databases/(default)/documents/<COLLECTIONNAME>",
{
fields: {
title: { stringValue: this.title },
category: { stringValue: this.category },
post: { stringValue: this.post },
summary: { stringValue: this.description },
published: { booleanValue: this.published },
created: { timestampValue: new Date() },
modified: { timestampValue: new Date() }
}
}
).then(res => { console.log("Post created") })
And an example here using Python Requests:
Using the Firestore REST API to Update a Document Field
(this is a PATCH request but the field formatting is the same as in a POST request)
import requests
import json
endpoint = "https://firestore.googleapis.com/v1/projects/[PROJECT_ID]/databases/(default)/documents/[COLLECTION]/[DOCUMENT_ID]?currentDocument.exists=true&updateMask.fieldPaths=[FIELD_1]"
body = {
"fields" : {
"[FIELD_1]" : {
"stringValue" : "random new value"
}
}
}
data = json.dumps(body)
headers = {"Authorization": "Bearer [AUTH_TOKEN]"}
print(requests.patch(endpoint, data=data, headers=headers).json())
I am using Google Apps Script UrlFetchApp.fetch to send my requests. I am able to use GET requests with no problems. For example, to get all the documents in a collection (in Google Apps Script):
function firestore_get_documents(){
var options = {
headers: { 'Authorization': 'Bearer ' + ScriptApp.getOAuthToken() },
method:'GET'
}
var response = UrlFetchApp.fetch('https://firestore.googleapis.com/v1/projects/<PROJECTIDHERE>/databases/(default)/documents/myCollection', options);
var parsed = JSON.parse(response.getContentText());
return parsed;
}
This works nicely. And changing 'method' to 'POST' creates a new document in myCollection as expected. Then I try to add a POST body with some fields (or just one field):
function firestore_create_new_document(){
var options = {
headers: { 'Authorization': 'Bearer ' + ScriptApp.getOAuthToken() },
method:'POST',
payload: {fields: { title: { stringValue: 'newTitle' } } }, // If you comment out this line, it works as expected
muteHttpExceptions:true
}
var response = UrlFetchApp.fetch('https://firestore.googleapis.com/v1/projects/<PROJECTIDHERE>/databases/(default)/documents/myCollection', options);
var contentText = response.getContentText();
var parsed = JSON.parse(response.getContentText());
return parsed;
}
I get the following errors:
code: 400 message: "Request contains an invalid argument."
status: "INVALID_ARGUMENT"
details[0][#type]: "type.googleapis.com/google.rpc.BadRequest"
details[0][fieldViolations][0][field]: "{title={stringValue=newTitle}}"
details[0][fieldViolations][0][description]: "Error expanding 'fields' parameter. Cannot find matching fields for path '{title={stringValue=newTitle}}'."
Documentation is available here:
https://firebase.google.com/docs/firestore/reference/rest/v1/projects.databases.documents/createDocument
https://firebase.google.com/docs/firestore/reference/rest/v1/projects.databases.documents#Document
The problem may be the formatting of my 'fields' object - I've tried several different formats from the documentation and examples
The problem may be that the fields don't exist yet? I think I should be able to create a new document with new fields
The problem may be with the way UrlFetchApp.fetch sends my JSON body. I have tried using payload = JSON.stringify(payload_object) and that doesn't work either.
I think UrlFetchApp is doing something slightly different than Axios or Python Requests - the body is getting sent differently, and not parsing as expected.
How about the following modification?
From:
var options = {
headers: { 'Authorization': 'Bearer ' + ScriptApp.getOAuthToken() },
method:'POST',
payload: {fields: { title: { stringValue: 'newTitle' } } }, // If you comment out this line, it works as expected
muteHttpExceptions:true
}
To:
var options = {
headers: { 'Authorization': 'Bearer ' + ScriptApp.getOAuthToken() },
method:'POST',
payload: JSON.stringify({fields: { title: { stringValue: 'newTitle' } } }),
contentType: "application/json",
muteHttpExceptions:true
}
When I tested above modified request, I could confirm that it worked. But if other error occurs, please tell me.
Reference:
Class UrlFetchApp
I am trying to take a single record from firebase to use in vuejs but I cant find out how to convert it to an array, if thats even what i should be doing.
my mutation
GET_CASE(state, caseId) {
state.caseId = caseId;
},
My action
getCase ({ commit, context }, data) {
return axios.get('http' + data + '.json')
.then(res => {
const convertcase = []
convertcase.push({ data: res.data })
//result below of what is returned from the res.data
console.log(convertcase)
// commit('GET_CASE', convertcase)
})
.catch(e => context.error(e));
},
I now get the following returned to {{ myCase }}
[ { "data": { case_name: "Broken laptop", case_status: "live", case_summary: "This is some summary content", contact: "", createdBy: "Paul", createdDate: "2018-06-21T15:20:22.932Z", assessor: "Gould", updates: "" } } ]
when all i want to display is Broken Laptop
Thanks
Example let obj = {a: 1, b: 'a'); let arr = Object.values(obj) // arr = [1, 'a']
async getCase ({ commit, context }, url) {
try {
let { data } = await axios.get(`http${url}.json`)
commit('myMutation', Object.values(data))
} catch (error) {
context.error(error)
}
}
But as I'm reading your post again, I think you don't want array from object. You want array with one object. So, maybe this is what you want:
async getCase ({ commit, context }, url) {
try {
let { data } = await axios.get(`http${url}.json`)
commit('myMutation', [data])
} catch (error) {
context.error(error)
}
}
Put this inside / after your .then
Object.keys(data).forEach(function(k, i) {
console.log(k, i);
});
With a response from Axios, you can get your data as:
res.data.case_name
res.data.case_number
....
Just build JavaScript object holding these properties and pass this object to your mutation. I think it is better than using an array.
const obj = {};
Object.assign(obj, res.data);
commit('GET_CASE', obj)
And in your mutation you do as follows:
mutations: {
GET_CASE (state, payload) {
for (var k in payload) {
if (payload.hasOwnProperty(k)) {
state[k] = payload[k]
}
}
}
}
Alternatively you can code your store as follows:
state: {
case: {},
...
},
getters: {
getCase: state => {
return state.case
},
....
},
mutations: {
GET_CASE (state, payload) {
state.case = payload
}
}
and you call the value of a case field form a component as follows:
const case = this.$store.getters.getCase
..... = case.case_name
I have the following:
require([
"dojo/dom",
"dojo/on",
"dojo/store/Observable",
"dojo/store/JsonRest",
"dojo/store/Memory",
"dgrid/OnDemandGrid"
], function (dom, on, Observable, JsonRest, Memory, OnDemandGrid) {
var store = new JsonRest({
target: 'client/list',
idProperty: 'id'
});
var grid = new OnDemandGrid({
columns: {
"id": "ID",
"number": "Name",
"description": "Description"
},
sort: "lastName",
store: store
}, "grid");
});
client/list is a rest url returning a json object {data:[...]}, but the content of the list never shows up :/
I think the problem is caused by the async data loading, because with a json hard coded object the content show up
EDIT :
I've succeeded in achieving this by using a dojo/request, but the JsonRest shouldn't normally act the same way ? Can someone point me to the right direction ?
require([
'dojo/dom',
'dojo/on',
'dojo/store/Memory',
'dojo/request',
'dgrid/OnDemandGrid'
], function (dom, on, Memory, request, OnDemandGrid) {
request('client/list', {
handleAs: 'json'
}).then(function (response) {
// Once the response is received, build an in-memory store with the data
var store = new Memory({ data: response });
// Create an instance of OnDemandGrid referencing the store
var grid = new OnDemandGrid({
store: store,
sort: 'id', // Initialize sort on id, ascending
columns: {
'id': 'ID',
'number': 'Name',
'description': 'Description'
}
}, 'grid');
console.log(store);
on(dom.byId('queryForm'), 'input', function (event) {
event.preventDefault();
grid.set('query', {
// Pass a RegExp to Memory's SimpleQueryEngine
// Note: this code does not go out of its way to escape
// characters that have special meaning in RegExps
description: new RegExp(this.elements.last.value, 'i')
});
});
on(dom.byId('queryForm'), 'reset', function () {
// Reset the query when the form is reset
grid.set('query', {});
});
});
});
Ok problem found :/
My "client/list" url was returning a json object like this:
{data: [{id:"1", label: "test"}, {id:"2", label: "test"}]}
Turns out that the JsonRest object is already encapsulating data in a data node, so by returning a json like this:
{[{id:"1", label: "test"}, {id:"2", label: "test"}]}
everything worked fine :)