Retrieve documentId from future - function

I have not been able to find an answer to my problem so I will ask here.
I created a document that contains one piece of data. Once that document is created I need to retrieve the id of that document so I can use that string to add to another document that will be created next. The code is below where I call the Future function "saveNewAgency" as well as the code for the Future function.
if (globals.newAgency == true) {
firestoreService.saveNewAgency(newAgency);
agentProvider.saveAgent();
globals.newAgency = false;
} else {
firestoreService.saveAgency(newAgency);
}
Future<String> saveNewAgency(Agency agency) async {
DocumentReference docRef = await _db
.collection('agency')
.add(agency.toMap())
.then((value) => value.id);
//globals.agencyId = docRef.id;
}
As you can see I tried to set a global variable inorder to get the documentId but that code never gets executed so I commented it out.
This is a small thing but it is just another step I have reached in my journey of learning flutter and firebase.
Thanks

String Id = _db .collection('agency').doc().id;
Future<String> saveNewAgency(Agency agency) async {
await _db
.collection('agency')
.doc(id)
.set(agency.toMap())
.then((value) => {
print(id);
});
}

Try this.
Future<String> saveNewAgency(Agency agency) async {
await _db
.collection('agency')
.add(agency.toMap())
.then((value) => {
globals.agencyId = value.id
});
}

Related

Save filereader result to variable for later use

I can't find simple answer, but my code is simple.
I tried something like that, but always when i try to console.log my testResult, then i always recieving null. How to save data from file correctly?
public getFile(
sourceFile: File
): string {
let testResult;
const file = sourceFile[0]
const fileReader = new FileReader();
fileReader.readAsText(file, "UTF-8")
fileReader.onloadend = (e) => {
testResult = fileReader.result.toString()
}
console.log(testResult)
return testResult
}
This problem is related to my other topics, main reason is i can't handle load json file, translate them and upload to user. If i can save this file outside onloadend, then i hope i can handle rest of them (other attempts failed, this one blocking me at beginning)
Your issue is quite classical and is related to the asynchronous operations. Function which you assign to the onloadend request is called only when loadend event fires, but the rest of code will not wait for that to happen and will continue execution. So console.log will be executed immediately and then return will actually return testResult while it is still empty.
Firstly, in order to understand what I just said, put the console.log(testResult) line inside of your onloadend handler:
fileReader.onloadend = (e) => {
testResult = fileReader.result.toString();
console.log(testResult);
}
At this point testResult is not empty and you may continue handling it inside this function. However, if you want your getFile method to be really reusable and want it to return the testResult and process it somewhere else, you need to wrap this method into a Promise, like this:
public getFile(
sourceFile: File
): Promise<string> {
return new Promise((resolve) => {
const file = sourceFile[0]
const fileReader = new FileReader();
fileReader.onloadend = (e) => {
const testResult = fileReader.result.toString();
resolve(testResult);
}
fileReader.readAsText(file, "UTF-8");
});
}
Now whereever you need a file you can use the yourInstance.getFile method as follows:
yourInstance.getFile().then(testResult => {
// do whatever you need here
console.log(testResult);
});
Or in the async/await way:
async function processResult() {
const testResult = await yourInstance.getFile();
// do whatever you need
console.log(testResult);
}
If you are now familiar with promises and/or async/await, please read more about here and here.

Access the common method from other component

