Sort() not working - sorting

I'm having an issue with the sort() in ranking data from coinmarketcap api. With an ajax api call, sort works in returning an array with the right ranking. With an axios api call, seen below, it doesn't.
Here is my code using axios and vue.js:
let coinMarket = 'https://api.coinmarketcap.com/v2/ticker/?limit=10'
let updateInterval = 60 * 1000;
let newApp = new Vue({
el: '#coinApp',
data: {
// data within an array
results: []
},
methods: {
getCoins: function() {
axios
.get(coinMarket)
.then((resp) => {
this.results = formatCoins(resp);
});
},
getColor: (num) => {
return num > 0 ? "color:green;" : "color:red;";
},
},
created: function() {
this.getCoins();
}
})
setInterval(() => {
newApp.getCoins();
},
updateInterval
);
function formatCoins(res) {
var coinDataArray = []
Object.keys(res.data).forEach(function(key) {
coinDataArray.push(res.data[key])
})
coinDataArray.sort(function(a,b) {
return a.rank > b.rank
})
console.log(coinDataArray)
}
Where am I going wrong?

If you look into the data responded by https://api.coinmarketcap.com/v2/ticker/?limit=10, you will find the data you need is under res.data.data, not res.data.
So within the function=formatCoins, replace res.data with res.data.data, then works.
Vue.config.productionTip = false
let coinMarket = 'https://api.coinmarketcap.com/v2/ticker/?limit=10'
let updateInterval = 60 * 1000;
function formatCoins(res) {
var coinDataArray = []
Object.keys(res.data.data).forEach(function(key) {
coinDataArray.push(res.data.data[key])
})
coinDataArray.sort(function(a,b) {
return a.rank - b.rank
})
return coinDataArray
}
let newApp = new Vue({
el: '#coinApp',
data: {
// data within an array
results: []
},
methods: {
getCoins: function() {
axios
.get(coinMarket)
.then((resp) => {
this.results = formatCoins(resp);
});
},
getColor: (num) => {
return num > 0 ? "color:green;" : "color:red;";
},
},
created: function() {
this.getCoins();
}
})
setInterval(() => {
newApp.getCoins();
},
updateInterval
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.js"></script>
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<div id="coinApp">
<div v-for="(record, index) in results" :key="index">
{{index}} - {{record.name}}: {{record.rank}}
</div>
</div>

Related

Warning: Expected server HTML to contain a matching <div> in <div> when use localStorage as params

I think this warning appear when i work with localStorage, some answers in another page is use useEffect(), but I don't know how to use query or mutation in useEffect().How can I fix it?
export const productListForBill = () : GetProductForBill[] =>{
const returnEmtpyArray : GetProductForBill[] = []
if(typeof window !== "undefined"){
if(localStorage.getItem("products"))
{
const tempProduct = JSON.parse(localStorage.getItem("products") || "")
if(Array.isArray(tempProduct)){
return tempProduct
}
}
}
return returnEmtpyArray
}
const Cart = () => {
const { data } = useGetSomeProductQuery({
variables: { productList: productListForBill() },
});
return (
<>
<Navbar />
{data?.getSomeProduct ? (
data.getSomeProduct.map((product) => (
<div key={`${product.name}-${product.type}`}>
name: {product.name} --|-- type: {product.type} --|-- amount :{" "}
{product.amount} --|-- unitPrice : {product.unitPrice} --|-- total:{" "}
{product.totalPrice}
</div>
))
) : (
<div>Nothing in here</div>
)}
</>
);
};
export const getStaticProps: GetStaticProps = async () => {
const apolloClient = initializeApollo();
await apolloClient.query({
query: GetSomeProductDocument,
variables: { productList: productListForBill() },
});
return addApolloState(apolloClient, {
props: {},
});
};
I have to type something for text checker of Stackoverflow, have a nice day!
code of useGetSomeProductQuery, i'm working with graphql and use codegen to generate it at client
#Query((_return) => [ProductOfBill], { nullable: true })
async getSomeProduct(
#Arg("productList", (_type) => [GetProductForBill])
productList: GetProductForBill[]
): Promise<ProductOfBill[] | null | undefined> {
try {
const newList : ProductOfBill[] = await Promise.all(productList.map(async (product) => {
const price = await Price.findOne({
where: {
type: product.type,
product: product.productId,
}
});
const newProductOfBill = ProductOfBill.create({
name:product.name,
amount:product.amount,
type:product.type,
unitPrice:price?.price
})
return newProductOfBill
}))
.then(list => {
console.log(list)
return list
})
.catch(_ => {
const resultList : ProductOfBill[] = []
return resultList
})
return newList;
} catch (error) {
console.log(error);
return undefined;
}
}

Pagination Apollo Client 3 - Cache merges but does not render new results when paginating

I'd love to implement nested pagination within my application. I have been reading the docs and looking at several other examples but I just can't get this to work - any help is appreciated! Thanks!
React component:
I am clicking the button to run the fetchMore function provided by the useQuery hook (apollo). The network request is going through and the new products are merged into the cache... but no new products render on the page.
export const FilterableKit = () => {
const selectedKitId = useReactiveVar(selectedKitIdVar);
const [
getKitProducts,
{ data: getKitProductsData, loading: getKitProductsLoading, fetchMore },
] = useGetKitProductsLazyQuery();
useEffect(() => {
if (selectedKitId) {
getKitProducts({
variables: {
getKitsInput: {
_id: {
string: selectedKitId,
filterBy: "OBJECTID" as StringFilterByEnum,
},
},
getProductsInput: {
config: {
pagination: {
reverse: true,
limit: 3,
},
},
},
},
});
}
}, [getKitProducts, selectedKitId]);
const kitProducts = getKitProductsData?.getKits.data?.find(
(kit) => kit?._id === selectedKitId
)?.products.data;
const handleLoadMore = () => {
if (kitProducts && kitProducts?.length > 0) {
const remaining =
getKitProductsData?.getKits.data[0]?.products.stats?.remaining;
if (remaining && remaining > 0) {
const cursor =
kitProducts[kitProducts.length - 1] &&
kitProducts[kitProducts.length - 1]?.createdAt;
fetchMore({
variables: {
getProductsInput: {
config: {
pagination: {
reverse: true,
createdAt: cursor,
},
},
},
},
});
}
}
};
return (
<CContainer>
<KitItemCards products={kitProducts} loading={getKitProductsLoading} />
<CContainer className="d-flex justify-content-center my-3">
<CButton color="primary" className="w-100" onClick={handleLoadMore}>
Load More
</CButton>
</CContainer>
</CContainer>
);
};
Type Policies: I define the "Kit" typePolicy to merge products into the correct field.
export const cache: InMemoryCache = new InMemoryCache({
typePolicies: {
Kit: {
fields: {
products: {
keyArgs: false,
merge(existing = [] as Product[], incoming: GetProductsResponse) {
if (!incoming) return existing;
if (!existing) return incoming;
const { data: products, ...rest } = incoming;
let result: any = rest;
result = [...existing, ...(products ?? [])];
return result;
},
},
},
},
});
Thanks for any pointers in the right direction! Let me know if there is something else you'd like to see.

useState depending on other state

I have this useSiren hook that should update its state with the incoming json argument but it doesnt.
On the first call the json is an empty object, because the fetch effect has not been run yet.
On the second call its also an empty object (triggered by loading getting set to true in App)
And on the third call its filled with valid data. However, the valid data is not applied. The state keeps its initial value.
I guess somehow setSiren must be called to update it, since initial state can only be set once. But how would I do that? Who should call `setSiren?
import { h, render } from 'https://unpkg.com/preact#latest?module';
import { useEffect, useState, useCallback } from 'https://unpkg.com/preact#latest/hooks/dist/hooks.module.js?module';
import htm from "https://unpkg.com/htm#latest/dist/htm.module.js?module";
const html = htm.bind(h);
function useFetch({
method = "GET",
autoFetch = true,
href,
body
}) {
const [loading, setLoading] = useState(false)
const [error, setError] = useState()
const [response, setResponse] = useState()
const [isCancelled, cancel] = useState()
const [json, setJson] = useState({})
const sendRequest = async payload => {
try {
setLoading(true)
setError(undefined)
const response = await fetch(href.replace("http://", "https://"), {
method
})
const json = await response.json()
if (!isCancelled) {
setJson(json)
setResponse(response)
}
return json
} catch (err) {
if (!isCancelled) {
setError(err)
}
throw err
} finally {
setLoading(false)
}
}
if (autoFetch) {
useEffect(() => {
sendRequest(body)
return () => cancel(true)
}, [])
}
return [{
loading,
response,
error,
json
},
sendRequest
]
}
function useSiren(json) {
const [{ entities = [], actions = [], links, title }, setSiren] = useState(json)
const state = (entities.find(entity => entity.class === "state")) || {}
return [
{
title,
state,
actions
},
setSiren
]
}
function Action(props) {
const [{ loading, error, json }, sendRequest] = useFetch({ autoFetch: false, href: props.href, method: props.method })
const requestAndUpdate = () => {
sendRequest().then(props.onRefresh)
}
return (
html`
<button disabled=${loading} onClick=${requestAndUpdate}>
${props.title}
</button>
`
)
}
function App() {
const [{ loading, json }, sendRequest] = useFetch({ href: "https://restlr.io/toggle/0" })
const [{ state, actions }, setSiren] = useSiren(json)
return (
html`<div>
<div>State: ${loading ? "Loading..." : (state.properties && state.properties.value)}</div>
${actions.map(action => html`<${Action} href=${action.href} title=${action.title || action.name} method=${action.method} onRefresh=${setSiren}/>`)}
<button disabled=${loading} onClick=${sendRequest}>
REFRESH
</button>
</div>
`
);
}
render(html`<${App}/>`, document.body)
Maybe what you want to do is to update the siren state when the json param changes? You can use a useEffect to automatically update it.
function useSiren(json) {
const [{ entities = [], actions = [], links, title }, setSiren] = useState(json)
useEffect(() => { // here
setSiren(json)
}, [json])
const state = (entities.find(entity => entity.class === "state")) || {}
return [
{
title,
state,
actions
},
setSiren
]
}
The pattern mentioned by #awmleer is packaged in use-selector:
import { useSelectorValue } from 'use-selector';
const { entities=[], actions=[], title} = json;
const siren = useSelectorValue(() => ({
state: entities.find(entity => entity.class === 'state') || {},
actions,
title
}), [json]);
Disclosure I'm author and maintainer of use-selector

rxjs stream of elasticsearch scroll API yields empty result set

My goal is to transform the elasticsearch result to an rxjs stream and thought of doing so using the scroll API fetching 1 data point on every call. However it seems that my rxjs stream returns no results for the second elastic query (searchElastic).
Below is a sample of my code:
import * as Rx from 'rxjs';
import {elasticClient} from '../Helpers.ts';
function searchElastic({query, sort}) {
const body: any = {
size: 1,
query,
_source: { excludes: ['logbookType', 'editable', 'availabilityTag'] },
sort
};
// keep the search results "scrollable" for 30 secs
const scroll = '30s';
return Rx.Observable
.fromPromise(elasticClient.search({ index: 'data', body, scroll }))
.concatMap(({_scroll_id, hits: {hits}}) => {
const subject = new Rx.Subject();
if(hits.length) {
// initial data
subject.onNext(hits[0]._source as ElasticDoc);
console.log(hits[0]._id);
const handleDoc = (err, res) => {
if(err) {
subject.onError(err);
return;
}
const {_scroll_id, hits: {hits}} = res;
if(!hits.length) {
subject.onCompleted();
} else {
subject.onNext(hits[0]._source as ElasticDoc);
console.log(hits[0]._id);
setImmediate(() =>
elasticClient.scroll({scroll, scrollId: _scroll_id},
handleDoc));
}
};
setImmediate(() =>
elasticClient.scroll({scroll, scrollId: _scroll_id},
handleDoc));
} else {
subject.onCompleted();
}
return subject.asObservable();
});
}
function getEntries() {
const entriesQuery = {
query: {
filtered: {
filter: {
bool: {
must: [{
range: {
creationTimestamp: {
gte: "2018-04-01T07:55:59.915Z",
lte: "2018-04-01T07:57:08.915Z"
}
}
}, {
query: {
query_string: {
query: "+type:*scan*"
}
}
}]
}
}
}
},
sort: [{
creationTimestamp: {
order: "asc"
},
id: {
order: "asc"
}
}]
};
return searchElastic(entriesQuery)
.concatMap(entry => {
// all entries are logged correctly
console.log(entry.id);
// array that contains MongoDB _ids as strings
const ancestors = entry.ancestors || [];
// if no parents => doesn't match
if(!ancestors.length) {
return Rx.Observable.empty();
}
const parentsQuery = {
query: {
filtered: {
filter: {
bool: {
must: [{
range: {
creationTimestamp: {
gte: "2018-04-01T07:55:59.915Z",
lte: "2018-04-01T07:57:08.915Z"
}
}
}, {
query: {
query_string: {
query: "+type:*block* +name:*Report*"
}
}
}]
}
}
}
},
sort: [{
creationTimestamp: {
order: "asc"
},
id: {
order: "asc"
}
}]
};
parentsQuery.query.filtered.filter.bool.must.push({
terms: {
id: ancestors
}
});
// fetch parent entries
return searchElastic(parentsQuery)
.count()
.concatMap(count => {
// count is always 0 even though entries are logged
// in the searchElastic console.logs
console.log(entry.id, count);
return Rx.Observable.just(entry);
});
});
}
function executeQuery() {
try {
getEntries()
.subscribe(
(x) => console.log(x.id),
err => console.error(err),
() => {}
)
} catch(e) {
console.error(e);
}
}
Looks like it's an rxjs problem, since all ancestors entries get logged but count always returns 0.
P.S. using elasticsearch v1.7
After playing with a couple rxjs examples with subjects, it seems like the subject is being completed (onCompleted) before an observer subscribes to it.
Working example
var subject = new Rx.Subject();
var subscription = subject.subscribe(
function(x) {
console.log('onNext: ' + x);
},
function(e) {
console.log('onError: ' + e.message);
},
function() {
console.log('onCompleted');
});
subject.onNext(1);
// => onNext: 1
subject.onNext(2);
// => onNext: 2
subject.onCompleted();
// => onCompleted
<!DOCTYPE html>
<html>
<head>
<script src="//code.jquery.com/jquery-2.1.0.min.js"></script>
<title>JS Bin</title>
<script src="//cdn.jsdelivr.net/rsvp/3.0.6/rsvp.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/rxjs/2.2.28/rx.all.min.js"></script>
</head>
</html>
Broken example
var subject = new Rx.Subject();
subject.onNext(1);
// => onNext: 1
subject.onNext(2);
// => onNext: 2
subject.onCompleted();
// => onCompleted
var subscription = subject.subscribe(
function(x) {
console.log('onNext: ' + x);
},
function(e) {
console.log('onError: ' + e.message);
},
function() {
console.log('onCompleted');
});
<!DOCTYPE html>
<html>
<head>
<script src="//code.jquery.com/jquery-2.1.0.min.js"></script>
<title>JS Bin</title>
<script src="//cdn.jsdelivr.net/rsvp/3.0.6/rsvp.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/rxjs/2.2.28/rx.all.min.js"></script>
</head>
</html>
So I fixed it by changing searchElastic to the following:
function searchElasticStream({query, sort}) {
const body: any = {
size: 1,
query,
_source: { excludes: ['logbookType', 'editable', 'availabilityTag'] },
sort
};
// keep the search results "scrollable" for 30 secs
const scroll = '30s';
return Rx.Observable
.fromPromise(elasticClient.search({ index: 'data', body, scroll }))
.flatMap(({_scroll_id, hits: {hits}}) => {
const subject = new Rx.Subject();
// this made the difference
setImmediate(() => {
if(hits.length) {
// initial data
subject.onNext(hits[0]._source as ElasticDoc);
const handleDoc = (err, res) => {
if(err) {
subject.onError(err);
return;
}
const {_scroll_id, hits: {hits}} = res;
if(!hits.length) {
subject.onCompleted();
} else {
subject.onNext(hits[0]._source as ElasticDoc);
setImmediate(() =>
elasticClient.scroll({scroll, scrollId: _scroll_id},
handleDoc));
}
};
setImmediate(() =>
elasticClient.scroll({scroll, scrollId: _scroll_id},
handleDoc));
} else {
subject.onCompleted();
}
});
return subject.asObservable();
});
}

Can't invoke the component's method

Using an EventBus on my Laravel-Vuejs project. I'm emitting an items-updated event from ItemCreate component after the successful Item creation. On the same page I'm using ItemList component which shows a list of created Items
Here is the codes:
app.js file
require('./bootstrap');
window.Vue = require('vue');
window.EventBus = new Vue();
Vue.component('item-list',
require('./components/entities/item/ItemList'));
Vue.component('item-create',
require('./components/entities/item/ItemCreate'));
const app = new Vue({
el: '#app'
});
ItemCreate.vue Component
export default {
data: function () {
return {
itemName: ''
}
},
methods: {sendItemData: function () {
axios.post('/dashboard/item', {
name: this.itemName
})
.then(response => {
if (response.status === 201) {
toastr.success('Item created successfully!', {timeout: 2000});
EventBus.$emit('items-updated');
}
})
.catch(error => {
toastr.error(error, 'Ooops! Something went wrong!');
})
}
}
}
ItemList.vue Component
export default {
data: function () {
return {
items: [],
}
},
methods: {
getItems: function () {
axios.get('/dashboard/items')
.then(response => {
this.items = response.data;
})
.catch(error => {
toastr.error(error, 'Ooops! Something went wrong!');
})
}
},
mounted() {
this.getItems();
EventBus.$on('items-updated', function () {
this.getItems();
});
}
}
It was a general JS mistake. Working code:
on ItemList.vue Component
export default {
data: function () {
return {
items: [],
}
},
methods: {
getItems: function () {
axios.get('/dashboard/items')
.then(response => {
this.items = response.data;
})
.catch(error => {
toastr.error(error, 'Ooops! Something went wrong!');
})
}
},
mounted() {
this.getItems();
let vm = this;
EventBus.$on('items-updated', function () {
vm.getItems();
});
}
}

Resources