Why is my tmp object getting undefined values?
The fetch is getting the object right, maybe <FormStructure {...this.state.system} /> is the wrong syntax to give the Element the object?
export default class System extends React.Component<any, State> {
constructor(props: any) {
super(props);
this.state = {
system: new SystemClass(),
}
this.GetData();
}
public render() {
return (
<div>
<FormStructure {...this.state.system} />
</div>
);
}
private GetData = (): void => {
fetch('api/Admin/Get', {
headers: { 'Content-Type': 'application/json' },
method: 'GET'
})
.then(response => response.json())
.then(data => this.setState({ system: Helper.ConvertData(data, SystemClass) as SystemClass }))
.then(() => console.log(this.state.system))
};
}
const FormStructure = (system: SystemClass) => {
const onFill = () => {
var tmp = { name: system.name, street: system.street };
console.log(tmp);
console.log(system);
}
}
Related
I'm currently working on a react-redux project with a rails API in the back end and am having some issues. So I currently have an update method in the back end for my edit functionality in the front end. For the most part, it seems to work fine, it updates the back end when edited but it won't render the updated object in the front end until manually refresh the page.
I'm thinking this has something to do with my async function but I can't figure it out.
Thank you!
Here are my actions:
return (dispatch) => {
fetch(`http://localhost:3000/haikus`)
.then(res => res.json())
.then(haikus => {
dispatch({type: "FETCH_HAIKUS", payload: haikus})
})
}
}
export function addHaiku(haiku){
return (dispatch) => {
const options = {
method: "POST",
headers: {
"Content-type": "application/json",
"accept": "application/json"
},
body: JSON.stringify({haiku})
}
fetch(`http://localhost:3000/haikus`, options)
.then(res => res.json())
.then(haiku => {
dispatch({type: "ADD_HAIKU", payload: haiku})
})
}
}
export function editHaiku(haiku){
// debugger
return (dispatch) => {
const options = {
method: "PATCH",
headers: {
"Content-type": "application/json",
"accept": "application/json"
},
body: JSON.stringify({haiku})
}
fetch(`http://localhost:3000/haikus/${haiku.id}`, options)
.then(res => res.json())
.then(haiku => {
dispatch({type: "EDIT_HAIKU", payload: haiku})
// dispatch({type: "EDIT_HAIKU", payload: haiku.data})
})
}
}
export function deleteHaiku(haikuId){
return (dispatch) => {
const options = {
method: "DELETE"
}
fetch(`http://localhost:3000/haikus/${haikuId}`, options)
.then(res => res.json())
.then(message => {
dispatch({type: "DELETE_LIST", payload: haikuId})
})
}
}```
Here is my reducer:
```export default function haikuReducer(state, action){
// debugger
switch(action.type){
case "FETCH_HAIKUS":
return {
haikus: action.payload
}
case "ADD_HAIKU":
return {
haikus: [...state.haikus, action.payload]
}
case "DELETE_HAIKU":
const newHaiku = state.haikus.filter(haiku => haiku.id !== action.payload)
return {
haikus: newHaiku
}
case "EDIT_HAIKU":
// debugger
// const editHaiku = state.haikus.map(haiku => haiku.id === action.payload.id ? action.payload : haiku)
const editHaiku = state.haikus.map(haiku => haiku.id === parseInt(action.payload.id) ? action.payload : haiku)
return {
haikus: editHaiku
}
default:
return state
}
}```
Here is my form component:
```import React, {Component} from 'react'
import { withRouter } from 'react-router-dom'
import { connect } from 'react-redux'
import { addHaiku } from './actions/haikuActions'
import { editHaiku } from './actions/haikuActions'
class HaikuForm extends Component {
// Normally do not set state directly to props
constructor(props){
super(props)
this.state = {
id: this.props.haiku ? this.props.haiku.id : "",
title: this.props.haiku ? this.props.haiku.title : "",
haiku: this.props.haiku ? this.props.haiku.haiku : "",
genre: this.props.haiku ? this.props.haiku.genre : ""
}
}
handleSubmit(event) {
event.preventDefault()
if(!this.props.haiku){
this.props.addHaiku(this.state)
} else {
this.props.editHaiku(this.state)
}
this.setState({ title: "", haiku: "", genre: "" ,id: "" })
this.props.history.push('/haikus')
}
handleChange(event){
this.setState({
[event.target.name]: event.target.value
})
}
redirectOrRenderForm = () => {
return (
<form onSubmit={this.handleSubmit.bind(this)}>
<input type="text" onChange={(event) => this.handleChange(event)} value={this.state.title} name="title"/><br></br>
<input type="text" onChange={(event) => this.handleChange(event)} value={this.state.haiku} name="haiku"/><br></br>
<input type="text" onChange={(event) => this.handleChange(event)} value={this.state.genre} name="genre"/>
<input type="submit"/>
</form>
)
}
render(){
return (
<>
{this.redirectOrRenderForm()}
</>
)
}
}
export default withRouter(connect(null, { addHaiku, editHaiku })(HaikuForm))```
I have a Promise(() => return resolve()) pattern in a function I got from firebase functions samples.
const appendPromise = (requestWithoutAuth: {
spreadsheetId: any
range: string
valueInputOption: string
insertDataOption: string
resource: { values: any[][] }
}): Promise<any> => {
// eslint-disable-next-line #typescript-eslint/no-misused-promises
return new Promise((resolve, reject) => {
return getAuthorizedClient().then((client) => {
const sheets = google.sheets('v4')
const request = requestWithoutAuth
request.auth = client
return sheets.spreadsheets.values.append(request, (err: any, response: { data: unknown }) => {
if (err) {
console.log(`The API returned an error: ${err}`)
return reject(err)
}
return resolve(response.data)
})
})
})
}
eslint complains about #typescript-eslint/no-misused-promises.
I'm not used to that, is it enough to replace new Promise() by Promise.resolve() ?
Thanks
I believe the linter's problem is that you use a Promise inside a Promise constructor. Use an async function and move the Promise constructor one indentation lower:
const appendPromise = async (requestWithoutAuth: {
spreadsheetId: any
range: string
valueInputOption: string
insertDataOption: string
resource: { values: any[][] }
}): Promise<any> => {
const client = await getAuthorizedClient();
return new Promise((resolve, reject) => {
const sheets = google.sheets('v4')
const request = requestWithoutAuth
request.auth = client
return sheets.spreadsheets.values.append(request, (err: any, response: { data: unknown }) => {
if (err) {
console.log(`The API returned an error: ${err}`)
return reject(err)
}
return resolve(response.data)
})
})
}
I have the following controller action
[HttpPost]
[Route("api/Tenant/SetTenantActive")]
public async Task<IHttpActionResult> SetTenantActive(string tenantid)
{
var tenantStore = CosmosStoreFactory.CreateForEntity<Tenant>();
var allTenants = await tenantStore.Query().Where(x => x.TenantDomainUrl != null).ToListAsync();
foreach(Tenant ten in allTenants)
{
ten.Active = false;
await tenantStore.UpdateAsync(ten);
}
var tenant = await tenantStore.Query().FirstOrDefaultAsync(x => x.Id == tenantid);
if (tenant == null)
{
return NotFound();
}
tenant.Active = true;
var result = await tenantStore.UpdateAsync(tenant);
return Ok(result);
}
And my react code:
import React, { Component } from 'react';
import { Table, Radio} from 'antd';
import { adalApiFetch } from '../../adalConfig';
import Notification from '../../components/notification';
class ListTenants extends Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
fetchData = () => {
adalApiFetch(fetch, "/Tenant", {})
.then(response => response.json())
.then(responseJson => {
if (!this.isCancelled) {
const results= responseJson.map(row => ({
key: row.ClientId,
ClientId: row.ClientId,
ClientSecret: row.ClientSecret,
Id: row.Id,
SiteCollectionTestUrl: row.SiteCollectionTestUrl,
TenantDomainUrl: row.TenantDomainUrl
}))
this.setState({ data: results });
}
})
.catch(error => {
console.error(error);
});
};
componentDidMount(){
this.fetchData();
}
render() {
const columns = [
{
title: 'Client Id',
dataIndex: 'ClientId',
key: 'ClientId'
},
{
title: 'Site Collection TestUrl',
dataIndex: 'SiteCollectionTestUrl',
key: 'SiteCollectionTestUrl',
},
{
title: 'Tenant DomainUrl',
dataIndex: 'TenantDomainUrl',
key: 'TenantDomainUrl',
}
];
// rowSelection object indicates the need for row selection
const rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
if(selectedRows[0].key != undefined){
console.log(selectedRows[0].key);
const options = {
method: 'post',
body: {tenantid:selectedRows[0].key},
};
adalApiFetch(fetch, "/Tenant/SetTenantActive", options)
.then(response =>{
if(response.status === 200){
Notification(
'success',
'Tenant created',
''
);
}else{
throw "error";
}
})
.catch(error => {
Notification(
'error',
'Tenant not created',
error
);
console.error(error);
});
}
},
getCheckboxProps: record => ({
type: Radio
}),
};
return (
<Table rowSelection={rowSelection} columns={columns} dataSource={this.state.data} />
);
}
}
export default ListTenants;
focus only on the onchange event,
And the screenshot:
And it looks like the request gets to the webapi (I attached the debugger)
Update:
Basically If I dont put FromBody I need to send the parameter via querystring.
However if I put from Body and I send the parameter in the body, its received null on the webapi
Add [FromBody] before your input parameter in your action method like this:
public async Task<IHttpActionResult> SetTenantActive([FromBody] string tenantid)
Then, convert your selected row key into string
const options = {
method: 'post',
body: { tenantid : selectedRows[0].key.toString() }
};
Action "type": undefined, is what I keep getting returned when I try to test a redux action with fetch-mock. Any suggestion on how to resolve this issue? Could it be a bug in fetch-mock?
Expected value to equal:
[{"type": undefined}, {"result": {"hello": "world"}, "type": undefined}]
Received:
[{"type": "DASHBOARD_RESULT_LOADING"}, {"result": {"hello": "world"}, "type": "DASHBOARD_RESULT_READY"}]
dashboardActions.js
function resultReady(json) {
return {
type: DASHBOARD_RESULT_READY,
result: camelizeKeys(json)
};
}
export function requestPredict(params) {
let url = `${process.env.API_URL}/predict/`;
const requestParams = {
method: 'post',
credentials: 'include'
};
return async (dispatch) => {
return fetch(url, requestParams)
.then(response => {
if (response.status === 200) {
return response.json();
} else {
throw Error(response.statusText);
}
})
.then(data => dispatch(resultReady(data)));
};
}
dashboardActions.test.js
const mockData = {
"hello": "world"
}
describe('action creators', () => {
afterEach(() => {
fetchMock.reset()
})
it('should create DASHBOARD_RESULT_LOADING', () => {
fetchMock.post('*', {"hello":"world"} );
const expectedActions = [
{ type: actions.DASHBOARD_RESULT_LOADING },
{ type: actions.DASHBOARD_RESULT_READY, result: mockData }
]
const store = mockStore({ result: {}})
return store.dispatch(actions.requestPredict())
.then((data) => {
expect(store.getActions()).toEqual(expectedActions)
})
})
})
You're receiving types "DASHBOARD_RESULT_LOADING" and "DASHBOARD_RESULT_READY", which seems to be the intended behaviour. You're expecting actions.DASHBOARD_RESULT_LOADING and actions.DASHBOARD_RESULT_READY, neither of which seems to be defined with any value.
Either define actions.DASHBOARD_RESULT_LOADING and actions.DASHBOARD_RESULT_READY:
actions.DASHBOARD_RESULT_LOADING = 'DASHBOARD_RESULT_LOADING'
actions.DASHBOARD_RESULT_READY = 'DASHBOARD_RESULT_READY'
or replace them with your expected types:
const expectedActions = [
{
type: 'DASHBOARD_RESULT_LOADING'
},
{
type: 'DASHBOARD_RESULT_READY',
result: mockData
}
]
I'm using ionic-native SQLite database for Ionic application and for testing in browser i'm using WebSQL.
It's all working fine in browser, but when running application in android devices. it gives me error like Cannot read property 'transaction' of undefined.
Below is code for reference.
1) DBProvider.ts
import { Platform } from 'ionic-angular';
import { Injectable } from '#angular/core';
import { SQLite, SQLiteObject } from '#ionic-native/sqlite';
declare var window: any;
#Injectable()
export class DBProvider {
DB_NAME: string = 'DailySheet.db';
public websql = null;
public sqlite: SQLite;
sqliteobj: any;
public AppUsers = [];
constructor(public platform: Platform) {
if (this.platform.is('core')) {
this.websql = window.openDatabase(this.DB_NAME, "1.0", "Test DB", 2 * 1024 * 1024);
console.log('Database opened.');
this.createTable();
}
this.platform.ready().then(() => {
if (!this.platform.is('core')) {
this.sqlite.create({ name: this.DB_NAME, location: 'default' })
.then((db: SQLiteObject) => {
console.log('Database opened.');
this.sqliteobj = db;
this.createTable();
});
}
});
}
createTable() {
this.query(`CREATE TABLE IF NOT EXISTS AppUser (
UserId INTEGER NOT NULL,
MobileNo TEXT NOT NULL UNIQUE,
Email TEXT,
PRIMARY KEY(UserId)
)`)
.then(data => {
console.log('Table created.');
})
.catch(err => {
console.error('Unable to create initial storage tables', err.tx, err.err);
});
}
getAppUsers(): Promise<any> {
let query = 'SELECT * FROM AppUser';
return this.query(query)
.then(data => {
if (data.res.rows.length > 0) {
console.log('Rows found.');
return data.res.rows;
}
else {
console.log('No rows found.');
}
});
}
insertAppUser(): Promise<any> {
let id = 1;
let mobileno = '8905606191';
let email = 'niravparsana94#gmail.com';
return this.query('INSERT INTO AppUser (UserId, MobileNo, Email) VALUES (' + id + ' ,\"' + mobileno + '\" ,\"' + email + '\")')
.then(data => {
console.log('Insert success.');
return data;
})
.catch(err => {
console.error('Unable to insert', err.tx, err.err);
});
}
updateAppUser(UserId): Promise<any> {
let query = "UPDATE Todo SET Email=? WHERE UserId=?";
return this.query(query, ['niravparsana#outlook.com', UserId])
.then(data => {
console.log('AppUser Updated.');
return data;
})
.catch(err => {
console.error('Unable to update', err.tx, err.err);
});
}
deleteAppUser(UserId): Promise<any> {
let query = "DELETE FROM AppUser WHERE UserId=?";
return this.query(query, [UserId])
.then(data => {
return data;
})
.catch(err => {
console.error('Unable to delete', err.tx, err.err);
});
}
query(query: string, params: any[] = []): Promise<any> {
return new Promise((resolve, reject) => {
try {
if (this.platform.is('core')) {
this.websql.transaction((tx: any) => {
tx.executeSql(query, params,
(tx: any, res: any) => resolve({ tx: tx, res: res }),
(tx: any, err: any) => reject({ tx: tx, err: err }));
},
(err: any) => reject({ err: err }));
}
else {
this.sqliteobj.transaction((tx: any) => {
tx.executeSql(query, params,
(tx: any, res: any) => resolve({ tx: tx, res: res }),
(tx: any, err: any) => reject({ tx: tx, err: err }));
},
(err: any) => reject({ err: err }));
}
} catch (err) {
reject({ err: err });
}
});
}
}
2) home.ts
import { Component, OnInit } from '#angular/core';
import { NavController, Platform } from 'ionic-angular';
import { DBProvider } from '../../providers/DBProvider';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage implements OnInit {
AppUsers: Array<Object>;
constructor(public navCtrl: NavController, private platform: Platform, public db: DBProvider) {
}
ionViewDidLoad() {
this.deleteAppUser();
this.insertAppUser();
this.getAllAppUsers();
}
ngOnInit() {
}
public deleteAppUser() {
this.db.deleteAppUser(1)
.then(data => {
if (data.res.rowsAffected == 1) {
console.log('AppUser Deleted.');
}
else {
console.log('No AppUser Deleted.');
}
})
.catch(ex => {
console.log(ex);
});
}
public insertAppUser() {
this.db.insertAppUser()
.then(data => {
})
.catch(ex => {
console.log(ex);
});
}
public getAllAppUsers() {
this.db.getAppUsers()
.then(data => {
this.AppUsers = data;
})
.catch(ex => {
console.log(ex);
});
}
}
While debugging, I figured out somewhat that code runs in difference sequence in browser and mobile.
In browser
DBProvider constructor
this.CreateTable() function(DBProvider.ts)
this.deleteAppUser() function(home.ts)
this.insertAppUser() function(home.ts)
this.getAllAppUsers() function(home.ts)
In Android device
DBProvider constructor
this.deleteAppUser() function(home.ts)
this.insertAppUser() function(home.ts)
this.getAllAppUsers() function(home.ts)
this.CreateTable() function(DBProvider.ts)
As you can this.sqliteobj is assigned in DBProvider constructor. but while debug i found that funtions from home.ts are calling before this.sqliteobj get assigned, that's why it gives an error like Cannot read property 'transaction' of undefined. But then question is why functions from home.ts getting called before this.sqliteobj get assigned?
To my knowledge you need this.sqlite.create(... in every call on the sqlite database. So you have to include it in your query function before the sqliteobj.