Here I am using some method for frequently used methods in common method ts file. If I am going to access these method I got null values Please help me out.
CommonMethod.ts:
GetCategoryList(){
let Mylist = [];
this.auth.Get("Master/Category").then((user) => {
Mylist= user.json();
});
return Mylist;
}
My Another component:
I am trying to access common method ts file here. by below way.
import {CommonMethod} from './CommonMethod';
...
...
construtor(private com:CommonMethod){}
ngOninit(){
console.log(this.com.GetCategoryList());
}
this.auth.Get is going to be async in nature due to which the return MyList line will get called even before the callback to the then method is called and the data is arrived and set in MyList.
You can use the async await syntax to fix it:
async GetCategoryList() {
let Mylist = [];
const user = await this.auth.Get("Master/Category");
Mylist = user.json();
return Mylist;
}
You can then use it like this in your Component:
import {CommonMethod} from './CommonMethod';
...
...
construtor(private com: CommonMethod) {}
async ngOninit() {
const myList = await this.com.GetCategoryList();
console.log(myList);
}
PS: Make sure that CommonMethod is a service and is added to the providers array of your #NgModule
Should update your common method:
GetCategoryList(): Promise<any>{
let Mylist = [];
return this.auth.Get("Master/Category").then((user) => {
Mylist= user.json();
Promise.resolve(Mylist);
});
}
And
ngOninit(){
this.com.GetCategoryList().then(results=>{
console.log(results);
});
}

Ava unbinds this for t.throws

AVA seems to unbind the instance methods 'this'.
class Person {
constructor(name) {
this.name = name;
}
sayMyName() {
const name = this.name;
return new Promise(function (resolve, reject) {
reject(new Error(name));
});
}
}
test('test', async (t) => {
const person1 = new Person('Bob');
const error = await t.throws(person1.sayMyName);
t.is(error.message, 'Bob');
});
For the above code, I get this:
85: const error = await t.throws(person1.sayMyName);
86: t.is(error.message, 'Bob');
87: });
Difference:
"CannBot read property \'name\' of undefinedb"
I've tried manually binding this promise like this:
person1.sayMyName.bind(person1), but this doesn't seem to work either.
t.throws() accepts a promise, so you just need to call your function:
const error = await t.throws(person1.sayMyName());
Bonus tip:
If you only need to check the error message, your assertion could be simplified to the following:
await t.throws(person1.sayMyName, 'Bob');
AVA seems to unbind the instance methods 'this'.
No, that's how this works in JavaScript. If you pass a class method, you need to bind it to its instance to preserve this. You might find my auto-bind module handy for this.

Map webservice data properly into typescript object (mapping Objects with capital letters / pascal case to camel case)

