React wait for function to finish? - html

I want to create a form where I type some data (barcode and name) and if an object with this data exists in my Products table I set states and save it in another Stocks table. But I have a problem when an objects doesn't exist. First I want to post it to a Products table and then to a target table. When I set product_id it is only visible in my saveProduct function and object is added to Stocks with id one down.
I tried also setting id in saveStocks() but it doesn't work because it is called before Products table is updated.
How to wait for it to finish? Or how to set id properly?
const saveProducts = () => {
var data = {
barcode: barcode,
name: name,
};
axiosInstance.post('/products/', data)
.then(response => {
setBarcode(response.data.barcode);
setName(response.data.name);
setProductId(response.data.product_id) //doesn't update in second function
})
.catch(e => {
console.log(e);
});
}
const saveStock = () => {
const temp = products.filter(product => product.barcode == barcode)
if (!temp.length) {
saveProducts()
getProducts()
setProductId(products[products.length-1].product_id) //called before saveProducts()
}
setTimeout(() => {
var data = {
stock_id: stock_id,
product_id: product_id,
count: count,
};
axiosInstance.post('/stocks/', data)
.then(response => {
setStockId(response.data.stock_id);
setProductId(response.data.product_id);
setCount(response.data.count);
})
.catch(e => {
console.log(e);
});
}, 2000)
}
I call saveStock() when clicking the button under my form:
<button className="btn btn-success" type="button" onClick={saveStock}>Add</button>

Why can't you have a separate button disable state which needs to be disabled untill saveProducts post response is back. Then you can enable it to use Add button. Then again, once you execute saveStock successfully, you can disable it again which disable the Add button.

You need to create a variable that you can await and then reference before running your second API call. For example:
let products = await saveProducts();
if (products) {
saveStock();
}

Related

(saved) event is getting triggered twice in HTML

I am working on ASP.Net project. My frontend page has 1 editable field.
When user edits data in the field, saved event trigger method. It is getting triggered twice so data entry is getting added twice in database.
.html -
<cool-inline-edit-field
name="homePhone"
[ngModel]="user.homePhone"
(saved)="saveUserHomePhoneAsync($event)"
required
pattern="[0-9]{10}"
>
</cool-inline-edit-field>
component.ts
saveUserHomePhoneAsyc($event: Event) {
this.showSpinner();
debugger;
const oldVal = this.user.homePhone;
const newVal = $event as unknown as keyof any;
debugger;
this.accountService.setBorrowerPreferencesChangeData(this.borrowerIdentityData.fullName, "HomePhone",
oldVal, newVal.toString()).subscribe(
(data) => {
this.hideSpinner();
this.user.homePhone = newVal.toString();
this.snackbarService.show("Value saved");
},
(error) => {
this.user.homePhone = oldVal;
this.hideSpinner();
this.showError('Failed to save home phone. Please try again');
console.log(error);
}
);
}
I am not getting why it is getting called twice? .ts method should run only once.

Firebase Updating User Data With Custom Fields After Creating User

I want to update the newly created User's data. The returned JSON is:
{
"user":{
"uid":"test123",
"displayName":null,
"photoURL":null,
"email":"test12#test.com",
"emailVerified":false,
"phoneNumber":null,
"isAnonymous":false,
"tenantId":null,
"providerData":[
{
"uid":"test12#test.com",
"displayName":null,
"photoURL":null,
"email":"test12#test.com",
"phoneNumber":null,
"providerId":"password"
}
],
"apiKey":"test123",
"appName":"[DEFAULT]",
"authDomain":"test123.firebaseapp.com",
"stsTokenManager":{
"apiKey":"test123",
"refreshToken":"test123",
"accessToken":"test123",
"expirationTime":1571238989357
},
"redirectEventId":null,
"lastLoginAt":"1571235389108",
"createdAt":"1571235389108"
},
"credential":null,
"additionalUserInfo":{
"providerId":"password",
"isNewUser":true
},
"operationType":"signIn"
}
This is my callout and update:
createUser = async (userData) => {
return await firebase.auth().createUserWithEmailAndPassword(userData.get('userName'), userData.get('password'))
.then((authData) => {
firebase.database().ref('users/' + authData.user.uid + '/').set({
fullName: userData.get('fullName'),
pictures: userData.get('pictures'),
phoneNumber: userData.get('phoneNumber')
});
})
};
Is it possible to add to the User table custom fields?
A few things are happening. It appears that userData can not be seen in the .then statement. So to solve this I attempted to pass in the userData JSON as a param. This did not work. I then broke out each value out of userData, saved it into a const and passed that value. This did not work.
I can see that userData has values in it before the .then statement. I am able to successfully create a new user with the right userName and password. This means to me either:
A - I am not passing the userData JSON correctly or
B - I am not allowed to pass data to firebase like I am doing
My end goal is to sign up a user and then take all of the data they input from a registration form (aka userData) and update the user table with it.
Articles I am using are:
https://firebase.google.com/docs/auth/web/manage-users
https://medium.com/mindorks/firebase-realtime-database-with-react-native-5f357c6ee13b
Main class that calls the createUser function:
const signUp = (dispatch) => {
return async (userData)=>{
try{
const response = await config.createUser(userData);
console.log('sign up resonse1: ' + response); //coming back as undefined
//todo:: figure out how to parse out the apikey out of response
await AsyncStorage.setItem('token', '123mockToken');
dispatch({type: 'sign_up', payload: '123mockToken'});
navigate('mainFlow');
} catch(e){
dispatch({type: 'add_error', payload: '' + e}); //we call dispatch anytime we want to update our state
}
}
};
I understand that the parameter userData holds all the data you want to use for creating the user ("all of the data they input from a registration form").
The following should work:
createUser = async userData => {
try {
const userCredential = await firebase
.auth()
.createUserWithEmailAndPassword(
userData.get('userName'),
userData.get('password')
);
const userId = userCredential.user.uid;
await firebase
.database()
.ref('users/' + userId + '/')
.set({
fullName: userData.get('fullName'),
pictures: userData.get('pictures'),
phoneNumber: userData.get('phoneNumber')
});
return userId; //As per your comment below
} catch (error) {
return error;
}
};
The createUserWithEmailAndPassword() method returns a UserCredential which contains a User.

