Append Each WebSocket Message to div - VUE3 - javascript

I'm trying to render each trade from Binances Websocket Stream in my VUE3 component. I can render 1 line and that line keeps updating, however this is not what i'm trying to achieve. Many thanks for all suggests / solutions.
<template>
<div>
<div v-for="data in tradeDataList" :key="data.id">
<div>
{{ data }}
</div>
</div>
</div>
</template>
<script>
export default {
name: 'App',
data: () => {
return {
connection: null,
tradeDataList: [],
}
},
created() {
this.getTradeStream();
},
methods: {
getTradeStream() {
console.log("Starting connection to WebSocket Server");
this.connection = new WebSocket("wss://stream.binance.com:9443/ws/btcusdt#trade");
this.connection.addEventListener("message", (event) => {
let tradeDataString = event.data;
this.tradeDataList = [];
let parsedData = JSON.parse(tradeDataString);
this.tradeDataList = parsedData;
console.log(this.tradeDataList);
});
this.connection.onopen = function (event) {
console.log(event);
console.log("Successfully connected to the echo websocket server...");
};
}
}
}
</script>
i have tried v-for looping through this.tradeDataList - I was expecting a list with one trade per line. What I saw was 1 line that constantly updates rather than making a new line.

Rather than, clearing out this.tradeDataList = []; and replaing the item this.tradeDataList = parsedData; push the item to the array. Optionally remove old items with splice etc
new Vue({
el: '#app',
data: () => {
return {
connection: null,
tradeDataList: [],
}
},
created() {
this.getTradeStream();
},
methods: {
getTradeStream() {
console.log("Starting connection to WebSocket Server");
this.connection = new WebSocket("wss://stream.binance.com:9443/ws/btcusdt#trade");
this.connection.addEventListener("message", (event) => {
let tradeDataString = event.data;
let parsedData = JSON.parse(tradeDataString);
// push new item to array
this.tradeDataList.push(parsedData);
// keep only last 10
this.tradeDataList = this.tradeDataList.slice(Math.max(this.tradeDataList.length - 10, 0))
});
this.connection.onopen = function(event) {
//console.log(event);
console.log("Successfully connected to the echo websocket server...");
};
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.14/vue.min.js"></script>
<div id="app">
<div>
<div v-for="data in tradeDataList" :key="data.id">
<div>
{{ data.t }} - {{ data.p }}
</div>
</div>
</div>
</div>

Related

Problem with Vue.js when it needs to fetch an url (wp-api) on a keyup event

Here's my issue. I created a tool with vue.js and the WordPress API to search through the search endpoints for any keyword and display the result. So far so good, everything is working, except for a bug that I spotted.
Here's the deal:
const websiteurl = 'https://www.aaps.ca'; //yourwebsite or anything really
var vm = new Vue({
el: '#blog-page',
data: {
noData: false,
blogs: [],
page: 0,
search: '',
totalPagesFetch: "",
pageAmp: "&page=",
apiURL: `${websiteurl}/wp-json/wp/v2/posts?per_page=6`,
searchbyid: `${websiteurl}/wp-json/wp/v2/posts?per_page=6&include=`,
searchUrl: `${websiteurl}/wp-json/wp/v2/search?subtype=post&per_page=6&search=`,
},
created: function () {
this.fetchblogs();
},
methods: {
fetchblogs: function () {
let self = this;
self.page = 1;
let url = self.apiURL;
fetch(url)
.then(response => response.json())
.then(data => vm.blogs = data);
},
searchonurl: function () {
let ampersand = "&page=";
searchPagination(1, this, ampersand);
},
}
});
function searchPagination(page, vm, pagen) {
let self = vm;
let searchword = self.search.toLowerCase();
let newsearchbyid = self.searchbyid;
let url;
self.page = page;
url = self.searchUrl + searchword + pagen + self.page;
self.mycat = 'init';
fetch(url)
.then(response => {
self.totalPagesFetch = response.headers.get("X-WP-TotalPages");
return response.json();
})
.then(data => {
let newid = [];
data.forEach(function (item, index) {
newid.push( item.id );
});
if (newid.length == 0) {
return newsearchbyid + '0';
} else {
return newsearchbyid + newid;
}
})
.then(response2 => {
return fetch(response2)
})
.then(function(data2) {
return data2.json();
})
.then(function(response3) {
console.log(response3)
if (response3.length == 0) {
vm.noData = true;
vm.blogs = response3;
} else {
vm.noData = false;
vm.blogs = response3;
}
})
}
<link href="https://cdn.jsdelivr.net/npm/bootstrap#4.3.1/dist/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div class="lazyblock-blogs testblog" id="blog-page">
<div class="container">
<div class="row controls">
<div class="col-md-12">
<div class="search-blog">
<img height="13" src="" alt="search">
<input id="sb" type="text" v-model="search" #keyup="searchonurl" placeholder="search">
</div>
</div>
</div>
<div class="row">
<div class="col-md-4" v-for="(b, index) in blogs">
<div class="h-100 box" v-cloak>
<img width="100%" v-bind:src=b.featured_image_url>
<a v-bind:href="b.link">
<h3 v-html=b.title.rendered></h3>
</a>
<div v-html=b.excerpt.rendered></div>
<p class="read-more"><a v-bind:href="b.link">read more</a></p>
</div>
</div>
<div class="no-data" v-if="noData">
<div class="h-100">
No post
</div>
</div>
</div>
</div>
</div>
I'm using a keyup event which is causing me some problems because it works, but in same cases, for example, if the user is very fast to type characters and then suddenly he wants to delete the word and start again, the response for the API has some sort of lag.
The problem is that I guess that the Vue framework is very responsive (I create a variable call search that will update immediately) but the API call in the network is not (please check my image here):
This first image appears if I type lll very fast, the third result will return nothing so it is an empty array, but if I will delete it immediately, it will return an url like that: https://www.aaps.ca//wp-json/wp/v2/search?subtype=post&per_page=6&search=&page=1 which in turn should return 6 results (as a default status).
The problem is that the network request won't return the last request but it gets crazy, it flashs and most of the time it returns the previous request (it is also very slow).
Is that a way to fix that?
I tried the delay function:
function sleeper(ms) {
return function(x) {
return new Promise(resolve => setTimeout(() => resolve(x), ms));
};
}
and then I put before the then function:
.then(sleeper(1000))
but the result is the same, delayed by one second (for example)
Any thought?
This is the case for debounced function. Any existing implementation can be used, e.g. Lodash debounce. It needs to be declared once per component instance, i.e. in some lifecycle hook.
That searchPagination accepts this as an argument means that something went wrong with its signature. Since it operates on component instance, it can be just a method and receive correct this context:
methods: {
searchPagination(page, pagen) {
var vm = this;
...
},
_rawsearchonurl() {
let ampersand = "&page=";
this.searchPagination(1, ampersand);
}
},
created() {
this.searchonurl = debounce(this._rawsearchonurl, 500);
...
}
You could use debounce, no call will leave until the user stop typing in the amount of time you chose
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function() {
timeout = null;
if (!immediate) func.apply(context, args);
}, wait);
if (immediate && !timeout) func.apply(context, args);
};
}
// in your "methods" (I put 1000ms of delay) :
searchonurl: function () {
let ampersand = "&page=";
debounce(searchPagination, 1000)(1, this, ampersand);
}
One of best ways is to use Debounce which is mentioned in this topic
Or use a function and combine it with watch. Follow these lines:
In mounted or created make an interval with any peroid you like (300 etc.) define a variable in data() and name it something like searched_value. In interval function check the value of your input and saerch_value, if they were not equal (===) then replace search_value with input value. Now you have to watch search_value. When it changed you call your api.
I use this method and works fine for me. Also it`s managable and everything is in your hand to config and modify.
===== UPDATE: =====
A simple code to do what I said above
<template>
<div>
<input type="search" v-model="search_key">
</div> </template>
<script> export default {
name: "SearchByApi",
data() {
return {
search_key: null,
searched_item: null,
loading: false,
debounceTime: 300,
}
},
created() {
this.checkTime()
const self = this
setInterval(function() {
self.checkTime()
}, this.debounceTime);
},
watch: {
searched_item() {
this.loadApi()
}
},
methods: {
checkTime() {
if (this.searched_item !== this.search_key && !this.loading) {
this.searched_item = this.search_key
}
},
loadApi() {
if (!this.loading && this.searched_item?.length > 0) {
this.loading = true
const api_url = 'http://api.yourdomain.com'
axios(api_url, {search: this.searched_item}).then(res => {
// whatever you want to do when SUCCESS
}).catch(err => {
// whatever you want to do when ERROR
}).then(res => {
this.loading = false
})
}
}
}
}
</script>

VueJS Array Index returns wrong result

I am creating a poetry app where poetry is fetched using an API call.
I fetch data using axios library and do v-for to populate data. I use the index from v-for to populate the image for each poem respectively.
I display 10 results per page using my own custom pagination. Currently, it's only for next button though.
The problem I am facing is when I navigate to Page 2! As I said earlier, that I use v-for's index to display images, it doesn't actually update the index when I move to the next page. As a result, the images are shown same as of page 1.
Code:
new Vue({
el: '#app',
data: {
proxy: 'https://cors-anywhere.herokuapp.com/',
imageIndex: 0,
pagination: {
start: 0,
end: 10,
resPerPage: 10
},
fetchData: [],
fetchImages: []
},
methods: {
paginate() {
this.pagination.start = this.pagination.start + this.pagination.resPerPage;
this.pagination.end = this.pagination.end + this.pagination.resPerPage;
},
async fetchDatas() {
try {
const res = await axios(`${this.proxy}http://poetrydb.org/author,title/Shakespeare;Sonnet`);
if (res) {
this.fetchData = res.data;
}
} catch (error) {
console.log(error);
}
},
async fetchImagess() {
const key = '9520054-7cf775cfe7a0d903224a0f896';
const perPage = 154;
const proxy = ''
const res = await axios(`${this.proxy}https://pixabay.com/api/?key=${key}&per_page=${perPage}`);
this.fetchImages = res.data.hits;
}
},
mounted() {
this.fetchDatas();
this.fetchImagess();
}
});
<div id="app">
<div v-for="(poetry, index) in fetchData.slice(this.pagination.start, this.pagination.end)">
<div>
<img :src="fetchImages[index].largeImageURL.toLowerCase()" style="max-width: 100%;height: auto;max-height: 320px;">
<div>
<h5>{{ poetry.title }}</h5>
<span v-for="(poetryBody, i) in poetry.lines.slice(0, 5)">
{{ i === 4 ? poetryBody.split(',').join('') + '...' : poetryBody }}
</span>
<br>
Read More
</div>
</div>
</div>
<nav style="padding-top: 3em;">
<button #click="paginate()">Next</button>
</nav>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
JSFiddle: http://jsfiddle.net/sanjaybanjade/vnu654gk/9/
As you can see the images doesn't get updated when I goto Page 2! Please help me fix this!
And please ignore the console errors. I am gonna fix them later.
The quick fix would be to calculate the offset in line 4 to update on pagination:
<img v-bind:src="fetchImages[index + pagination.start].largeImageURL.toLowerCase()" style="max-width: 100%;height: auto;max-height: 320px;">
wrong at this line fetchImages[index].largeImageURL.toLowerCase().
Since you are iterating a sliced array of fetchData, it's index is related to sliced array, not original array. So, you should apply pagination slice to your fetchImages too.
When you run fetchData.slice(), it returns a new object. So if you slice out 10 new pieces of poetry, their indexes are still going to be 0-9, since the returned object only has that many items each time.
Why it's not working is because you only slice fetchData on this line fetchData.slice(this.pagination.start, this.pagination.end) but you don't slice the fetchImages what means fetchImages still is the same array it didn't change, meaning that index 0 is still the same image. Best is if you keep them in sync so I would add a pageData and pageImages array's and every time you change the paging you update both of them. like in a updatePageData method
new Vue ({
el: '#app',
data: {
proxy: 'https://cors-anywhere.herokuapp.com/',
imageIndex: 0,
pagination: {
start: 0,
end: 10,
resPerPage: 10
},
fetchData: [],
fetchImages: [],
pageData: [],
pageImages: []
},
methods: {
paginateNext() {
this.pagination.start = this.pagination.start + this.pagination.resPerPage;
this.pagination.end = this.pagination.end + this.pagination.resPerPage;
this.updatePageData()
},
updatePageData () {
this.pageData = this.fetchData.slice(this.pagination.start, this.pagination.end)
this.pageImages = this.fetchImages.slice(this.pagination.start, this.pagination.end)
},
async fetchDatas() {
try {
const res = await axios(`${this.proxy}http://poetrydb.org/author,title/Shakespeare;Sonnet`);
if(res) {
this.fetchData = res.data;
}
} catch(error) {
console.log(error);
}
},
async fetchImagess() {
const key = '9520054-7cf775cfe7a0d903224a0f896';
const perPage = 154;
const proxy = ''
const res = await axios(`${this.proxy}https://pixabay.com/api/?key=${key}&per_page=${perPage}`);
this.fetchImages = res.data.hits;
}
},
mounted() {
Promise.all([
this.fetchDatas(),
this.fetchImagess()
])
.then(() => this.updatePageData())
}
});
<div id="app">
<div v-for="(poetry, index) in pageData">
<div>
<img :src="pageImages[index].largeImageURL.toLowerCase()" style="max-width: 100%;height: auto;max-height: 320px;">
<div>
<h5>{{ poetry.title }}</h5>
<span v-for="(poetryBody, i) in poetry.lines.slice(0, 5)">
{{ i === 4 ? poetryBody.split(',').join('') + '...' : poetryBody }}
</span>
<br>
Read More
</div>
</div>
</div>
<nav style="padding-top: 3em;">
<button #click="paginateNext()">Next</button>
</nav>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>

