using Alpine js to show something for exactly one second - alpine.js

I'd like my alpine js to x-show something for only one second, repeatedly, every 60 seconds. I've got a js timer (likely more robust than I need) and I can get x-text to show me the seconds counting down and then resetting, but I can't quite figure out what to put into x-show to get the div to display at the time I've designated. Here's my html/alpine
<p x-text="time().seconds"></p>
<div x-show="time().seconds.value === 48">
hello
</div>
and my javascript timer:
function timer(expiry) {
return {
expiry: expiry,
remaining:null,
init() {
this.setRemaining()
setInterval(() => {
this.setRemaining();
}, 1000);
},
setRemaining() {
const diff = this.expiry - new Date().getTime();
this.remaining = parseInt(diff / 1000);
},
days() {
return {
value:this.remaining / 86400,
remaining:this.remaining % 86400
};
},
hours() {
return {
value:this.days().remaining / 3600,
remaining:this.days().remaining % 3600
};
},
minutes() {
return {
value:this.hours().remaining / 60,
remaining:this.hours().remaining % 60
};
},
seconds() {
return {
value:this.minutes().remaining,
};
},
format(value) {
return ("0" + parseInt(value)).slice(-2)
},
time(){
return {
days:this.format(this.days().value),
hours:this.format(this.hours().value),
minutes:this.format(this.minutes().value),
seconds:this.format(this.seconds().value),
}
},
}
}
</script>

I changed the format function to a string:
format(value) {
return ("0" + value).slice(-2)
}
and then x-show worked fine with this modification:
<div x-show="time().seconds === '48'">
hello
</div>

Related

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();
});
}

infinite scrolling using AgGridReact

I'm trying to achieve infinite scrolling using ag grid react component, but it doesn't seems to be working.
here is my implementation :
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid/dist/styles/ag-grid.css';
import 'ag-grid/dist/styles/ag-theme-balham.css';
class TasksGridContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: true,
gridOptions: {
//virtual row model
rowModelType: 'infinite',
paginationPageSize: 100,
cacheOverflowSize: 2,
maxConcurrentDatasourceRequests: 2,
infiniteInitialRowCount: 1,
maxBlocksInCache: 2,
components: {
loadingRenderer: function(params) {
console.log('loadingCellRenderer', params);
if (params.value !== undefined) {
return params.value;
} else {
return '<img src="https://raw.githubusercontent.com/ag-grid/ag-grid-docs/master/src/images/loading.gif">';
}
}
},
defaultColDef: {
editable: false,
enableRowGroup: true,
enablePivot: true,
enableValue: true
}
}
};
}
componentDidMount() {
this.props.actions.getAssignedTasks();
this.props.actions.getTeamTasks();
}
componentWillReceiveProps(newProps) {
if (this.props.taskView.taskGrid.listOfTasks.length > 0) {
this.setState({
loading: false ,
gridOptions: {
datasource: this.props.taskView.taskGrid.listOfTasks
}
});
}
}
render() {
return (
<div id="tasks-grid-container">
<div style={Style.agGrid} id="myGrid" className="ag-theme-balham">
<AgGridReact
columnDefs={this.props.taskView.taskGrid.myTaskColumns}
rowData={this.props.taskView.taskGrid.listOfTasks}
gridOptions={this.state.gridOptions}>
</AgGridReact>
</div>
</div>
);
}
}
TasksGridContainer.propTypes = {
listOfTasks: PropTypes.array,
actions: PropTypes.object
};
const mapStateToProps = ({ taskView }) => {
return {
taskView: {
taskGrid: {
listOfTasks: taskView.taskGrid.listOfTasks,
myTaskColumns: taskView.taskGrid.myTaskColumns,
teamTaskColumns: taskView.taskGrid.teamTaskColumns
}
}
}
};
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators(taskGridActions, dispatch)
};
}
module.exports = connect(mapStateToProps, mapDispatchToProps)(TasksGridContainer);
columnDefs are being set once props.taskView.taskGrid.myTaskColumns is available.
a sample columndef:
[
{
cellRenderer: "loadingRenderer", checkboxSelection: true, field: "action", headerCheckboxSelection: true, headerCheckboxSelectionFilteredOnly: true, headerName: "Action"
},
{
"activity"headerName: "Activity Name"
}
]
Although grid is loading fine, but when i scroll it should call "loadingRenderer" component. But,I'm not able to see any loading gif when i scroll.
Am i doing something wrong in implementation?
Actual issue was not calling the the props properly and was not having onGridReady function to use gridAPi.
I modified the code and it starts working:
<AgGridReact
components={this.state.components}
enableColResize={true}
rowBuffer={this.state.rowBuffer}
debug={true}
rowSelection={this.state.rowSelection}
rowDeselection={true}
rowModelType={this.state.rowModelType}
paginationPageSize={this.state.paginationPageSize}
cacheOverflowSize={this.state.cacheOverflowSize}
maxConcurrentDatasourceRequests={this.state.maxConcurrentDatasourceRequests}
infiniteInitialRowCount={this.state.infiniteInitialRowCount}
maxBlocksInCache={this.state.maxBlocksInCache}
columnDefs={this.props.columns}
rowData={this.props.rowData}
onGridReady={this.onGridReady}
>
</AgGridReact>
state :
this.state = {
components: {
loadingRenderer: function(params) {
if (params.value !== undefined) {
return params.data.action;
} else {
return '<img src="https://raw.githubusercontent.com/ag-grid/ag-grid-docs/master/src/images/loading.gif">';
}
}
},
rowBuffer: 0,
rowSelection: "multiple",
rowModelType: "infinite",
paginationPageSize: 100,
cacheOverflowSize: 2,
maxConcurrentDatasourceRequests: 2,
infiniteInitialRowCount: 1,
maxBlocksInCache: 2
};
onGridReady function :
onGridReady = (params, data = []) => {
this.gridApi = params;
this.gridColumnApi = params.columnApi;
this.updateData(params,data);
}

