Triggering Cordova Camera Plugin w/ Reactjs - reactjs

I am trying to trigger the cordova camera plugin from inside reactjs. In previous iterations It was utilizing a simple HTML5 file input.
Upon further research I discovered that that the current webview that cordova uses for android platforms does not provide an option for camera control (thus the native cordova plugin).
I am trying to trigger the cordova camera plugin from inside reactjs after being built with:
npm run build
then the contents of the app's build directory are coppied to cordovas 'www' directory.
The Cordova app is relatively vanilla and camera plugin added with the command.
cordova plugin add cordova-plugin-camera
Here is the config.xml
<?xml version='1.0' encoding='utf-8'?>
<widget id="io.cordova.hellocordova" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>HelloCordova</name>
<description>
A sample Apache Cordova application that responds to the deviceready event.
</description>
<author email="dev#cordova.apache.org" href="http://cordova.io">
Apache Cordova Team
</author>
<content src="index.html" />
<plugin name="cordova-plugin-whitelist" spec="1" />
<access origin="*" />
<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />
<allow-intent href="tel:*" />
<allow-intent href="sms:*" />
<allow-intent href="mailto:*" />
<allow-intent href="geo:*" />
<platform name="android">
<allow-intent href="market:*" />
</platform>
<platform name="ios">
<allow-intent href="itms:*" />
<allow-intent href="itms-apps:*" />
</platform>
<plugin name="cordova-plugin-console" spec="^1.1.0" />
<plugin name="cordova-plugin-camera" spec="^4.0.3" />
<plugin name="cordova-plugin-media-capture" spec="^3.0.2" />
<engine name="android" spec="^7.1.4" />
</widget>
The React Component is as follows
import React, { Component } from "react";
import { Alert } from "reactstrap";
import "../../Containers/containers.css";
import { connect } from "react-redux";
import userTools from "../../Services/userTools";
class Avatar extends Component {
constructor() {
super();
this.state = {
avatar: "https://denisol.se/wp-content/uploads/2018/05/empty-avatar.jpg"
};
}
takepicture() {
if (!window.cordova) {
var Camera;
//Unless theres another way to suppress webpack
//During Build
}
navigator.camera.getPicture(
file => this.readFiles(file),
err => console.log(err),
{
quality: 50,
destinationType: Camera.DestinationType.FILE_URI
}
);
}
readFiles(file) {
var reader = new FileReader();
reader.readAsDataURL(file);
reader.addEventListener(
"load",
() => {
console.log(reader.result);
this.setState({
avatar: reader.result
});
},
false
);
}
render() {
var avatarurl;
if (this.props.myinfo && this.props.myinfo.avatar.avatar.url) {
avatarurl = this.props.myinfo.avatar.url;
} else {
avatarurl =
"https://denisol.se/wp-content/uploads/2018/05/empty-avatar.jpg";
}
if (this.props.new) {
avatarurl = this.state.avatar;
}
return (
<div>
<img
id="avatar"
alt="test"
src={avatarurl}
onClick={() => {
if (this.props.updatable || this.props.new) {
this.takepicture();
//As for normal html input you would do the following
this.refs.fileUploader.click();
}
}}
/>
<input
type="file"
name="avatar"
ref="fileUploader"
style={{ display: "none" }}
accept="image/*;capture=camera"
capture
onChange={e => {
if (e.target.files.length && this.props.updatable) {
userTools.updateAvatar(e.target.files[0]);
} else if (e.target.files.length && this.props.new) {
this.readFiles(e.target.files[0]);
this.props.newAvatar(e.target.files[0]);
}
}}
/>
</div>
);
}
}
function mapStateToProps(state) {
return { myinfo: state.myinfoReducer };
}
export default connect(mapStateToProps)(Avatar);
when i execute the function there seems to be no response from cordova (permissions request, camera opening, ect...). Note that this component is only part of a larger react project where everything else is working more or less as expected.
Any Help is greatly appreciated thanks.

