Nativescript-vue Autocomplete using remote json - autocomplete

I'm desperately trying to make this work, but haven't found much documentation about it, except for this tutorial.
However, it seems to be written in TypeScript (which is strange), so using this code brings 2 errors:
'types' can only be used in a .ts file.
Here is the code:
import * as http from 'tns-core-modules/http';
export default {
template: `
<Page>
<StackLayout>
<Label text="Select airport"></Label>
<RadAutoCompleteTextView ref="autocomplete"
displayMode="plain"
suggestMode="Suggest"
:items="dataItems">
<SuggestionView ~suggestionView suggestionViewHeight="300">
<StackLayout v-suggestionItemTemplate orientation="vertical" padding="10">
<v-template>
<Label :text="item.text"></Label>
</v-template>
</StackLayout>
</SuggestionView>
</RadAutoCompleteTextView>
</StackLayout>
</Page>
`,
data () {
return {
dataItems: new ObservableArray(),
};
},
mounted () {
const jsonUrl = 'https://raw.githubusercontent.com/telerik/nativescript-ui-samples/master/examples-data/airports.json';
this.$refs.autocomplete.setLoadSuggestionsAsync((text) => {
const promise = new Promise((resolve, reject) => {
http.getJSON(jsonUrl).then((r: any) => {
const airportsCollection = r.airports;
const items: Array<TokenModel> = new Array();
for (let i = 0; i < airportsCollection.length; i++) {
items.push(new TokenModel(airportsCollection[i].FIELD2, null));
}
resolve(items);
}).catch((err) => {
const message = `Error fetching remote data from ${jsonUrl}: ${err.message}`;
console.log(message);
alert(message);
reject();
});
});
return promise;
});
},
};
Thanks for enlightening me !