Angular 4 - Fetch ID from one api and concatinate it with the url of another api

I'm working with Angular 4+.
I've added an API that fetches the company list. Against that company list, I'm fetching services that specific company booked.
For fetching the list of services that specific company booked I've created a function getCompanyServices() and called it in ngOnInit my API looks like this:
www.example.com/company-service/255 where 255 is the id of the company being fetched by another api.
If I simply show {{company.id}} in html view it displays the id but how do I concatenate that company.id in the api url of get company services?
Approaches I've tried:
Create a variable, company_id in my component.ts file assigned it data.company-id but the data doesn't parse.
Pass company id in the function where I'm calling my getCompanyServices() api but I'm unable to understand how to parse the data into it.
Code:
demo.service.ts (for hitting apis)
getCompanies() {
return this.http.get('http://example.com/api/companies', httpOptions);
}
getCompanyServices() {
return this.http.get('http://example.com/v1/company-service', httpOptions);
}
services.component.ts file
companies: any=[];
services: any=[];
ngOnInit() {
this.getCompanies();
this.getCompanyServices();
}
getCompanies() {
this._demoService.getCompanies().subscribe(
(response:any) => { console.log("Response =" , response);
this.companies = response.data;
},
err => { console.error(err)},
() => { console.log("ALL Companies FETCHED!") }
)
}
getCompanyServices() {
this._demoService.getCompanyServices().subscribe(
(response:any) => { console.log("Response =" , response);
this.services = response.data;},
err => { console.error(err)},
() => { console.log("Services FETCHED!") }
)
}
HTML file
<p>{{company.id}}</p>
<p>{{company.name}}</p>
<button class="sidebar-services">Services</p>
You may do the second call when the first will resolve to take all the companies list in your app then you can request the company specific service information.
In ngOninit
this.getcountries().subscribe(data=>{
// your stuff
this.getCompanyServices(id) // the company id that
you need
.subscribe(...
Getcompanyservice definition
`
getCompanyServices(id) {
this._demoService.getCompanyServices(id).subscribe(
(response:any) => { console.log("Response =" , response);
this.services = response.data;},
err => { console.error(err)},
() => { console.log("Services FETCHED!") }
)
}
`
In the service
`getCompanyServices(id) {
return this.http.get(`http://example.com/v1/company-service/${id}, httpOptions);
}`
Based on what I understand from your question, what you need is to get companyId and for each companyId get its services. You can achieve it by using flatMap as below:
this.subsc = this._demoService.getCompanies()
.pipe(flatMap((s) => {
this.companies.push(s);
return s.id;
}),
flatMap((id) =>{
return this._demoService.getCompanyServices(id).pipe(map((services) => {
return services;
}));
}))
.subscribe(res => {
this.services.push(res);
},
undefined,
() => console.log('complete'));
I mimicked demo here: https://stackblitz.com/edit/angular-xwsltm
You can use promises, fetch the data and do another call..
if im not mistaken you can do something like that
this.http.get('http://example.com/api/companies', httpOptions)
.timeout(15000)
.map(res => res.json()) // you might not need this line
.toPromise()
.then(compList => {
//here you have the company list
console.log('compList', compList); // check your developer tools to see the list
this.http.get('www.example.com/company-service/' + x, httpOptions) //where x should be substituted with the id of the desired company
}
If you dont want to change the implementation you already have then wrap each of the functions used with a Promise and just call the function and .then it
will become something like getCompanies().then(...
if you need me to explain more or something just comment..
Hope this helped

Sequelize update ignores invalid column names

When I try to update a certain entry in the db, sequelize - Model.update() ignores the wrong column name passed to it. Say my table has columns 'id' and 'password', if I pass an object that has 'id' and 'pwd' to the update function, the 'pwd' is simply ignored and 'id' is updated.
Is there a way, through sequelize, to check if an invalid column name is being passed to the update function?
You can do this by adding a custom instance function to your Model via prototype to be able to access this to pass up to this.update() if your checks pass.
const MyModel = sequelize.define(
'table_name',
{ ...columns },
{ ...options },
);
MyModel.prototype.validatedUpdate = async (updates, options) => {
// get a list of all the column names from the attributes, this will include VIRTUAL, but you could filter first.
const columnNames = Object.keys(MyModel.attributes);
// the keys from the updates we are trying to make
const updateNames = Object.keys(updates);
// check to see if each of the updates exists in the list of attributes
updateNames.forEach(updateName => {
// throw an Error if we can't find one.
if (!columNames.some((columnName) => columnName == updateName)) {
throw new Error(`The field ${updateName} does not exist.`);
}
});
// pass it along to the normal update() chain
return this.update(updates, options);
}
module.exports = MyModel;
Then use it like this:
try {
const myInstance = await MyModel.findById(someId);
await myInstance.validatedUpdate({ fake: 'field' });
} catch(err) {
console.log(err); // field does not exist
}

Create or Update Sequelize

I'm using Sequelize in my Nodejs project and I found a problem that I'm having a hard time to solve.
Basically I have a cron that gets an array of objects from a server than inserts it on my database as a object ( for this case, cartoons ). But if I already have one of the objects, I have to update it.
Basically I have a array of objects and a could use the BulkCreate() method. But as the Cron starts again, it doesn't solve it so I was needing some sort of update with an upsert true flag. And the main issue: I must have a callback that fires just once after all these creates or updates. Does anyone have an idea of how can I do that? Iterate over an array of object.. creating or updating it and then getting a single callback after?
Thanks for the attention
From the docs, you don't need to query where to perform the update once you have the object. Also, the use of promise should simplify callbacks:
Implementation
function upsert(values, condition) {
return Model
.findOne({ where: condition })
.then(function(obj) {
// update
if(obj)
return obj.update(values);
// insert
return Model.create(values);
})
}
Usage
upsert({ first_name: 'Taku' }, { id: 1234 }).then(function(result){
res.status(200).send({success: true});
});
Note
This operation is not atomic.
Creates 2 network calls.
which means it is advisable to re-think the approach and probably just update values in one network call and either:
Look at the value returned (i.e. rows_affected) and decide what to do.
Return success if update operation succeeds. This is because whether the resource exists is not within this service's responsibility.
You can use upsert
It's way easier.
Implementation details:
MySQL - Implemented as a single query INSERT values ON DUPLICATE KEY UPDATE values
PostgreSQL - Implemented as a temporary function with exception handling: INSERT EXCEPTION WHEN unique_constraint UPDATE
SQLite - Implemented as two queries INSERT; UPDATE. This means that the update is executed regardless of whether the row already
existed or not
MSSQL - Implemented as a single query using MERGE and WHEN (NOT) MATCHED THEN Note that SQLite returns undefined for created, no
matter if the row was created or updated. This is because SQLite
always runs INSERT OR IGNORE + UPDATE, in a single query, so there
is no way to know whether the row was inserted or not.
Update 07/2019 now with async/await
async function updateOrCreate (model, where, newItem) {
// First try to find the record
const foundItem = await model.findOne({where});
if (!foundItem) {
// Item not found, create a new one
const item = await model.create(newItem)
return {item, created: true};
}
// Found an item, update it
const item = await model.update(newItem, {where});
return {item, created: false};
}
I liked the idea of Ataik, but made it a little shorter:
function updateOrCreate (model, where, newItem) {
// First try to find the record
return model
.findOne({where: where})
.then(function (foundItem) {
if (!foundItem) {
// Item not found, create a new one
return model
.create(newItem)
.then(function (item) { return {item: item, created: true}; })
}
// Found an item, update it
return model
.update(newItem, {where: where})
.then(function (item) { return {item: item, created: false} }) ;
}
}
Usage:
updateOrCreate(models.NewsItem, {slug: 'sometitle1'}, {title: 'Hello World'})
.then(function(result) {
result.item; // the model
result.created; // bool, if a new item was created.
});
Optional: add error handling here, but I strongly recommend to chain all promises of one request and have one error handler at the end.
updateOrCreate(models.NewsItem, {slug: 'sometitle1'}, {title: 'Hello World'})
.then(..)
.catch(function(err){});
This might be an old question, but this is what I did:
var updateOrCreate = function (model, where, newItem, onCreate, onUpdate, onError) {
// First try to find the record
model.findOne({where: where}).then(function (foundItem) {
if (!foundItem) {
// Item not found, create a new one
model.create(newItem)
.then(onCreate)
.catch(onError);
} else {
// Found an item, update it
model.update(newItem, {where: where})
.then(onUpdate)
.catch(onError);
;
}
}).catch(onError);
}
updateOrCreate(
models.NewsItem, {title: 'sometitle1'}, {title: 'sometitle'},
function () {
console.log('created');
},
function () {
console.log('updated');
},
console.log);
User.upsert({ a: 'a', b: 'b', username: 'john' })
It will try to find record by hash in 1st param to update it, if it will not find it - then new record will be created
Here is example of usage in sequelize tests
it('works with upsert on id', function() {
return this.User.upsert({ id: 42, username: 'john' }).then(created => {
if (dialect === 'sqlite') {
expect(created).to.be.undefined;
} else {
expect(created).to.be.ok;
}
this.clock.tick(1000);
return this.User.upsert({ id: 42, username: 'doe' });
}).then(created => {
if (dialect === 'sqlite') {
expect(created).to.be.undefined;
} else {
expect(created).not.to.be.ok;
}
return this.User.findByPk(42);
}).then(user => {
expect(user.createdAt).to.be.ok;
expect(user.username).to.equal('doe');
expect(user.updatedAt).to.be.afterTime(user.createdAt);
});
});
Sound likes you want to wrap your Sequelize calls inside of an async.each.
This can be done with the custom event emitter.
Assuming your data is in a variable called data.
new Sequelize.Utils.CustomEventEmitter(function(emitter) {
if(data.id){
Model.update(data, {id: data.id })
.success(function(){
emitter.emit('success', data.id );
}).error(function(error){
emitter.emit('error', error );
});
} else {
Model.build(data).save().success(function(d){
emitter.emit('success', d.id );
}).error(function(error){
emitter.emit('error', error );
});
}
}).success(function(data_id){
// Your callback stuff here
}).error(function(error){
// error stuff here
}).run(); // kick off the queries
you can use findOrCreate and then update methods in sequelize. here is a sample with async.js
async.auto({
getInstance : function(cb) {
Model.findOrCreate({
attribute : value,
...
}).complete(function(err, result) {
if (err) {
cb(null, false);
} else {
cb(null, result);
}
});
},
updateInstance : ['getInstance', function(cb, result) {
if (!result || !result.getInstance) {
cb(null, false);
} else {
result.getInstance.updateAttributes({
attribute : value,
...
}, ['attribute', ...]).complete(function(err, result) {
if (err) {
cb(null, false);
} else {
cb(null, result);
}
});
}
}]
}, function(err, allResults) {
if (err || !allResults || !allResults.updateInstance) {
// job not done
} else {
// job done
});
});
Here is a simple example that either updates deviceID -> pushToken mapping or creates it:
var Promise = require('promise');
var PushToken = require("../models").PushToken;
var createOrUpdatePushToken = function (deviceID, pushToken) {
return new Promise(function (fulfill, reject) {
PushToken
.findOrCreate({
where: {
deviceID: deviceID
}, defaults: {
pushToken: pushToken
}
})
.spread(function (foundOrCreatedPushToken, created) {
if (created) {
fulfill(foundOrCreatedPushToken);
} else {
foundOrCreatedPushToken
.update({
pushToken: pushToken
})
.then(function (updatedPushToken) {
fulfill(updatedPushToken);
})
.catch(function (err) {
reject(err);
});
}
});
});
};
2022 update:
You can use the upsert function:
https://sequelize.org/api/v6/class/src/model.js~model#static-method-upsert
Insert or update a single row. An update will be executed if a row which matches the supplied values on either the primary key or a unique key is found. Note that the unique index must be defined in your sequelize model and not just in the table. Otherwise you may experience a unique constraint violation, because sequelize fails to identify the row that should be updated.
Implementation details:
MySQL - Implemented with ON DUPLICATE KEY UPDATE`
PostgreSQL - Implemented with ON CONFLICT DO UPDATE. If update data contains PK field, then PK is selected as the default conflict key.
Otherwise first unique constraint/index will be selected, which can satisfy conflict key requirements.
SQLite - Implemented with ON CONFLICT DO UPDATE
MSSQL - Implemented as a single query using MERGE and WHEN (NOT) MATCHED THEN
Note that Postgres/SQLite returns null for created, no matter if the row was created or updated