precheck checkboxes depending on the response coming in json form in vuejs

Sorry for my English. I am trying to pre select those checkboxes whos values have been saved in the database. I did it using javascript in vuejs but those selected checkboxes values are not storing in array.
My code is like
role.component.js
getRoleRowData(data) {
this.roleaction = "edit";
this.addrolemodal = true;
console.log(data.role_id);
axios
.post(apiUrl.api_url + "getRolePermissionData", {
role_id: data.role_id
}).then(
result => {
this.permid = result.data;
var list = [];
result.data.forEach(function(value) {
list.push(value.perm_id);
});
var options = list;
for (var i = 0; i < options.length; i++) {
if (options[i]) document.querySelectorAll('input[value="' + options[i] + '"][type="checkbox"]')[0].checked = true;
}
},
error => {
console.error(error);
}
);
this.addrole = data;
},
And role.component.html
<div class="col-md-8">
<b-form-fieldset>
<div class="form" id="demo">
<h6>Permissions</h6>
<span v-for="perm_name_obj in listPermissionData">
<input type="checkbox" class="perm_id" v-bind:value="perm_name_obj.perm_id" name="perm_id" id="perm_name" v-model="checkedPerm_Id"> {{perm_name_obj.perm_name}}<br>
</span>
<span>Checked names: {{ checkedPerm_Id }}</span>
</div>
</b-form-fieldset>
</div>
And the Output
And the Ouput I got
In simple words I want to pre check checkboxes in vuejs of which values are stored in database.
See the following example, using simulation data
var app = new Vue({
el: '#app',
data () {
return {
listPermissionData: [],
checkedPerm_Id: []
}
},
created () {
setTimeout(_=>{
//Here simulates axois to request Permission data
this.listPermissionData = [
{perm_id:1,perm_name:'perm_name1'},
{perm_id:2,perm_name:'perm_name2'},
{perm_id:3,perm_name:'perm_name3'},
{perm_id:4,perm_name:'perm_name4'},
{perm_id:5,perm_name:'perm_name5'}
];
//Here simulates axois to request Selected Permissions (result.data)
var selected = [
{perm_id:2,perm_name:'perm_name2'},
{perm_id:5,perm_name:'perm_name5'}
]
this.checkedPerm_Id = selected.map(o=>o.perm_id)
},1000)
}
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<div class="form">
<h6>Permissions</h6>
<span v-for="perm_name_obj in listPermissionData">
<input type="checkbox" class="perm_id" v-bind:value="perm_name_obj.perm_id" name="perm_id" id="perm_name" v-model="checkedPerm_Id"> {{perm_name_obj.perm_name}}<br>
</span>
<span>Checked names: {{ checkedPerm_Id }}</span>
</div>
</div>
I solved my problem, here is my code
role.component.js
getRoleRowData(data) {
this.roleaction = "edit";
this.addrolemodal = true;
console.log(data.role_id);
let tempthis = this;
axios
.post(apiUrl.api_url + "getRolePermissionData", {
role_id: data.role_id
}).then(
result => {
this.permid = result.data;
var list = [];
result.data.forEach(function(value) {
//by using tempthis variable we provided the current access to the checkedPerm_Id array which not accessible from out of the function which is getRoleRowData
tempthis.checkedPerm_Id.push(value.perm_id);
list.push(value.perm_id);
});
},
error => {
console.error(error);
}
);
this.addrole = data;
},

Having issues displaying sqlite db data in nativescript application

My app has a scoreboard page that is supposed to fetch data from sqlite db and display it onNavigatingTo page in a ListView but it does not do it as expected.
The xml page to display the results:
<Page xmlns="http://schemas.nativescript.org/tns.xsd" loaded="pageLoaded" navigatingTo="onNavigatingTo">
<ActionBar title="Scoreboard">
<NavigationButton text="Back" android.systemIcon="ic_menu_back" tap="homeTap"/>
</ActionBar>
<StackLayout orientation="vertical">
<Label text="Your Performance Sheet"></Label>
<ListView items= "{{results}}" >
<ListView.itemTemplate>
<Label text="{{testname}}"/>
<Label text="{{score}}"/>
<Label text="{{percent}}"/>
</ListView.itemTemplate>
</ListView>
</StackLayout>
</Page>
The scoreboard-view-model:
var Observable = require("data/observable").Observable;
var ObservableArray = require("data/observable-array").ObservableArray;
var Sqlite = require("nativescript-sqlite");
function scoreViewModel (database) {
var viewModel = new Observable();
viewModel.results = new ObservableArray([]);
viewModel.select = function () {
this.results = new ObservableArray([]);
database.all("SELECT * FROM scores").then(rows => {
for (var row in rows) {
this.results.push(rows[row]);
}
}, error => {
console.log("SELECT ERROR", error);
})
}
viewModel.select();
return viewModel;
}
exports.scoreViewModel = scoreViewModel;
I am selecting all the data from the scores table and pushing to viewModel.results array which is already bound to the view.
The scoreboard.js :
var observable = require("data/observable");
var scoreViewModel = require("./scoreboard-view-model").scoreViewModel;
var page;
var Sqlite = require("nativescript-sqlite");
exports.onNavigatingTo = function (args) {
page = args.object;
(new Sqlite("scoreboard.db")).then(db => {
db.execSQL("CREATE TABLE IF NOT EXISTS scores (id INTEGER PRIMARY KEY AUTOINCREMENT, testname TEXT, score TEXT, percent TEXT)")
.then(id => {
page.bindingContext = scoreViewModel(db);
}, error => {
console.log(error)
});
}, error => {
console.log(error);
});
}
Someone please help me get the data to show up in the list view.
I got it work by editing the scoreboard-view-model.js file, precisely in the viewModel.Select function:
viewModel.select = function () {
//the line below was changed to this...
//testname, score, and percent are column names in the scores table
database.all("SELECT testname, score, percent FROM scores").then(rows => {
for (var row in rows) {
//the line below was changed too...
viewModel.results.push({testname: rows[row][0], score: rows[row][1], percent: rows[row][2]});
}, error => {
console.log("SELECT ERROR", error);
})
}
Done. it works as expected now. Thanks TJ Vantoll for trying to help. Really Appreciated.

Extracting photos from FB-graph with Meteor

I'm trying to display photos from the NPM FB-Graph (https://npmjs.org/package/fbgraph) package by following this example (http://www.andrehonsberg.com/article/facebook-graph-api-meteor-js). I've managed to connect the API and render data, however I'm having trouble extracting the EJSON data into my picture template.
First off, let me show you the code I'm working with. Here is my client code:
function Facebook(accessToken) {
this.fb = Meteor.require('fbgraph');
this.accessToken = accessToken;
this.fb.setAccessToken(this.accessToken);
this.options = {
timeout: 3000,
pool: {maxSockets: Infinity},
headers: {connection: "keep-alive"}
}
this.fb.setOptions(this.options);
}
Facebook.prototype.query = function(query, method) {
var self = this;
var method = (typeof method === 'undefined') ? 'get' : method;
var data = Meteor.sync(function(done) {
self.fb[method](query, function(err, res) {
done(null, res);
});
});
return data.result;
}
Facebook.prototype.getUserData = function() {
return this.query('me');
}
Facebook.prototype.getPhotos = function() {
return this.query('/me/photos?fields=picture');
}
Meteor.methods({
getUserData: function() {
var fb = new Facebook(Meteor.user().services.facebook.accessToken);
var data = fb.getPhotos();
return data;
}
});
Meteor.methods({
getPhotos: function() {
var fb = new Facebook(Meteor.user().services.facebook.accessToken);
var photos = fb.getPhotos;
return photos;
}
});
Here is my client code:
Template.fbgraph.events({
'click #btn-user-data': function(e) {
Meteor.call('getUserData', function(err, data) {
$('#result').text(JSON.stringify(data, undefined, 4));
});
}
});
var fbPhotos = [];
Template.fbgraph.events({
fbPhotos : function(e) {
Meteor.call('getUserData', function(err, data) {
$('input[name=fbPhotos]').text(EJSON.stringify(data, undefined, 4));
});
}
});
Template.facebookphoto.helpers({
pictures: fbPhotos
});
And here are my templates:
<template name="fbgraph">
<div id="main" class="row-fluid">
{{> facebookphoto}}
</div>
<button class="btn" id="btn-user-data">Get User Data</button>
<div class="well">
<pre id="result"></pre>
</div>
</template>
<template name="facebookphoto">
<div class="photos">
{{#each pictures}}
{{> photoItem}}
{{/each}}
</div>
</template>
<template name="photoItem">
<div class="photo">
<div class="photo-content">
<img class="img-rounded" src="{{picture}}">
</div>
</div>
</template>
Right now, I'm testing the data with the id="results" tag and the Facebook API returns data as below.
{
"data": [
{
"picture": "https://photo.jpg",
"id": "1234",
"created_time": "2013-01-01T00:00:00+0000"
},
{
"picture": "https://photo.jpg",
"id": "12345",
"created_time": "2013-01-01T00:00:00+0000"
}
}
However I'm having difficulty pulling each of the pictures out of the EJSON and render them in templates. What I'd like to do is to display each picture in the {{picture}} template. I believe the problem with the code is somewhere in the client, but I'm not sure how to fix it.
Thanks in advance!
It looks like in your client code you have
Template.fbgraph.events({ ... })
defined twice. Did you mean to write:
Template.fbgraph.helpers({
fbPhotos : function(e) {
Meteor.call('getUserData', function(err, data) {
$('input[name=fbPhotos]').text(EJSON.stringify(data, undefined, 4));
});
}
});
A simpler way to do it might just be to call the getUserData method in your facebookphoto template itself, thus:
Template.facebookphoto.helpers({
pictures : function(e) {
Meteor.call('getUserData', function(err, data) {
$('input[name=fbPhotos]').text(EJSON.stringify(data, undefined, 4));
});
}
});

Categories

Resources