Sort() not working

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>

Get component to wait for asynchronous data before rendering

I am calling an endpoint to bring back an object, which does fetch the data, however not fast enough for the component to grab the data and render. Instead, the component renders with blank values where there should be data.
If I break point the code on creation, then continue maybe a second later, the text correctly renders.
How do I implement it to not render until the data is back?
My API call:
checkScenarioType: function () {
this.$http.get('ScenariosVue/GetScenarioTypeFromParticipant/' + this.ParticipantId).then(response => {
// get body data
this.ScenarioType = response.body.value;
if (this.ScenarioType.timeConstraint) {
store.commit('switchConstraint');
}
}, response => {
// error callback
});
}
The component having the issues:
var questionArea = Vue.component('questionarea', {
props: ["scenariotype"],
data: function () {
return ({
position: "",
vehicleType: ""
});
},
methods: {
transformValuesForDisplay: function () {
switch (this.scenariotype.perspective) {
case 1: {
this.position = "Driver";
this.vehicleType = "Autonomous";
break;
}
case 2: {
this.position = "Passenger";
this.vehicleType = "Manually Driven";
break;
}
case 3: {
this.position = "Driver";
this.vehicleType = "Manually Driven";
break;
}
}
}
},
beforeMount() {
this.transformValuesForDisplay();
},
template:
`<h1>You are the {{ this.position }}! What should the {{ this.vehicleType }} car do?</h1>`
});
In cases like there's asynchronous loading of data, we typically use a simple v-if to hide the element until the data is present.
The template would be like:
<h1 v-if="position">You are the {{ position }}! What should the {{ vehicleType }} car do?</h1>
Notice the use of this in the template is unnecessary.
Also, in your case, instead of the beforeMount() hook, you would add a (deep/immediate) watch to the prop, to pick up changes when it is loaded externally:
watch: {
scenariotype: {
handler: function(newValue) {
this.transformValuesForDisplay();
},
deep: true,
immediate: true
}
},
Full demo below.
Vue.component('questionarea', {
props: ["scenariotype"],
data: function () {
return ({
position: "",
vehicleType: ""
});
},
methods: {
transformValuesForDisplay: function () {
switch (this.scenariotype.perspective) {
case 1: {
this.position = "Driver";
this.vehicleType = "Autonomous";
break;
}
case 2: {
this.position = "Passenger";
this.vehicleType = "Manually Driven";
break;
}
case 3: {
this.position = "Driver";
this.vehicleType = "Manually Driven";
break;
}
}
}
},
watch: {
scenariotype: {
handler: function(newValue) {
this.transformValuesForDisplay();
},
deep: true,
immediate: true
}
},
template:
`<h1 v-if="position">You are the {{ position }}! What should the {{ vehicleType }} car do?</h1>`
});
new Vue({
el: '#app',
data: {
ScenarioType: {perspective: null}
},
methods: {
checkScenarioType: function () {
this.$http.get('https://reqres.in/api/users/2').then(response => {
// get body data
this.ScenarioType.perspective = response.body.data.id; // for testing purposes only
}, response => {
// error callback
});
}
},
mounted: function() {
this.checkScenarioType();
}
})
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vue-resource"></script>
<div id="app">
<p>Notice while it is null, the h1 is hidden: {{ ScenarioType }}</p>
<br>
<questionarea :scenariotype="ScenarioType"></questionarea>
</div>

Double click and click on ReactJS Component

