Hello I find myself confused as to why my console log is coming back undefined in Angular. Im am trying to display data on a card in html. The plan was to wire whatever data was coming in and display it through cardValue
Service.ts
incomingData(): Observable<any> {
return this.httpClient.get<any>('this_is_incoming_data/', {});
}
Component.ts
cardDetails = [
{
cardValue: console.log('this is test 3, 'this.custom()),
}
returnedData: any;
ngOnInit(): any {
this.Service.incomingData().subscribe((data: any) => {
this.returnedData = data;
console.log('test1',this.returnedData);
this.custom();
});
}
custom(): any {
const placeholder = "hello"
return placeholder
}
cardValue: {Name: John, Size: Medium, Age: 34}
on the console.log for test 1-3 logs perfectly fine & as expected but when I change custom() to need the card value like so.
custom(): any {
const placeholder = this.returnedData.cardValue]
return placeholder
}
test 3 returns undefined and gives me an error
Uncaught (in promise): TypeError: Cannot read properties of undefined (reading 'cardValue')
I know that the issue is in custom function but I do not know what to change it to get it to work
Following code runs before ngOnInit. this.returnedData is set in ngOnInit, and hence it throws error for this.returnedData.cardValue as returnedData is undefined.
cardDetails = [
{
cardValue: console.log('this is test 3, 'this.custom()),
}
Change your custom code to as below. This will ensure if returnedData is not set, placeholder is empty otherwise card value of returned data.
const placeholder = this.returnedData?
this.returnedData.cardValue : '';
Issue of undefined is because of asyncronous thread of observeable please modify your custom() method by accepting an argument when data received
ngOnInit(): any {
this.Service.incomingData().subscribe((data: any) => {
this.returnedData = data;
this.custom(this.returnedData.cardValue);
});
}
custom(value): any {
const placeholder = value
return placeholder
}
Related
I have a Service TypeScript File with following Code
export class FirebaseService {
constructor(private afs: AngularFirestore, private storage: AngularFireStorage) {}
uploadFile(file: any) {
const filePath = 'path/to/save/file';
const fileRef = this.storage.ref(filePath);
const task = this.storage.upload(filePath, file);
return task.snapshotChanges().pipe(
finalize(() => {
return fileRef.getDownloadURL()
})
).toPromise();
}
createDocument(collection: string, name: string, file: any) {
this.uploadFile(file).then(downloadURL => {
if (downloadURL) {
const data = {
title: name,
downloadURL: downloadURL
};
this.afs.collection(collection).add(data);
} else {
console.log("downloadURL is not defined.");
}
});
}
}
In my Component I have following code
export class CreatePage implements OnInit {
name = ""
file: any;
onFileChanged(event: any) {
this.file = event.target.files[0];
}
onSubmit() {
console.log("this.selectedOption = ",JSON.stringify(this.selectedOption))
console.log("this.name = ",JSON.stringify(this.name,))
console.log("this.file = ",JSON.stringify(this.file))
console.log("TEST", this.file)
this.firebaseService.createDocument(this.selectedOption, this.name, this.file)
this.name = "", this.selectedOption = ""
}
}
The output of the 4 values from console log is:
Selected option: freunde
this.selectedOption = "freunde"
this.name = "test"
this.file = {}
TEST File {name: 'berge.jpg', lastModified: 1673618629595, lastModifiedDate: Fri Jan 13 2023 15:03:49 GMT+0100 (Mitteleuropäische Normalzeit), webkitRelativePath: '', size: 990092, …}
Selected option:
The HTML looks like this:
<ion-item> <input type="file" (change)="onFileChanged($event)"> </ion-item>
How can I fix this problem? The error message says following:
core.mjs:9095 ERROR Error: Uncaught (in promise): FirebaseError: [code=invalid-argument]: Function addDoc() called with invalid data. Unsupported field value: undefined (found in field downloadURL.metadata.cacheControl in document freunde/lcAwFcHvQSo5iV6ExQPi)
It will upload the image to the storage, but not the downloaded url in the firebase firestore. Can someone help me please?
The Object look like this after printing the data object
data = {
"title": "test",
"downloadURL": {
"source": {
"source": {
"source": {}
}
}
}
}
Debugging step 1.
Make your code show exactly what you are sending to Firebase. Change the console.log to three console.logs
console.log("this.selectedOption = ",JSON.stringify(this.selectedOption,null,2))
console.log("this.name = ",JSON.stringify(this.name,null,2))
console.log("this.file = ",JSON.stringify(this.file,null,2))
Also add this before your call to add:
Before this:
this.afs.collection(collection).add(data);
Add this:
console.log("data = ", JSON.stringify(data, null, 2))
Show in your question the exact output from the above
This will maximise our chances of finding the problem.
Why JSON.stringify?
I can see by your comment that you are annoyed by my suggestion to use JSON.stringify. The reason to use it is to force the console.log output to be the instantaneous value of the variable at that time, rather than an automatically-updating value that might display a different value on the console than the value being experienced by your program at the time of the error.
You can now see the utility of the JSON.stringify!
Your simple console.log(this.file) is reporting the full value with properties filled in.
But the JSON stringify, is showing you that, at the time that the line was run, this.file was simply {}, i.e. an empty object. Firebase was being sent {}, not the filled-in object.
You can also see the value of the ,null,2
This would have prevented the truncation of a line at:
size: 990092, ...
Volunteers on Stack Overflow would therefore have been able to tell if there was something later on in the object that was undefined or in some other way conflicting with Firebase.
These debugging tips are there to help us help you. If you don't follow the advice we give, it lessens people's enthusiasm to help.
I have now fixed my problem with following code.
I changed the uploadFile Function to following:
uploadFile(file: any) {
const filePath = 'images/file';
const fileRef = this.storage.ref(filePath);
const task = this.storage.upload(filePath, file);
return task.snapshotChanges().toPromise().then(() => {
return fileRef.getDownloadURL().toPromise()
});
}
Instead of returning a object I return now a Promise. This way we are waiting for the promise to resolve and getting the downloadURL. It should resolve the issue and allow the createDocument method to access the download URL without any error.
I have mocha, chai and sinon Javascript testing framework and library are availabe on an application I inherited. I am new to all ^ of them and I've been reading their APIs to learn how to use them properly.
Here is Javascript object I want to validate. If expected name property is missing, validator would throw UsageError: name property is missing.
// person object
const person = { name: 'peter' }
// validate method of exported validator JS component
async validate(person) {
const { name } = person;
const promises = [];
if (typeof name !== object) { throw new UsageError('name property is missing'); }
...
else { promises.push(fooService(name)); }
try {
await Promise.all(promises);
} catch (error) { throw (error); }
}
// unit test in sinon
describe('Validate person', async function() {
it('should throw error not find name property', async function() {
const person = { 'foo': '1234 somewhere' };
try {
await validator.validate(person);
} catch(error) {
sinon.assert.match(error, 'name property is missing');
}
});
This is async + await code so little I know sinon would be a fit then when I executed the unit test, I am even puzzled with following error msg:
AssertError: expected value to match
expected = UsageError: name property is missing
actual = UsageError: name property is missing
Both actual and expected come out same in string I think but I don't understand why I got AssertError. I'd appreciate if someone can explain to me what I am doing wrong and guide me the right way to implement this unit test. Ty!
[update]
My apology, I realized that I gave a wrong example within the sample test I have posted. I corrected it by having person object does not contain name property.
it is failed because the test compares the reference. The code creates new reference by using new statement e.g new UsageError.
An example:
let expect = require('chai').expect;
it('checks equality' ,function() {
const actual = new Error('name property is missing');
const expected = new Error('name property is missing');
expect(actual).to.equal(expected);
})
The code above will give output
// output
AssertionError: expected [Error: name property is missing] to equal [Error: name property is missing]
+ expected - actual
The solution maybe you could compare the message by accessing message property
it('checks equality' ,function() {
...
expect(actual.message).to.equal(expected.message);
})
Hope it helps
In React Docs - AJAX and APIs there is an example where a JSON object is created with a key that has no value.
I believe I am missing some fundamental understanding of JavaScript objects. What value is given to the error key in the following snippet and how does this value get there?
(error) => {
this.setState({
isLoaded: true,
error
})
}
Later, when the state is rendered, the value of error is assumed to have some message property. I have run the example code and it clearly works, but I am stuck trying to explain to another person how it works exactly.
if(error) {
return <div>Error: {error.message}</div>
It's object property shorthand ,
basically if you have a variable with the same name as the key, you can do :
const key = "someValue";
const obj = { key };
// instead of
const obj = { key : key };
const name = "John";
const age = 30;
const obj = {
name,
age
}
console.log(obj);
In the example you provided, error is an object having message inside it, something like :
const error = {
message: "some message",
// ...
}
I am very new to typescript/ionic 4. I am trying to access data stored in firebase and use it in my typescript file. when in .subscribe I can display the data as requested. but this is not what I am looking for. I need to perform the calculation outside of .subscribe on my page.ts .
I have seen many similar issues, but I cannot seem to get a solution.
Here is my Typescript services file
export interface Place{
title: string;
type: string;
latitude: number;
longitude: number;
}
export class PlaceService {
placess: Place[];
place: Place;
private placesCollection: AngularFirestoreCollection<Place>;
private places: Observable<Place[]>;
constructor(db: AngularFirestore) {
this.placesCollection = db.collection<Place>('places');
this.places = this.placesCollection.snapshotChanges().pipe(
map(actions =>{
return actions.map(a => {
const data = a.payload.doc.data();
const id = a.payload.doc.id;
return{ id, ...data};
});
})
);
}
getPlaces() {
return this.places;
}
}
and the relevant part in my page typescript
import { PlaceService, Place } from '../services/place.service';
places: Place[];
ngOnInit() {
this.placeService.getPlaces()
.subscribe(res =>{
this.places = res;
console.log(this.places[0].title);//WORKS
});
console.log(this.places[0].title);//FAILED
}
I get the following error message:
MapPage_Host.ngfactory.js? [sm]:1 ERROR TypeError: Cannot read property '0' of undefined
Your problem is that your code works as you wrote it. When the page initializes the ngOnInit is called. Inside the code goes to the first element (this.placeService.getPlaces() ... ) and immediately goes to the seconde element (console.log(this.places[0]). This throws the error, because the places variable is not yet set from your call to the placeService and is currently undefined.
ngOnInit() {
this.placeService.getPlaces() // called first
.subscribe(res =>{
this.places = res;
console.log(this.places[0].title);
});
console.log(this.places[0].title); // called second (undefined error)
}
If you call a function after you set the places variable, the second console.log() will work.
ngOnInit() {
this.placeService.getPlaces()
.subscribe(res =>{
this.places = res;
console.log(this.places[0].title);
this.showFirstTitle(); // this.places is set
});
}
showFirstTitle() {
console.log(this.places[0].title); // will work
}
.subscribe method has to complete( ajax request has to be 200-OK), inside subscribe method you can store into your local variables, Then further modifications are possible.
you can not use a variable which has no data.
this.placeService.getPlaces()
.subscribe(res =>{
this.places = res;
});
will take some seconds to complete the ajax call and fetch the response and storing in "Places".
workaround(not recommended) use set timeout function wait for at least 2 sec. increment seconds until you find a minimal seconds that request and response completed.
then you can do some calculations on this.places.
I am new to all JavaScript and angular. so I am struggling to do the following:
I have the following service, to read X from a local JSON file. The X is what user select from a dropdownbox:
getBySector(sector){
this.http.get('../../assets/Sectors.json').map(res => res).subscribe
(res => {
this.SectorsArray = res as ISectors[];
this.SectorsArray= res.find(item=>item.Sector===sector);
console.log(this.industrySectorsArray);
return this.industrySectorsArray;
},
(err: HttpErrorResponse) => {
console.log (err.message);
}
)
}
as an additional note, I have an interface which is ISector and matches the JSOn file.
The above code give me in Console the exact thing I expect. which is the following:
{IndustrySector: "Households", isSelected: "false", dataSubjectCategories: Array(2), dataTypeCategories: "Data", SubIndustries: Array(2)}
HOW can I return the above object/json output to ms TS file where I have called the service?
I have done the followings which are failed:
//even this failed:
console.log(this.readjsonService.getBySector(mission));
//
var output:Isector;
output=this.readjsonService.getBySector(mission)
// cannot subscribe to it as well
BTW, the find gives me the following error:
error TS2339: Property 'find' does not exist on type 'Object'.
UPDATE:
I solved the issue the code had with the help of people who replied. But the code got another error, although it works fine. t says:
"Cannot read property 'dataSubjectCategories' of undefined"
dataSubjectCategories is one of the key in the ISector: here is the ISector:
export interface ISectors {
IndustrySector: string;
isSelected: string;
dataSubjectCategories:string[];
dataTypeCategories:string[];
SubIndustries:[{
IndustrySector: string;
isSelected: string;
dataSubjectCategories:string[];
dataTypeCategories:string[];
SubIndustries:[{}]
}]
}
Please help to resolve this. Thanks a lot.
Normally, your service should just be returning the Observable and should not include the subscribe. Best practice suggests that you subscribe as close to the UI as possible.
My service methods look like this:
getProducts(): Observable<IProduct[]> {
return this.http.get<IProduct[]>(this.productUrl).pipe(
tap(data => console.log('All: ' + JSON.stringify(data))),
catchError(this.handleError)
);
}
getProduct(id: number): Observable<IProduct | undefined> {
return this.getProducts().pipe(
map((products: IProduct[]) => products.find(p => p.productId === id))
);
}
Using the generic parameter on the get: get<IProduct[]> helps Angular automatically map the returned response to an array of data, ISectors in your example.
The calling code in the component then looks like this:
getProduct(id: number) {
this.productService.getProduct(id).subscribe(
product => this.product = product,
error => this.errorMessage = <any>error);
}
Notice that here is where we subscribe. It then gets the product in the first function passed to the subscribe method.
You can find the complete example here: https://github.com/DeborahK/Angular-GettingStarted/tree/master/APM-Final