i made this sample repo to study the problem and it seems to be recovering the image correctly.
By doing this and reading the event handling docs, a possible cause could be the lack of bind() on handlers since this is a class component.
Please see the repo and i really rope it helps.

I was able to get the camera functionality working by migrating my react project over to browserify. Not the simplest solution but will work for the time being thanks for that tip.

Related

React Admin - overiding handleSubmit on SimpleForm

What I want to achieve is this but using react admin SimpleForm instead of Form:
import React, { useState } from "react";
export function NameForm(props) {
const [name, setName] = useState("");
const handleSubmit = (evt) => {
evt.preventDefault();
alert(`Submitting Name ${name}`)
}
return (
<form onSubmit={handleSubmit}>
<label>
Frirst Name:
<input
type="text"
value={name}
onChange={e => setName(e.target.value)}
/>
</label>
<input type="submit" value="Submit" />
</form>
);
}
When I try the same pattern, i.e.:
<SimpleForm onSubmit={handleSubmit}>
it never reaches the handleSubmit function. I also tried:
<SimpleForm handleSubmit={handleSubmit}>
But again no joy.
The react admin docs here say:
Finally, it receives a handleSubmit function as prop, to be called with the updated record as an argument when the user submits the form.
Unfortunately being new to react this doesn't give me any clue as to what I should do to get this to work.
When you're using any third party library you need to follow their rules. Here you're using React admin library which support normal admin features like add/edit/listing etc. With minimal effort you can create admin panel.
So when you're focusing on creating form using React Admin , you can create add/edit form.
In your App.js you need to first define routing using Resource which contains create and edit attribute. In create/edit you can import your add/edit component and pass it. The example is given below. You can see dataProvider link is also provided. When you'll create edit form it will take data from there
// in src/App.js
import * as React from "react";
import { Admin, Resource } from 'react-admin';
import jsonServerProvider from 'ra-data-json-server';
import { PostCreate, PostEdit } from './posts';
const App = () => (
<Admin dataProvider={jsonServerProvider('https://jsonplaceholder.typicode.com')}>
<Resource name="posts" create={PostCreate} edit={PostEdit} />
</Admin>
);
export default App;
After creating proper routing you can go to your component and can create add/edit form just like below
// in src/posts.js
import * as React from "react";
import { Create, Edit, SimpleForm, TextInput, DateInput, ReferenceManyField, Datagrid, TextField, DateField, EditButton } from 'react-admin';
import RichTextInput from 'ra-input-rich-text';
export const PostCreate = (props) => (
<Create {...props}>
<SimpleForm>
<TextInput source="title" />
<TextInput source="teaser" options={{ multiLine: true }} />
<RichTextInput source="body" />
<DateInput label="Publication date" source="published_at" defaultValue={new Date()} />
</SimpleForm>
</Create>
);
export const PostEdit = (props) => (
<Edit {...props}>
<SimpleForm>
<TextInput disabled label="Id" source="id" />
<TextInput source="title" validate={required()} />
<TextInput multiline source="teaser" validate={required()} />
<RichTextInput source="body" validate={required()} />
<DateInput label="Publication date" source="published_at" />
<ReferenceManyField label="Comments" reference="comments" target="post_id">
<Datagrid>
<TextField source="body" />
<DateField source="created_at" />
<EditButton />
</Datagrid>
</ReferenceManyField>
</SimpleForm>
</Edit>
);
React-admin injects a few props to the create and edit views: the resource name, the basePath (the root URL), the permissions, and, in the case of the edit view, the record id. That’s why you need to pass the props to the <Create> and <Edit> components.
The <Create> and <Edit> components call the dataProvider, prepare the form submit handler, and render the page title and actions.
<SimpleForm> Which you mentioned in your question is responsible for creating the form only, it's not responsible for handleSubmit operation , <Create> and <Edit> components handle that.
To know more in details follow the React Admin <Create> and <Edit> doc carefully.
So I found the solution I was looking for thanks to the answer from Saswata Pal. To achieve the effect I was looking for I changed the component so it was like this:
<Create transform={transform}>
This allowed me to grab the form result before submission and modify.
Relevant docs here:
https://marmelab.com/blog/2020/06/09/react-admin-3-6.html