I have a ReactJS component that I want to have different behavior on a single click and on a double click.
I read this question.
<Component
onClick={this.onSingleClick}
onDoubleClick={this.onDoubleClick} />
And I tried it myself and it appears as though you cannot register both single click and double click on a ReactJS component.
I'm not sure of a good solution to this problem. I don't want to use a timer because I'm going to have 8 of these single components on my page.
Would it be a good solution to have another inner component inside this one to deal with the double click situation?
Edit:
I tried this approach but it doesn't work in the render function.
render (
let props = {};
if (doubleClick) {
props.onDoubleClick = function
} else {
props.onClick = function
}
<Component
{...props} />
);
Here is the fastest and shortest answer:
CLASS-BASED COMPONENT
class DoubleClick extends React.Component {
timer = null
onClickHandler = event => {
clearTimeout(this.timer);
if (event.detail === 1) {
this.timer = setTimeout(this.props.onClick, 200)
} else if (event.detail === 2) {
this.props.onDoubleClick()
}
}
render() {
return (
<div onClick={this.onClickHandler}>
{this.props.children}
</div>
)
}
}
FUNCTIONAL COMPONENT
const DoubleClick = ({ onClick = () => { }, onDoubleClick = () => { }, children }) => {
const timer = useRef()
const onClickHandler = event => {
clearTimeout(timer.current);
if (event.detail === 1) {
timer.current = setTimeout(onClick, 200)
} else if (event.detail === 2) {
onDoubleClick()
}
}
return (
<div onClick={onClickHandler}>
{children}
</div>
)
}
DEMO
var timer;
function onClick(event) {
clearTimeout(timer);
if (event.detail === 1) {
timer = setTimeout(() => {
console.log("SINGLE CLICK");
}, 200)
} else if (event.detail === 2) {
console.log("DOUBLE CLICK");
}
}
document.querySelector(".demo").onclick = onClick;
.demo {
padding: 20px 40px;
background-color: #eee;
user-select: none;
}
<div class="demo">
Click OR Double Click Here
</div>
I know this is an old question and i only shoot into the dark (did not test the code but i am sure enough it should work) but maybe this is of help to someone.
render() {
let clicks = [];
let timeout;
function singleClick(event) {
alert("single click");
}
function doubleClick(event) {
alert("doubleClick");
}
function clickHandler(event) {
event.preventDefault();
clicks.push(new Date().getTime());
window.clearTimeout(timeout);
timeout = window.setTimeout(() => {
if (clicks.length > 1 && clicks[clicks.length - 1] - clicks[clicks.length - 2] < 250) {
doubleClick(event.target);
} else {
singleClick(event.target);
}
}, 250);
}
return (
<a onClick={clickHandler}>
click me
</a>
);
}
I am going to test this soon and in case update or delete this answer.
The downside is without a doubt, that we have a defined "double-click speed" of 250ms, which the user needs to accomplish, so it is not a pretty solution and may prevent some persons from being able to use the double click.
Of course the single click does only work with a delay of 250ms but its not possible to do it otherwise, you have to wait for the doubleClick somehow...
All of the answers here are overcomplicated, you just need to use e.detail:
<button onClick={e => {
if (e.detail === 1) handleClick();
if (e.detail === 2) handleDoubleClick();
}}>
Click me
</button>
A simple example that I have been doing.
File: withSupportDoubleClick.js
let timer
let latestTouchTap = { time: 0, target: null }
export default function withSupportDoubleClick({ onDoubleClick = () => {}, onSingleClick = () => {} }, maxDelay = 300) {
return (event) => {
clearTimeout(timer)
const touchTap = { time: new Date().getTime(), target: event.currentTarget }
const isDoubleClick =
touchTap.target === latestTouchTap.target && touchTap.time - latestTouchTap.time < maxDelay
latestTouchTap = touchTap
timer = setTimeout(() => {
if (isDoubleClick) onDoubleClick(event)
else onSingleClick(event)
}, maxDelay)
}
}
File: YourComponent.js
import React from 'react'
import withSupportDoubleClick from './withSupportDoubleClick'
export default const YourComponent = () => {
const handleClick = withSupportDoubleClick({
onDoubleClick: (e) => {
console.log('double click', e)
},
onSingleClick: (e) => {
console.log('single click', e)
},
})
return (
<div
className="cursor-pointer"
onClick={handleClick}
onTouchStart={handleClick}
tabIndex="0"
role="button"
aria-pressed="false"
>
Your content/button...
</div>
)
}
onTouchStart start is a touch event that fires when the user touches the element.
Why do you describe these events handler inside a render function? Try this approach:
const Component = extends React.Component {
constructor(props) {
super(props);
}
handleSingleClick = () => {
console.log('single click');
}
handleDoubleClick = () => {
console.log('double click');
}
render() {
return (
<div onClick={this.handleSingleClick} onDoubleClick={this.handleDoubleClick}>
</div>
);
}
};

Resources