Try removing the typings.
http.getJSON(jsonUrl).then((r: any) => {
will become
http.getJSON(jsonUrl).then((r) => {
Also
const items: Array<TokenModel> = new Array();
will become
const items = new Array();

I just ran into something similar and while I can get this working I'm a bit confused as to how when I set the AutoComplete items to the local dataItems array I never see in the setLoad

Related

react-map-gl-draw addFeatures creating duplicates

Describe the Issue
When feature is deleted and new feature is added for vertices to update. The new feature is created twice. Every time this cycle is repeated.
Function deleteVertex have the the code of this problem.
Actual Result
new duplicate feature is added.
Expected Result
Only 1 new feature should be created.
Reproduce Steps
Add a feature. Remove the Feature any 1 index of coordinates. Remove the old Feature. Add the this new modified version of Feature using addFeatures.
Question
Also I want to know if there is any easy way to add or remove the vertices.
Code
const MapOverLay = ({
type,
viewport,
setViewport,
save,
close,
previousFeatures,
defaultViewPort,
}) => {
const editorRef = useRef(null);
const featureRef = useRef(null);
const locateIconRef = useRef(false);
const [previousFeature, setPreviousFeature] = useState(previousFeatures);
const [mapData, setMapData] = useState({
editor: {
type: previousFeatures.length ? 'Editing' : '',
mode: previousFeatures.length ? new EditingMode() : null,
selectedFeatureIndex: null,
},
});
const onSelect = map => {
if (map.selectedEditHandleIndex !== null) {
featureRef.current = {
type: 'selectedEditHandleIndex',
index: map.selectedEditHandleIndex,
featureIndex: map.selectedFeatureIndex,
};
}
else {
featureRef.current = {
type: 'selectedFeatureIndex',
index: map.selectedFeatureIndex,
};
}
};
const onUpdate = ({ editType, data }) => {
if (editType === 'addFeature') {
const temp_data = makeClone(mapData);
temp_data.editor.type = 'Editing';
temp_data.editor.mode = new EditingMode();
setMapData(temp_data);
}
if (previousFeatures.length) {
setPreviousFeature(data);
}
};
const deleteVertex = () => {
const feature = editorRef.current.getFeatures()[0];
if (feature.geometry.coordinates[0].length !== 2) {
feature.geometry.coordinates[0].splice(featureRef.current.index, 1);
editorRef.current.deleteFeatures(featureRef.current.featureIndex);
editorRef.current.addFeatures(feature);
console.log('Delete');
}
else {
editorRef.current.deleteFeatures(featureRef.current.featureIndex);
}
featureRef.current = null;
};
// console.log(editorRef.current);
const deleteFeature = () => {
if (previousFeature.length) {
setPreviousFeature([]);
}
else {
editorRef.current.deleteFeatures(featureRef.current.index);
}
save([]);
};
const onDelete = () => {
if (featureRef.current !== null) {
switch (featureRef.current.type) {
case 'selectedEditHandleIndex':
deleteVertex();
return;
case 'selectedFeatureIndex':
deleteFeature();
return;
default:
break;
}
}
};
const onSave = map => {
save(map.getFeatures());
};
const getDrawModeStyle = () => {
switch (type) {
case 'Polygon':
return style.polygon;
case 'Point':
return style.point;
}
};
const getDrawModePressedStyle = () => {
switch (type) {
case 'Polygon':
return style.pressed_polygon;
case 'Point':
return style.pressed_point;
}
};
const getDrawMode = () => {
switch (type) {
case 'Polygon':
return new DrawPolygonMode();
case 'Point':
return new DrawPointMode();
}
};
const onToolClick = () => {
if (editorRef.current.getFeatures().length === 0) {
const temp_data = makeClone(mapData);
if (type === temp_data.editor.type) {
temp_data.editor.type = '';
temp_data.editor.mode = null;
setMapData(temp_data);
}
else {
temp_data.editor.type = type;
temp_data.editor.mode = getDrawMode();
setMapData(temp_data);
}
}
};
const locate = map => {
locateIconRef.current = true;
const features = map.getFeatures();
if (features.length) {
const center = centerOfMass(features[0]);
setViewport(prevState => ({
...prevState,
zoom: 10,
longitude: center.geometry.coordinates[0],
latitude: center.geometry.coordinates[1],
}));
}
else {
defaultViewPort();
}
};
const viewPortChangeFromMap = nextViewport => {
locateIconRef.current = false;
setViewport(nextViewport);
};
const _renderDrawTools = () => {
// copy from mapbox
return (
<>
<div className='mapboxgl-ctrl-top-left'>
<div className={['mapboxgl-ctrl-group mapboxgl-ctrl', style.navigation_group_top_left].join(' ')}>
<button
className="mapbox-gl-draw_ctrl-draw-btn"
title="Save"
onClick={() => onSave(editorRef.current)}
/>
<button
id={getDrawModeStyle()}
className={
[
'mapbox-gl-draw_ctrl-draw-btn mapbox-gl-draw_polygon',
mapData.editor.type === type ? getDrawModePressedStyle() : '',
].join(' ')
}
title="Select"
onClick={() => onToolClick()}
/>
<button
className="mapbox-gl-draw_ctrl-draw-btn mapbox-gl-draw_trash"
title="Remove"
onClick={onDelete}
/>
</div>
</div>
<div className='mapboxgl-ctrl-top-right'>
<div className={['mapboxgl-ctrl-group mapboxgl-ctrl', style.navigation_group_top_right].join(' ')}>
<button
className="mapbox-gl-draw_ctrl-draw-btn"
title="Close"
onClick={close}
/>
</div>
</div>
<div className='mapboxgl-ctrl-bottom-right'>
<div className={['mapboxgl-ctrl-group mapboxgl-ctrl', style.navigation_group_bottom_right].join(' ')}>
<button
className="mapbox-gl-draw_ctrl-draw-btn"
title="Focus"
onClick={() => locate(editorRef.current)}
/>
</div>
</div>
</>
);
};
return (
<ReactMapGL
{...viewport}
mapStyle="mapbox://styles/giddyops/ckips5wdw61xb17qs3eorsoj9"
mapboxApiAccessToken={MAPBOX_TOKEN}
onViewportChange={viewPortChangeFromMap}
attributionControl={false}
transitionDuration={1000}
transitionInterpolator={locateIconRef.current ? new FlyToInterpolator() : null}
>
<Editor
ref={editorRef}
mode={mapData.editor.mode}
clickRadius={12}
features={previousFeature.length ? previousFeature : null}
onSelect={onSelect}
onUpdate={onUpdate}
editHandleShape={'circle'}
featureStyle={getFeatureStyle}
editHandleStyle={getEditHandleStyle}
onClick={e => console.log(e)}
/>
{_renderDrawTools()}
</ReactMapGL>
);
};
I have reviewed your code and found out that it could be issue that delete features was finished deleting way after than adding feature.
you can solve this issue by making the following changes:
make deleteVertex function async.
cloning the feature in the first line.
await the editorRef.current.deleteFeatures(featureRef.current.featureIndex);

testing input events with react testing library

I've created a small keypad app in react and I'm trying to test the input event on the app and for some reason I am not getting the expected result. I'm trying to test it to failure and success. The test I'm running is this below, I want to input 1995 (the correct combination), click the unlock button and ultimately have a message return Unlocked! but it only returns Incorrect Code! which should only happen if the code is incorrect or the input field is empty. But it shouldn't be empty as I have filled it out in the test..
here is a codesandbox: https://codesandbox.io/s/quirky-cloud-gywu6?file=/src/App.test.js:0-26
Any ideas?
test:
const setup = () => {
const utils = render(<App />);
const input = utils.getByLabelText("input-code");
return {
input,
...utils
};
};
test("It should return a successful try", async () => {
const { input, getByTestId } = setup();
await act(async () => {
fireEvent.change(input, { target: { value: "1995" } });
});
expect(input.value).toBe("1995");
await act(async () => {
fireEvent.click(getByTestId("unlockbutton"));
});
expect(getByTestId("status")).toHaveTextContent("Unlocked!");
});
the component I'm trying to test
import React, { useState, useEffect } from "react";
import Keypad from "./components/Keypad";
import "./App.css";
import "./css/Result.css";
function App() {
//correctCombination: 1995
const [result, setResult] = useState("");
const [locked, setLocked] = useState("Locked");
const [tries, setTries] = useState(0);
const [hide, setHide] = useState(true);
//Along with the maxLength property on the input,
// this is also needed for the keypad
useEffect(() => {
(function() {
if (result >= 4) {
setResult(result.slice(0, 4));
}
})();
}, [result]);
const onClick = button => {
switch (button) {
case "unlock":
checkCode();
break;
case "clear":
clear();
break;
case "backspace":
backspace();
break;
default:
setResult(result + button);
break;
}
};
const checkCode = () => {
if (result === "1995") {
setLocked("Unlocked!");
setTries(0);
} else if (tries === 3) {
setHide(false);
setLocked("Too many incorrect attempts!");
setTimeout(() => {
setHide(true);
}, 3000);
} else {
setLocked("Incorrect code!");
setTries(tries + 1);
}
};
const clear = () => {
setResult("");
};
const backspace = () => {
setResult(result.slice(0, -1));
};
const handleChange = event => {
setResult(event.target.value);
};
return (
<div className="App">
<div className="pin-body">
<h1>Pin Pad</h1>
<div className="status">
<h2 data-testid="status">{locked}</h2>
</div>
<div className="result">
<input
maxLength={4}
type="phone"
aria-label="input-code"
data-testid="inputcode"
placeholder="Enter code"
onChange={handleChange}
value={result}
/>
</div>
{hide ? <Keypad onClick={onClick} /> : false}
</div>
</div>
);
}
export default App;

Not closing camera in zxing

In the Zxing library, I want to close the camera when the user clicks cancel. So I used a button and add onclick event to it. it is calling resetReader method. I called this method after gets a barcode value or in the cancel button onclick event.If it is getting barcode values, this resetReader method works perfectly. if we cancel, the camera doesn't stop. Am I missing something?
const codeReader = new BrowserMultiFormatReader(hints);
const resetReader = () => {
codeReader.reset();
codeReader.stopContinuousDecode();
};
for those who haven't figured it out yet? I have found a solution to this problem. Harendrra's solution didn't work for me, but this one did in combination with usestate. For my project the code uses Bootstrap. So when I click on a button the Modal appears. The camera loads. When I click on the Close button the camera disappears. Hope this is a solutions for everyone, enjoy ;-)
export default function Example(props) {
// codeReader
const [codeReader, setReader] = useState(new BrowserMultiFormatReader());
const [videoInputDevices, setVideoInputDevices] = useState([]);
const [selectedVideoDevice, selectVideoDevice] = useState('');
useEffect(() => {
(async () => {
const videoInputDeviceList = await codeReader.listVideoInputDevices();
setVideoInputDevices(videoInputDeviceList);
if (videoInputDeviceList.length > 0 && selectedVideoDevice == null) {
selectVideoDevice(videoInputDeviceList[0].deviceId);
}
})();
}, [codeReader, selectedVideoDevice]);
const handleShow = () => {
setBrand('');
// Open modal.
setShow(true);
codeReader.decodeFromVideoDevice(selectedVideoDevice, 'videoElement', (res) => {
setCanClose(true);
if (res) {
const rawText = res.getText();
axios
.get(`https://world.openfoodfacts.org/api/v0/product/${rawText}.json`)
.then((result) => {
// set data
setBrand(result.data.product.brands);
// close modal
setShow(false);
// codeReader reset
codeReader.reset();
})
.catch((err) => console.log('error', err));
}
});
};
const handleClose = () => {
// codeReader reset.
setReader(codeReader.reset());
// Close modal
setShow(false);
// Set new codeReader.
// The solution for the error messages after the codeReader reset.
// This will build the codeReader for the next time.
setReader(new BrowserMultiFormatReader(hints));
};
return (
<Fragment>
<div className='py-2'>
<div>Brand: {brand}</div>
<Button variant='primary' onClick={handleShow}>
Launch static backdrop modal
</Button>
<Modal show={show} onHide={handleClose} backdrop='static' keyboard={false} centered id='scanProductModal'>
<Modal.Body>
<div
onChange={(event) => {
const deviceId = event.target.value;
selectVideoDevice(deviceId);
}}
>
<div className='button-group-top'>
<select className='form-select form-select-sm' aria-label='Default select example'>
{videoInputDevices &&
videoInputDevices.map((inputDevice, index) => {
return (
<option value={inputDevice.deviceId} key={index}>
{inputDevice.label || inputDevice.deviceId}
</option>
);
})}
</select>
</div>
<video id='videoElement' width='600' height='400' />
<Button className='btn btn-danger' onClick={handleClose}>
Close
</Button>
</div>
</Modal.Body>
</Modal>
</Fragment>
);
}
Yes, I resolved. You have to create codeReader object at the top of the Class. Try this code.
import "../App.css";
import { BrowserBarcodeReader } from "#zxing/library";
class Barcode extends React.Component {
codeReader = new BrowserBarcodeReader();
constructor(props) {
super(props);
this.state = { reader: {}, selectedDevice: "" };
this.startButton = this.startButton.bind(this);
this.resetButton = this.resetButton.bind(this);
this.getBarcode = this.getBarcode.bind(this);
}
componentDidMount() {
this.getBarcode();
}
startButton() {
console.log("start", this.codeReader);
this.codeReader
.decodeOnceFromVideoDevice(this.state.selectedDevice, "video")
.then(result => {
document.getElementById("result").textContent = result.text;
})
.catch(err => {
console.error(err.toString());
document.getElementById("result").textContent = err;
});
console.log(
`Started continous decode from camera with id ${this.state.selectedDevice}`
);
}
resetButton() {
this.codeReader && this.codeReader.reset();
document.getElementById("result").textContent = "";
}
getBarcode() {
let selectedDeviceId;
return this.codeReader.getVideoInputDevices().then(videoInputDevices => {
const sourceSelect = document.getElementById("sourceSelect");
selectedDeviceId = videoInputDevices[0].deviceId;
if (videoInputDevices.length > 1) {
videoInputDevices.forEach(element => {
const sourceOption = document.createElement("option");
sourceOption.text = element.label;
sourceOption.value = element.deviceId;
sourceSelect.appendChild(sourceOption);
});
sourceSelect.onchange = () => {
selectedDeviceId = sourceSelect.value;
};
const sourceSelectPanel = document.getElementById(
"sourceSelectPanel"
);
sourceSelectPanel.style.display = "block";
}
this.setState({
selectedDevice: selectedDeviceId
});
})
.catch(err => {
alert(err);
});
}
render() {
return (
<div>
<h2>Barcode</h2>
{Object.keys(this.codeReader).length > 0 && (
<div>
<div>
<button
className="button"
id="startButton"
onClick={this.startButton}
>
Start
</button>
<button
className="button"
id="resetButton"
onClick={this.resetButton}
>
Reset
</button>
</div>
<div>
<video
id="video"
width="600"
height="400"
style={{ border: "1px solid gray" }}
></video>
</div>
<label>Result:</label>
<pre>
<code id="result"></code>
</pre>
</div>
)}
</div>
);
}
}
export default Barcode; ```

waitForEvent is not finding the DOM change after fireEvent is called

I have the following component that I'm trying to test with react-testing-library:
const PasswordIconButton = ({
stateString
}) => {
const { state, dispatch } = useContext(Store);
const showPassword = getObjectValue(stateString, state);
const toggleShowPassword = event => {
event.preventDefault();
dispatch(toggleBoolean(stateString, !showPassword));
};
return (
<Layout
showPassword={showPassword}
toggleShowPassword={toggleShowPassword}
/>
);
};
export default PasswordIconButton;
const Layout = ({
showPassword,
toggleShowPassword
}) => {
return (
<IconButton onClick={toggleShowPassword} data-testid="iconButton">
{showPassword ? (
<HidePasswordIcon data-testid="hidePasswordIcon" />
) : (
<ShowPasswordIcon data-testid="showPasswordIcon" />
)}
</IconButton>
);
};
This works exactly as intended in production. If the user clicks the button then it calls toggleShowPassword() which toggles the value of boolean const showPassword.
If showPassword is equal to false and the user clicks the button, I can see that the <ShowPasswordIcon /> is removed and <HidePasswordIcon /> appears. Both have the correct data-testid attributes set.
I'm attempting to test the component will the following test:
import React from "react";
import {
render,
cleanup,
fireEvent,
waitForElement
} from "react-testing-library";
import PasswordIconButton from "./PasswordIconButton";
afterEach(cleanup);
const mockProps = {
stateString: "signUpForm.fields.password.showPassword"
};
describe("<PasswordIconButtonIcon />", () => {
it("renders as snapshot", () => {
const { asFragment } = render(<PasswordIconButton {...mockProps} />);
expect(asFragment()).toMatchSnapshot();
});
//
// ISSUE IS WITH THIS TEST:
// ::::::::::::::::::::::::::
it("shows 'hide password' icon on first click", async () => {
const { container, getByTestId } = render(
<PasswordIconButton {...mockProps} />
);
const icon = getByTestId("iconButton");
fireEvent.click(icon);
const hidePasswordIconTestId = await waitForElement(
() => getByTestId("hidePasswordIcon"),
{ container }
);
expect(hidePasswordIconTestId).not.toBeNull();
});
});
The shows 'hide password' icon on first click test always fails and I'm not sure why. The mockProps are definitely correct and work perfectly in production.
What am I missing here?
I figured it out... The issue is that I needed to wrap the component in the context provider as const { state, dispatch } = useContext(Store); won't work properly without it.
So I changed the render to:
const { container, getByTestId } = render(
<StateProvider>
<PasswordIconButton {...mockProps} />
</StateProvider>
);`
And now the test passes fine.

Using react-select Async with loadOptions and redux-form

I'm using react-select library to display a select box. I'm using Select.Async because I need to pull my options from an API. I use Select with loadOptions and it works during the intial page render. However, I'm also using redux-form which can change the value of a Field dynamically (using change). However, when I change the value of the Field like this, the value of the input does change (and I can verify this), but react-select's loadOptions is never called again (even though I thought it was supposed to be listening to a change of value). My question is, is there a way to dynamicaly call loadOptions every time the input value changes?
Thanks,
Edit: Answered on github here
this.state = {
store: '',
};
this.handleStoreSelect = this.handleStoreSelect.bind(this);
handleStoreSelect = (item) => {
this.setState({
store: item.value
}
};
<Select.Async
name="storeID"
value={this.state.store}
loadOptions={getStores}
onChange={this.handleStoreSelect}
/>
const getStores = () => {
return fetch(
"api to be hit",
{
method: 'get',
headers: {
'Content-Type': 'application/json'
}
}
)
.then(response => {
if(response.status >= 400){
throw new Error("error");
}
return response.json()
})
.then(stores => {
let ret = [];
for(let store of stores) {
ret.push({value: store._id, label: store.name})
}
return {options: ret};
})
.catch(err => {
console.log('could not fetch data');
console.log(err);
return {options: []}
})
};
Using this we can fetch the data and pass this object in the loadoptions.
copy this code outside the class. and also i'm posting the code to be implemented for loadoptions
It might be a better solution than this, but a quick one is to set a ref to your Select.Async component, and when a change action is triggered (like the change of an input - your case, or one button click event - like in the code below) you can update its options. I'm using a similar example with the example of their docs.
class YourClass extends React.Component {
getOptions = (input, callback) => {
setTimeout(function () {
callback(null, {
options: [
{value: 'one', label: 'One'},
{value: 'two', label: 'Two'}
]
});
}, 500);
};
updateOptions = () => {
this.selectAsync.state.options.push(
{value: 'three', label: 'Three'}
)
}
render() {
let props = this.props;
return (
<div>
<Select.Async
ref={selectAsync => this.selectAsync = selectAsync}
loadOptions={this.getOptions}
/>
<button onClick={this.updateOptions}>
Load more items
</button>
</div>
)
}
}