Error loading model: Missing chain 'vespa'

I have my custom Searcher and my custom DocumenetProcessor in my vespa app.My service.xml is given below:
<services version="1.0">
<container id="default" version="1.0">
<document-api/>
<search>
<chain id="default" inherits="vespa">
<searcher id="com.example.test.CustomSearcher" bundle="example-vespa-app"/>
</chain>
</search>
<nodes>
<node hostalias="node1" />
</nodes>
<document-processing>
<chain id="default" inherits="vespa">
<documentprocessor id="com.example.test.CustomDocumentProcessor"/>
</chain>
</document-processing>
</container>
<content id="test_user" version="1.0">
<redundancy>1</redundancy>
<documents>
.....
</documents>
<nodes>
<node hostalias="node1" distribution-key="0" />
</nodes>
</content>
</services>
My CustomDocumentProcessor is given below:
public class CustomDocumentProcessor extends DocumentProcessor {
#Override
public Progress process(Processing processing) {
for (DocumentOperation op : processing.getDocumentOperations()) {
if (op instanceof DocumentPut) {
DocumentPut put = (DocumentPut) op;
Document document = put.getDocument();
document.setFieldValue("documentType",
String.valueOf(document.getDataType()));
}
}
return Progress.DONE;
}
}
When I remove CustomDocumentProcessor from service.xml, my app works .When I add it , It gives an error:
Request failed. HTTP status code: 400
Invalid application package: default.default: Error loading model: Missing chain 'vespa'.
Why is that? Please help.
Remove "inherits=vespa" from the document-processing chain.
There is no "vespa document processing chain like there is for search chains.

reactjs Link not working,when render after ajax request

