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.
Related
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
}
I would like to display users' data, but I have a problem with displaying the correct profile pictures. If a user does not have a profile picture, I get "undefined" in the console. If one user has a profile picture, then the same picture will be displayed for all the users. I need help finding the error in my code.
export interface UserData {
id: number,
name: string
}
export interface UserWithImage extends UserData{
image?: string
}
export interface UserProfileImage {
id: number,
url: string
}
After I get the necessary data from the services, I try to push the profile image into the userData.
user-data.ts
userData: UserWithImage[];
userProfiles: UserProfileImage[];
userProfileImage: UserProfileImage[];
getUserData() {
this.userData = this.userService.getData();
this.userProfiles = await this.imagesService.getProfilePicture(this.userData?.map(u => u.id));
this.userProfileImage = this.userProfiles.filter(u => u.url);
this.userData?.forEach((data, i) => {
data.image = this.userProfileImage[i].url;
});
}
images.service.ts
public async getProfilePicture(ids: number[]): Promise<UserProfileImage[]> {
const toLoad = ids.filter(id => !this.userProfileImages.find(up => up.id === id)).map(u => u);
if (toLoad || toLoad.length) {
const loaded = (await firstValueFrom(this.httpClient.post<UserProfile[]>
(this.imgService.getServiceUrl(customersScope, `${basePath}settings/users/profil`), JSON.stringify(toLoad), {headers}))).map(sp => {
return {
id: sp.userId,
url: sp.profilepicId ? this.imgService.getServiceUrl(customersScope,
`${basePath}web/download/profilepic/${sp.profilepicId}/users/${sp.userId}`, true) : ''
} as UserProfileImage
});
this.userProfileImages = [...loaded, ...this.userProfileImages];
}
return this.userProfileImages;
}
user-data.html
<div ngFor="data of userData">
<etc-profil [name]="data.name" [image]="data.image"></etc-profil>
</div>
this.userData = this.userService.getData();
Is this an async function (i.e. are you missing an await)?
this.userProfiles = await this.imagesService.getProfilePicture(this.userData?.map(u => u.id));
This line would fail is this.userData is a promise. this.userProfiles would be undefined due to the use of optional chaining (?.)
this.userProfileImage = this.userProfiles.filter(u => u.url);
This line appears to do nothing, the filter predicate is saying that anything with a url property that is not null or undefined is included, but the interface says that url is non-optional and doesn't support null or undefined.
this.userData?.forEach((data, i) => {
data.image = this.userProfileImage[i].url;
});
Again, if this.userData is a promise, this will do nothing due to the optional chaining.
If it does run, its assumed that there is a one-to-one relationship between users and profile images (index count and order must be the same).
I didn't consider the implementation of getProfilePicture because I think these issues need resolving first.
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
I am trying to read data from json file but I have some trouble.
How can I get items from a json file to individual items?
My json file:
[
{
"UserName": "test#test.en",
"Password": "tests123"
}
]
My method:
element(by.name('username')).sendKeys(browser.params.UserName);
element(by.name('password')).sendKeys(browser.params.Password);
as a result i get
Failed: each key must be a number of string; got undefined
You are passing an array of object and not an object, thus, you have to be precise in your variable.
Either directly pass an object
{
"UserName": "test#test.en",
"Password": "tests123"
}
Or specify the index in the array
element(by.name('username')).sendKeys(browser.params[0].UserName);
element(by.name('password')).sendKeys(browser.params[0].Password);
My Test was also failing with json file then i converted my datafile into ts file like below
export const DataForSearch =
{
Login:
{
CorrectCreds: { username: 'test#test.en', password: 'tests123' }
}
};
then use this in my test case like
import {DataForSearch } from "../DataLogin"
const using = require("jasmine-data-provider");
describe("Login Page", () => {
using(DataForSearch.Login, (data: any, alldesc: any) => {
it("Login", () => {
element(by.name('username')).sendKeys(data.username);
element(by.name('password')).sendKeys(data.password);
})
})
})
you can try typescript file, if you still facing issue.If you face any issue let me know
I need to use masterKey inside my angular2 app, but I can't pass it to initialize function and I can't google out why.
From package.json: "parse": "~1.9.2".
Initialization:
import {Parse} from '~/node_modules/parse/dist/parse';
#Injectable()
export class TFCloudService {
constructor() {
this.parse = Parse;
Parse.initialize(appConfig.parse.appId, null, appConfig.parse.masterKey);
Parse.serverURL = appConfig.parse.clientServerUrl;
Parse.liveQueryServerURL = appConfig.parse.liveQueryServerURL;
}
}
Error source:
this.edittedUser.save(null, {useMasterKey: true})
.then((user) => {
console.log(user);
});
Error text:
Error: Cannot use the Master Key, it has not been provided.
appConfig.parse.masterKey works fine, I checked that line with hard-coded key too, but got the same result.
Actually guessed the right way to pass that key:
Parse.initialize(appConfig.parse.appId);
Parse.masterKey = appConfig.parse.masterKey;