I want to read a JSON file from the API every 11 seconds and display it in the interface
In my case :
the interface server is running at http://localhost:8080/
the API at http://localhost:8088/route (and I need to refresh it every 11 seconds because parameters changes)
and in route.js :
var i=0;
var delayInms = 11000;
var myVar = setInterval(TempFunction, 1000);
function TempFunction() {
router.get('/', (req,res,next)=>{
var text =[
{"carspeed":[233+i,445+i,223+i,444+i,234+i]},
]
console.log(text);
res.status(200).json(text);
});
window.location.reload(true);
i++;
}
********THE PROBLEM is that I get this error:
ReferenceError: window is not defined
I have another question :
to read the JSON (which is updated in http://localhost:8088/route every 11 seconds) I did this :
in car.vue :
<template>
.
.
<ul>
<li v-for="todo of todos" :key="todo.id">{{todo.text}}</li>
</ul>
.
.
</template>
followed by :
<script>
import axios from 'axios';
const WorkersURL="http://localhost:8088/route";
export default {
data: () => ({
drawer: false,
todos:[]
}),
async created()
{
try
{
const res = await axios.get(WorkersURL);
this.todos=res.data;
}
catch(e)
{
console.error(e)
}
}
}
<script>
********AND THE SECOND PROBLEM : it doesn't read the JSON file from http://localhost:8088/route
You'll need to make sure that you are enabling your server to be hit from web pages running at a different host/domain/port. In your case, the server is running on a different port than the webpage itself, so you can't make XHR (which is what Axios is doing) calls successfully because CORS (Cross-Origin Resource Sharing) is not enabled by default. Your server will need to set the appropriate headers to allow that. Specifically, the Access-Control-Allow-Origin header. See https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
Second, to update your client every 11 seconds there are a few choices. The simplest would be to make you call via axios every 11 seconds using setInterval:
async created()
{
try
{
const res = await axios.get(WorkersURL);
this.todos=res.data;
// set a timer to do this again every 11 seconds
setInterval(() => {
axios.get(WorkersURL).then((res) => {
this.todos=res.data;
});
}, 11000);
}
catch(e)
{
console.error(e)
}
}
There are a couple of options that are more advanced such as serve-sent events (See https://github.com/mdn/dom-examples/tree/master/server-sent-events) or websockets (https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API). Both of these options allow you to control the interval on the server instead of the client. There are some things to consider when setting up your server for this, so the setInterval options is probably best in your case.
Related
I have a React application that calls the Places API through Google's dedicated places library.
The library is imported as such:
<script defer src="https://maps.googleapis.com/maps/api/js?key=<API_KEY>&libraries=places&callback=initPlaces"></script>
The code above is inside /public, in index.html. The initPlaces callback, specified in the URL looks as such:
function initPlaces() {
console.log("Places initialized");
}
To make the actual request, the following code is used:
async function makeGapiRequest() {
const service = new window.google.maps.places.AutocompleteService();
const response = await service.getQueryPredictions({
input: "Verona"
});
console.log(res);
}
For testing purposes, the function is called when the document is clicked:
document.addEventListener("click", () => {
makeGapiRequest();
});
On every request, there is a response coming back. For instance, when the input has the value of Verona, the following response is received, and is only visible in the browser network tab:
{
predictions: [
{
description: "Verona, VR, Italy",
...
},
...
],
status: "OK"
}
Whenever maleGapiRequest is called, even though there is a visible response from the API, the response variable is undefined at the time of logging, and the following error is thrown in the console:
places_impl.js:31 Uncaught TypeError: c is not a function
at places_impl.js:31:207
at Tha.e [as l] (places_impl.js:25:320)
at Object.c [as _sfiq7u] (common.js:97:253)
at VM964 AutocompletionService.GetQueryPredictionsJson:1:28
This code is thrown from the Places library imported in /public/index.html.
Did anyone encounter this error before, or has an idea as to what is the problem? I would like it if the solution was available to me, not the library.
The problem was that I was calling the wrong method. Instead of getQueryPredictions call the getPlacePredictions method. It will return different results, but you can configure it to suite your needs.
Old code:
async function makeGapiRequest() {
const service = new window.google.maps.places.AutocompleteService();
const response = await service.getQueryPredictions({
input: "Verona"
});
console.log(res);
}
New code:
async function makeGapiRequest() {
const service = new window.google.maps.places.AutocompleteService();
const response = await service.getPlacePredictions({
input: "Verona",
types: ["(cities)"]
});
console.log(res);
}
After putting off testing for a while now due to Cypress not allowing visiting chrome:// urls, I decided to finally understand how to unit/integration test my extension - TabMerger. This comes after the many times that I had to manually test the ever growing functionality and in some cases forgot to check a thing or two. Having automated testing will certainly speed up the process and help me be more at peace when adding new functionality.
To do this, I chose Jest since my extension was made with React (CRA). I also used React Testing Library (#testing-library/react) to render all React components for testing.
As I recently made TabMerger open source, the full testing script can be found here
Here is the test case that I want to focus on for this question:
import React from "react";
import { render, fireEvent } from "#testing-library/react";
import * as TabFunc from "../src/Tab/Tab_functions";
import Tab from "../src/Tab/Tab";
var init_groups = {
"group-0": {
color: "#d6ffe0",
created: "11/12/2020 # 22:13:24",
tabs: [
{
title:
"Stack Overflow - Where Developers Learn, Share, & Build Careersaaaaaaaaaaaaaaaaaaaaaa",
url: "https://stackoverflow.com/",
},
{
title: "lichess.org • Free Online Chess",
url: "https://lichess.org/",
},
{
title: "Chess.com - Play Chess Online - Free Games",
url: "https://www.chess.com/",
},
],
title: "Chess",
},
"group-1": {
color: "#c7eeff",
created: "11/12/2020 # 22:15:11",
tabs: [
{
title: "Twitch",
url: "https://www.twitch.tv/",
},
{
title: "reddit: the front page of the internet",
url: "https://www.reddit.com/",
},
],
title: "Social",
},
};
describe("removeTab", () => {
it("correctly adjusts groups and counts when a tab is removed", () => {
var tabs = init_groups["group-0"].tabs;
const { container } = render(<Tab init_tabs={tabs} />);
expect(container.getElementsByClassName("draggable").length).toEqual(3);
var removeTabSpy = jest.spyOn(TabFunc, "removeTab");
fireEvent.click(container.querySelector(".close-tab"));
expect(removeTabSpy).toHaveBeenCalledTimes(1);
expect(container.getElementsByClassName("draggable").length).toEqual(2); // fails (does not remove the tab for some reason)
});
});
I mocked the Chrome API according to my needs, but feel that something is missing. To mock the Chrome API I followed this post (along with many others, even for other test runners like Jasmine): testing chrome.storage.local.set with jest.
Even though the Chrome storage API is mocked, I think the issue lies in this function which gets called upon initial render. That is, I think the chrome.storage.local.get is not actually being executed, but am not sure why.
// ./src/Tab/Tab_functions.js
/**
* Sets the initial tabs based on Chrome's local storage upon initial render.
* If Chrome's local storage is empty, this is set to an empty array.
* #param {function} setTabs For re-rendering the group's tabs
* #param {string} id Used to get the correct group tabs
*/
export function setInitTabs(setTabs, id) {
chrome.storage.local.get("groups", (local) => {
var groups = local.groups;
setTabs((groups && groups[id] && groups[id].tabs) || []);
});
}
The reason I think the mocked Chrome storage API is not working properly is because when I manually set it in my tests, the number of tabs does not increase from 0. Which forced me to pass a prop (props.init_tabs) to my Tab component for testing purposes (https://github.com/lbragile/TabMerger/blob/f78a2694786d11e8270454521f92e679d182b577/src/Tab/Tab.js#L33-L35) - something I want to avoid if possible via setting local storage.
Can someone point me in the right direction? I would like to avoid using libraries like jest-chrome since they abstract too much and make it harder for me to understand what is going on in my tests.
I think I have a solution for this now, so I will share with others.
I made proper mocks for my chrome storage API to use localStorage:
// __mocks__/chromeMock.js
...
storage: {
local: {
...,
get: function (key, cb) {
const item = JSON.parse(localStorage.getItem(key));
cb({ [key]: item });
},
...,
set: function (obj, cb) {
const key = Object.keys(obj)[0];
localStorage.setItem(key, JSON.stringify(obj[key]));
cb();
},
},
...
},
...
Also, to simulate the tab settings on initial render, I have a beforeEach hook which sets my localStorage using the above mock:
// __tests__/Tab.spec.js
var init_ls_entry, init_tabs, mockSet;
beforeEach(() => {
chrome.storage.local.set({ groups: init_groups }, () => {});
init_ls_entry = JSON.parse(localStorage.getItem("groups"));
init_tabs = init_ls_entry["group-0"].tabs;
mockSet = jest.fn(); // mock for setState hooks
});
AND most importantly, when I render(<Tab/>), I noticed that I wasn't supplying the id prop which caused nothing to render (in terms of tabs from localStorage), so now I have this:
// __tests__/Tab.spec.js
describe("removeTab", () => {
it("correctly adjusts storage when a tab is removed", async () => {
const { container } = render(
<Tab id="group-0" setTabTotal={mockSet} setGroups={mockSet} />
);
var removeTabSpy = jest.spyOn(TabFunc, "removeTab");
var chromeSetSpy = jest.spyOn(chrome.storage.local, "set");
fireEvent.click(container.querySelector(".close-tab"));
await waitFor(() => {
expect(chromeSetSpy).toHaveBeenCalled();
});
chrome.storage.local.get("groups", (local) => {
expect(init_tabs.length).toEqual(3);
expect(local.groups["group-0"].tabs.length).toEqual(2);
expect(removeTabSpy).toHaveBeenCalledTimes(1);
});
expect.assertions(4);
});
});
Which passes!!
Now on to drag and drop testing 😊
My project were working fine. I just found out in console network that one of my GET request is sending twice, even I just send it once. See network console
If I comment the the whole code of created function, all GET request would no longer load/exist in the console network. (see code below)
I want to know what causes this, and how should I fix this?
Here is the Component.vue
<script>
export default {
created: async function() {
await this.$store.dispatch('file/all');
},
};
</script>
And the vuex module post.js's action:
const actions = {
all({commit}, data) {
return axios.get(`files`)
.then(response => {
commit('setData', response);
});
},
}
After many hours of searching, I found out that the key that is assigned to the Component caused the problem.
When the key is modified the GET request will send again. This the reason why it sends twice. Special thanks to #Anatoly for giving me the hint.
Below is the usage codes:
<template>
<Component :key="componentKey" #edit="dataIsChanged"/>
</template>
<script>
export default {
components: { Component },
data: () => ({
componentKey: 0,
}),
methods: {
dataIsChanged: function() {
this.componentKey = Math.random();
}
}
};
</script>
I'm trying to fetch data from a django rest-framework API, using `ReactJS' but I keep facing the same error:
SyntaxError: "JSON.parse: unexpected character at line 1 column 1 of the JSON data"
I think the actual problem is is in my API, since I have already tried 3 different approaches to get the data into React. Maybe somebody knows what I can do to my API to make it work? Im using the djangorestframework library. The API looks as following:
{
"questions":"http://127.0.0.1:8000/api/questions/?format=json",
"choices":"http://127.0.0.1:8000/api/choices/?format=json"
}
And the React Component I'm using to retreive the data is the following:
import React, { Component } from 'react';
class ApiFetch extends Component {
state = {
data: []
};
async componentDidMount() {
try {
const res = await fetch('127.0.0.1:8000/api/?format=json');
const data = await res.json();
this.setState({
data
});
console.log(this.state.data)
} catch (e) {
console.log(e); //SyntaxError: "JSON.parse: unexpected character at line 1 column 1 of the JSON data"
}
}
render() {
return (
<div>
{(this.state.data)}
</div>
);
}
}
export default ApiFetch;
The Header of the API looks like that:
allow →GET, HEAD, OPTIONS
content-length →123
content-type →application/json
date →Fri, 17 Aug 2018 11:01:36 GMT
server →WSGIServer/0.2 CPython/3.6.3
vary →Accept, Cookie
x-frame-options →SAMEORIGIN
I tried the following example API with my client and it worked:
https://jsonplaceholder.typicode.com/todos/1
So something about djangorestframework and my client must be incompatible.
Solution: needed to add Cross Origin Resource Sharing (CORS)
The Problem here was, that Browsers prevent Javascript from reaching out to other domains according to the Same origin Policy.
The default workaround for this in Django, is "django-cors-headers".
To install it:
pip install django-cors-headers
Then it can be activated in the settings.py
Writing:
INSTALLED_APPS = (
##...
'corsheaders'
)
MIDDLEWARE_CLASSES = (
'corsheaders.middleware.CorsMiddleware',
#...
)
CORS_ORIGIN_ALLOW_ALL = True
You do not seem to query a valid route, perhaps try following:
async componentDidMount() {
try {
const res = await fetch('127.0.0.1:8000/api/questions/?format=json');
const data = await res.json();
this.setState({
data
});
console.log(this.state.data)
} catch (e) {
console.log(e); //SyntaxError: "JSON.parse: unexpected character at line 1 column 1 of the JSON data"
}
}
Intro
What I'm trying to achieve is a simple file upload with a progress indication with redux-saga and react). I'm having problems getting this indication because the file upload seems the be blocking - which it shouldn't be.
Expected behaviour
before the file upload starts a re render is triggered and the spinner is shown and the window is not blocked.
Current behaviour
What I have at the moment is a component with a table that show a file per row. A optimistic row gets added with a spinner as the content when the users uploads a file. As soon as the file is uploaded the optimistic row will be replaced by a real row with the file's name etc. When I'm uploading a file around 50MB the window gets blocked and shortly before the file is uploaded (around 0.5s before) the spinner appears and then the file is already uploaded and the spinner disappears again.
side notes
If you replace the file upload with new Promise(res => setTimeout(res, 5000)) it all works fine => it seems like there is a problem with the xhr / fetch.
I've implemented the same using XHR, promises and an onProgress callback to make sure the problem is not fetch.
the implementation looks very close to: https://gist.github.com/robinfehr/2f4018259bf026a468cc31100fed5c9f
Also with this implementation I've experienced the same issue - blocking until almost the end of the upload.
If I put log statements into the render function of the component to see if it's getting re rendered before the file is uploaded, I see (as soon as the block stops and the file is uploaded) that the log statements in the render function are actually correctly triggered with a timestamp before the file upload was done.
In this implementation I'm using the same reducer: optimistic event as well as the real event that reverts the optimistic event, they go trough the same reducer (named fileReducer here).
using a second reducer and concatination instead of the optimistic revert logic helps to displaying the spinner earlier but does not help with the blocking. It therefore seems like the middleware also gets blocked by the blocking call.
saga: (postData uses fetch)
function* createDocument(partnerId, { payload, meta }) {
const siteId = getSiteIdFromRoute();
const {
mediaGroupId,
customArticleId,
logicalComponentId,
type,
name,
documentSrc,
meta: metaFromFrontEnd
} = payload;
const commonEventId = uuid();
const hans = {
optimistic: true
};
const payloadBasic = {
id: commonEventId,
version: 0,
aggregate: {
id: uuid(),
name: 'document'
},
context: {
name: 'contentManagement'
},
payload: {
name,
type,
links: {
partnerId,
siteId,
logicalComponentId,
customArticleId,
mediaGroupId
}
}
};
// creates the optimistic (fake) row with a spinner in the file list component - action marked as optimistic which will be reverted.
yield put(actions.denormalizeEvent({
...payloadBasic,
name: 'documentCreated',
optimistic: true,
payload: {
...payloadBasic.payload,
uploading: true
}
}));
yield fork(executeDocumentUpload, type, siteId, partnerId, documentSrc, payloadBasic);
}
function* executeDocumentUpload(type, siteId, partnerId, documentSrc, payloadBasic) {
const req = yield call(uploadDocument, type, siteId, partnerId, documentSrc);
const body = yield req.json();
const { meta: metaFromFileUpload, id } = body.response;
// removes the optimistic (fake) row from the file list component and and adds the real row with more file information (optimistic event gets reverted in middleware)
yield put(actions.sendCommandSuccess({
...payloadBasic,
name: 'createDocument',
payload: {
...payloadBasic.payload,
meta: metaFromFileUpload
}
}));
}
function uploadDocument(type, siteId, partnerId, documentSrc) {
let url;
if (type === 'site' || type === 'mediaGroup' || type === 'logicalComponent') {
url = `/file/site/${siteId}/document`;
} else if (type === 'customArticle') {
url = `/file/partner/${partnerId}/document`;
}
return postData(url, documentSrc);
}
The problem was that I did send the file as a base64 encode string and set up the request with the wrong content-type.
'Content-Type': 'text/plain;charset=UTF-8'
putting the file into a FormData object and send the request without the mentioned content-type lead to a non-blocking request.