I want render a table from ajax request data. Each row with a action link to enter a edit page.when I click the link, the url on browser was changed,but it can't enter edit page.
In addition, when I click link button, the url on browser is not I except. (It's http://192.168.1.186:4444/table/basic-table2/articles rather than http://192.168.1.186:4444/#/table/basic-table2/articles).
I'm thinking the problem maybe cause by lifecycle of component mounting. Because when I put Link element out side the table(not wait ajax request return), it work well.
I'm new in React, Can anyone finger out where I am wrong?following is the code:
renderOperator = (value, index, record) => {
const edit = <Link exact="true" to="/table/basic-table2/articles">编辑</Link>;
const context = {};
return (
<div>
<Router context={context}>
<span>
{edit}
</span>
</Router>
<a
style={styles.removeBtn}
onClick={this.deleteItem.bind(this, record)}
>
删除
</a>
</div>
);
};
fllowing is the table:
<Table
dataSource={this.state.dataSource}
isLoading={this.state.isLoading}
rowSelection={{
...this.rowSelection,
selectedRowKeys: this.state.selectedRowKeys,
}}
>
<Table.Column title="编码" dataIndex="article_id" width={120} />
<Table.Column title="名称" dataIndex="article_title" width={250} />
<Table.Column title="报名数" dataIndex="enrolled_num" width={160} />
<Table.Column title="访问人数" dataIndex="visited_user_num" width={160} />
<Table.Column title="访问次数" dataIndex="visited_num" width={120} />
<Table.Column title="转载数" dataIndex="reprinted_num" width={120} />
<Table.Column
title="操作"
cell={this.renderOperator}
lock="right"
width={120}
/>
</Table>
I think the issue may be that you're including an instance of <Router /> inside if the <Table.Column />. You shouldn't need to create a separate instance for every <Link />
Try removing the <Router /> inside of renderOperator
It took me a while to understand your question.
Firstly, please check if there is any console errors in your browser. If yes, it may block your UI router to view the page.
Secondly, if you are using react-router, here is a link to the hash router usage: https://reacttraining.com/react-router/web/api/HashRouter. Basically, you need to wrap the routes with <HashRouter>.
Lastly, a few tips to improve your code, not related to your question:
You can use <React.Fragment> to replace your <div> wrapper
You can use items.map() to iterate through the list of Table.Column.

Create custom form in react-admin

I am using Edit and SimpleForm from react-admin. How do I create a custom form to allow customised action and type on submit?
App.js
<Resource name="category" list={CategoryList} edit={CategoryEdit} />
index.js
<Edit actions={<CategoryEditActions />} title={<CategoryTitle />} {...props} >
<SimpleForm>
<DisabledInput source="id" />
<DisabledInput source="code" />
<TextInput source="name" />
</SimpleForm>
Here the api call would be /category/:categoryId with PUT request. I want to modify url to /category/:categoryId/test with method as POST. Is there any way to customize this?
I have handled this in my CustomDataProvider -
case UPDATE:
if(resource === 'category'){
options.method = 'POST';
url = `${apiUrl}/${resource}/${params.id}/test`;
} else {
options.method = 'PUT';
url = `${apiUrl}/${resource}/${params.id}`;
}
break;
Is there any other way to handle it?
This is the job of your dataProvider in react-admin (restClient in admin-on-rest). You'll have to create a custom one:
For react-admin: https://marmelab.com/react-admin/DataProviders.html#writing-your-own-data-provider
For admin-on-rest: https://marmelab.com/admin-on-rest/RestClients.html#writing-your-own-rest-client
You'll have to check the resource and type then build the fetch options by yourself.

Kendo UI Grid React customize header

I updated my data grid from Kendo UI React Wrappers to new React package #progress/kendo-react-grid 0.3.0. In old jQuery grid version (or wrapped into React components) I was able to manipulate grid header (e.g.: set columnMenu filterable to false) or to define headerTemplate.
Documentation of new package does not mention anything about this. In package source files I found directory header which contains files GridFilterRow.js, GridHeader.js, GridHeaderRow.js, but there is no way (or, I didn't found it) to customize these components.
I wonder if there is any way to customize grid header in new rewritten version of Kendo UI Grid for React?
The FilterRow of the grid is not customizable in the 0.3.0 version, and this is why it is not mentioned in the documentation.
There is an issue logged in the official kendo-react repository about this:
Make Grid Filter Cells more customizable
For the current version, the filterable and filter settings can be controlled using the columns settings per each column. And there is property headerClassName that you can use for styling the cells.
Column/filter menu is not in the roadmap for now, but you can vote for it in the official feedback portal.
You can modify the header manually, using by adding an HTML element using Java Script after the grid renders, not a pretty solution but it will do the work while waiting for the official solution.
Here is what I did:
class Table extends React.Component {
constructor(props) {
super(props);
this.gridRef = React.createRef();
}
render() {
return (
<div ref={this.gridRef} >
<Grid
data={data}
onItemChange={itemChange}
cellRender={cellRender}
rowRender={rowRender}
editField="inEdit"
>
<GridToolbar>
</GridToolbar>
<Column title="Column Name" field="ProductName" width={300} locked={true}/>
<Column field="ProductID" title="Id" editable={false} />
<Column title="Units In Stock" editor="numeric" field="UnitsInStock" />
<Column title="First Ordered" editor="date" format="{0:d}" field="FirstOrderedOn" />
<Column editor="boolean" field="Discontinued" />
<Column title="Units In Stock" editor="numeric" field="UnitsInStock" />
<Column title="First Ordered" editor="date" format="{0:d}" field="FirstOrderedOn" />
<Column editor="boolean" field="Discontinued" />
</Grid>
</div>
);
}
componentDidMount(){
var ths = this.gridRef.current.getElementsByTagName('th');
for(var i = 0; i < ths.length; i++){
ths[i].appendChild( this.createColumnMenuIcon() );
}
}
createColumnMenuIcon(){
var icon = document.createElement('i');
icon.classList.add('fa');
icon.classList.add('fa-chevron-down');
icon.setAttribute("style", "position: absolute;top: 12px;right: 10px;");
icon.addEventListener('click', function(){
console.log('Click Menu');
})
return icon;
}
}
export default Table

Resources