I realized a strange thing with my Angular2 typescript project. I have objects coming in from a webservice which have the type "Level" (it has the same properties as the Json coming from the webservice). In runtime comes out that the properties of the Level from the webservice have capital letters (Pascal case) at the beginning and the ones in my typescript project have small ones (visible in the browser's developer debug tool).
I guess I need to map the json properties somewhere somehow instead of doing a cast by writing "as Level[]" everywhere. How to I do it properly?
Update regarding the question that I should post some code:
(Controller)
ngOnInit(): void {
this.levelsObservable = this.levelsService.getAllLevels();
this.levelsObservable.subscribe(
data => console.log(data)
);
}
(Service)
observable : Observable<Response>;
getAllLevels(): Observable<Level[]> {
this.observable = this.achievementsService.getAllAchievements(this.allLevelsUrlPart);
return this.observable
.map((response: Response) => {
const srcData = response.json() as Level[];
return srcData;})
.catch(error => this.handleError(error));}
getAllAchievements(detailPath): Observable<Response> {
// prepare request url and header
this.specificUrl = this.webServiceUrl + detailPath;
this.headers.append('Content-type', 'application/json');
let options = new RequestOptions({ headers: this.headers });
this.result = this.http.get(this.specificUrl, options)
.catch(error => this.handleError(error));
return this.result;}
Update:
I polished my code a bit with the help of one answer below (not integrated above because not essential to solve the main problem).
I tried to use the other answer from below to reach the camel cases but it wasn't working (I have an array and in the array are objects with properties, but an object's properties aren't accessible with iterator methods).
Update:
I finally managed it (!) :) I shortened this post a bit and will now post my solution below. It's for sure not the most beautiful, but I'm happy to have one after searching around for hours. Thanks to all people helping me with their great and input here!
You could use this to get the lowercased objects.
modifiedSrc(srcData){
let obj = {};
Object.keys(srcData).forEach((key)=>{
obj[key.uncapitalize()] = srcData[key];
})
return obj
}
String.prototype.uncapitalize = function() {
return this.charAt(0).toLowerCase() + this.slice(1);
}
Then you can return the modified data
getAllLevels(): Observable<Level[]> {
this.observable = this.achievementsService.getAllAchievements(this.allLevelsUrlPart);
return this.observable
.map((response: Response) => {
const srcData = response.json() as Level[];
return this.modifiedSrc(srcData);})
.catch(error => this.handleError(error));}
You have complicated both of your methods.Make it simple as
this.webServiceUrl = "http...." ; // your service end point address
this.headers.append('Content-type', 'application/json');
let options = new RequestOptions({ headers: this.headers });
// For all your error handling
private handleError(error: Response) {
console.log(error);
return Observable.throw(error.json().error || 'Internal Server error');
}
Your service method can use TypeCasting which will look like
getAllLevels(detailPath): Observable<Level[]> {
return this.http.get(detailPath, options)
.map((response: Response) => <Level[]>response.json())
.catch(this.handleError);
}
Your component should raise the request to your service as
ngOnInit() : void{
this._myService.getAllLevels()
.subscribe(levels => this.levels = levels,
error =>this.errorMessage =<any> error);
}
Your variable declaration must be like
levels:Level[];
So finally... I got a solution! For sure not the most beautiful one, but easy to understand and done with hard work and research:
private useLevelProperties (response: any): Level[]{
let levels: Level[] = [];
Object.keys(response).forEach((key) => {
//create a new object and just take out the json parts needed. The webservice retrieves Pascal case letters, so we
//need to convert them into camel case ones.
this.level = new Level(response[key]["AchievementId"], response[key]["Image"],
response[key]["GrantedTo"], response[key]["GrantedBy"], response[key]["GrantedWhen"], response[key]["Description"],
response[key]["Name"], response[key]["CompetitionCode"], response[key]["Number"]);
levels[key] = this.level;
});
return levels;
};
Another option is to do it server-side with an extra json option for camel case: example for server-side camel casing

await doesn't seem to wait till the async call is finished

I have the async function which uses await calls and I thought that when you use await, it should pause the function execution until the value it received. For some reason, it doesn't work with me.
Here's my function (it is inside a class):
async userExistsInDB(email) {
let userExists;
await MongoClient.connect('mongodb://127.0.0.1:27017/notificator', async(err, db) => {
if (err) throw err;
let collection = db.collection('users');
userExists = await collection.find({email: email}).limit(1).count() > 0;
console.log("INSIDE:\n", userExists);
db.close();
});
console.log("OUTSIDE:\n", userExists);
return userExists;
}
And here's how I call it in another function inside the same class:
async getValidationErrors(formData) {
let userExists = await this.userExistsInDB(formData.email);
console.log("ANOTHER FUNC:\n", userExists);
}
So, I get the following output:
OUTSIDE:
undefined
ANOTHER FUNC:
undefined
INSIDE:
true
although the value INSIDE: true I expect to get printed the first.
Basically, what I need is to get the boolean value userExists from the userExistsInDB function and use it in other code.
What am I doing wrong here?
await only works with promises, so MongoClient.connect(…) would need to return a promise. Yet, you are using it as a callback API, and even with an async (promise-returning) callback function, which is not gonna work. Assuming mongo returns promises if you don't pass a callback, your code should look like
async function userExistsInDB(email) {
let db = await MongoClient.connect('mongodb://127.0.0.1:27017/notificator');
let collection = db.collection('users');
let userExists = (await collection.find({email: email}).limit(1).count()) > 0;
db.close();
return userExists;
}
though ideally you'd rather do
async function userExistsInDB(email) {
let db = await MongoClient.connect('mongodb://127.0.0.1:27017/notificator');
try {
let collection = db.collection('users');
let userCount = (await collection.find({email: email}).limit(1).count();
return userCount > 0;
} finally {
db.close();
}
}