I'm hooking to the creation of objects on a specific collection (orders)
I need to add a new property to the object before it's saved, not returning anything, to no avail.
I have looked at the documentation at https://firebase.google.com/docs/reference/functions/functions.firestore.DocumentBuilder#writing_data
but it's for onUpdate so it doesn't work as i intend it.
exports.createOrder = firestore.document('orders/{orderId}').onCreate((snap, context) => {
const newOrder = snap.data()
console.log('triggered', newOrder)
const orderId = randomize('A0', 10)
console.log({ orderId })
return newOrder.ref.set({ orderId }, { merge: true })
//newOrder.ref.set is undefined
return newOrder.set({ orderId }, { merge: true })
//newOrder.set is undefined
})
snap.data() returns a raw JavaScript object whose properties contain the values of the fields in the document. It does not contain a property called ref (unless you had a document field also called ref).
If you need to write back to the document that changed, use the DocumentReference type object provided in snap.ref. See also the API documentation for the DocumentSnapshot type object passed to the function.
snap.ref.set(...)
Related
So, basically i have two api calls in two different functions that are called from a single component page. The await function is applied to each of them because each of the response provided from the first api is used as params to the second api. for the first api call, inside the function, the response is pushed to the array named all_countries.
const apiCall = getCountry(req)
.then((response) => {
all_countries.push(...response);
dispatch(actionSuccess(NICE_ONE, response));
})
.catch((error) => {
dispatch(actionFailure(NICE_ONE, error));
});
each of the array objects inside all_countries is to be mapped and value(i.e country code present inside each object from all_countries) is used to call another api to get the response but my problem is upon console logging the all_countries from the second function, it shows the symbol of empty array. upon opening the array, it lists all the countries. when i console log the length of the array, it shows 0 and upon console logging the elements like all_countries[0], it shows undefined. Why is this happening? Could someone help me out please?
This gets shown on console
Upon expanding, this is shown:
enter image description here
So, i found that on adding all_countries.push, intellij shows me a warning, response is not an array type but rather AllCountryRes type.
AllRelaysRes type is defined as:
export type AllRelaysRes = {
logo: string;
ipv4: string;
ipv6: string;
port: string;
location: {
id: number;
ip: string
};
};
and the getCountry function is like this:
export const getCountry = (url: string): Promise<AllRelaysRes> => {
ExternalApi.setBaseUrl(url);
return ExternalApi.apiCall<AllRelaysRes>({
method: "GET",
url: "something",
});
};
The response from the api is an array of objects. I am just a beginner with typescript and trying out stuffs. My question is how do i make the response type as an array of object AllRelayRes? Doesnot return ExternalApi.apiCall angular brackets AllRelayRes mean that the response expected is an array of AllRelayRes since, AllRelayRes is kept inside the angular brackets? How do i fix this? Is this the real problem why i get the array length 0?
I believe you're dealing with a collection of objects of type AllRelayRes, not an array. It shows as [] in the console, but that represents a collection.
If you want to use it as an array, specify the type in your function <AllRelaysRes[]>
export const getCountry = (url: string): Promise<AllRelaysRes[]> => {
ExternalApi.setBaseUrl(url);
return ExternalApi.apiCall<AllRelaysRes[]>({
method: "GET",
url: "something",
});
};
Then you should be able to manipulate the data in an array format
If you want to keep it as a collection, you can probably iterate through them with
let x, key, obj
for(x in AllRelayRes) {
key = x; // 0, 1, 2 .. the index
obj = AllRelayRex[x] // the object
// country_name = AllRelayRex[x].country_name, etc
}
When I use return await Model.save() they return a lot of data. There is a way to return the document only?
Code:
async (data) =>{
const charData = new MyModel({
steamId: data.steam,
phone: data.phone,
registration: data.registration,
})
return await MyModel.save()
}
I've searched in many websites but I didnt find any example using async functions.
There is a example that MyModel.save() are returning:
So, I want get only _doc object instead it all when return await MyModel.save()
Document.prototype.toObject() converts a mongoose document into the plain javascript object representation.
async (data) =>{
const charData = new MyModel({
steamId: data.steam,
phone: data.phone,
registration: data.registration,
})
await charData.save()
return charData.toObject({ getters: true })
}
Note the options which affect how the document is represented:
getters apply all getters (path and virtual getters), defaults to false
aliases apply all aliases if virtuals=true, defaults to true
virtuals apply virtual getters (can override getters option), defaults to false
minimize remove empty objects, defaults to true
transform a transform function to apply to the resulting document before returning
depopulate depopulate any populated paths, replacing them with their original refs, defaults to false
versionKey whether to include the version key, defaults to true
flattenMaps convert Maps to POJOs. Useful if you want to JSON.stringify() the result of toObject(), defaults to false
useProjection set to true to omit fields that are excluded in this document's projection. Unless you specified a projection, this
will omit any field that has select: false in the schema.
Then try like this
let res = await Model.save();
return res._doc;
bellow the class i initialize an object of type Photo, which photo is an interface with some attributes. And then i'm trying to use array filter to filter photos.
The filter return a copy of the photos array but i specify which photo do i want so i will have an array with 1 index inside. And then, i try to set a property inside the object to false, and the "Cannot set property 'ismain' of undefined in angular" occurs. Any ideas why?
Here is my code.
setMainPhoto(photo: Photo) {
this.userService
.setMainPhoto(this.authService.decodedToken.nameid, photo.id)
.subscribe(
() => {
// We use the array filter method to filter the photos apart from the main photo
// Filter returns a copy of the photos array. Filters out anything that it doesn`t match in the p
this.currentMain = this.photos.filter(p => p.ismain === true)[0];
this.currentMain.ismain = false;
photo.ismain = true;
this.alertify.success('Successfully set to profile picture');
},
error => {
this.alertify.error('Photo could not be set as profile picture');
}
);
}
In the above method the error occurs.
And below is my Photo interface.
export interface Photo {
id: number;
url: string;
dateadded: Date;
ismain: boolean;
description: string;
}
ERROR
UPDATE
PROBLEM SOLVED
The response from the server needs to match with the object attributes. So i had to change the property interface to match the JSON object coming back from the server.
Try this
setMainPhoto(photo: Photo) {
this.userService
.setMainPhoto(this.authService.decodedToken.nameid, photo.id)
.subscribe(
() => {
// We use the array filter method to filter the photos apart from the main photo
// Filter returns a copy of the photos array. Filters out anything that it doesn`t match in the p
this.currentMain = this.photos.filter(p => p.ismain === true)[0];
if (this.currentMain) {
this.currentMain.ismain = false;
photo.ismain = true;
this.alertify.success('Successfully set to profile picture'); }
},
error => {
this.alertify.error('Photo could not be set as profile picture');
}
);
}
If this works, then the filter does not return anything.
Interesting, I've had this come up before too. Typescript is quite strict with capitalization and if you don't match the json object exactly, it won't work, create a new property, etc.
I've found it helpful to take a look at the json return before building my typescript model, to make sure I'm matching the object being sent.
I'm having an issue trying to access the properties of an object, which has a reference property of another object. In other words, an object contained in other object.
After fetching the data from API calls like this one for example:https://api.apixu.com/v1/current.json?key=2352219608ed457fb3a12903193008&q=Helsinki my code get the response's data and set the hook's value: capitalWeather.
Anyways, the object has two attributes: location and current, which are also object type. Everytime I try to access any value from a reference, like accessing value of capitalWeather.location.region what I get is=
Uncaught TypeError: Cannot read property 'region' of undefined
As expected, It also applies for any of the other attributes: name,country,lat,lon,tz_id,etc....
I don't understand why I'm getting a typeError, considering both are object types.
This is the code snippet where the error takes place:
const Displayer = ({ country }) => {
const [capitalWeather,setWeather]=useState([]);
useEffect(() => {
axios.get(`https://api.apixu.com/v1/current.json?key=2352219608ed457fb3a12903193008&q=${country.capital}`)
.then(response => {
console.log('Promise fullfiled, succesfully fetched data');
setWeather(response.data);
})
}, [country.capital]);
console.log(capitalWeather.location.region);
return (<div className='displayer'>
<h2>{country.name}</h2>
<p>Capital {country.capital}</p>
<p>Population {country.population}</p>
<h4>Languages</h4>
<ul>
{country.languages.map(lang => <li key={lang.name}>{lang.name}</li>)}
</ul>
<img src={country.flag} alt='this is the country flag'></img>
{/*<Weather weather={capitalWeather}/>*/}
</div>)
}
capitalWeather is initialized as an array in this code.
const [capitalWeather,setWeather]=useState([]);
capitalWeather.location.region for sure will throw an error because it does not have a property location.
From the Firebase note:
Given a single key path like alanisawesome, updateChildren() only updates data at the first child level, and any data passed in beyond the first child level is a treated as a setValue() operation. Multi-path behavior allows longer paths (like alanisawesome/nickname) to be used without overwriting data. This is why the first example differs from the second example.
I am trying to use a single function createOrUpdateData(object) in my code. In case of update, it updates first level children properly, but if I have nested object passed, then it deletes all other properties of that nested object.
Here's the code:
function saveUserDetails(email,object){
var hashedEmail = Utilities.getHashCode(email);
var userRef = ref.child(hashedEmail);
return $q(function(resolve,reject){
return userRef.update(object, function(error){
if(error){
reject(error);
}else{
resolve("Updated successfully!");
}
});
});
}
So if I pass:
{
name: 'Rohan Dalvi',
externalLinks: {
website: 'mywebsite'
}
}
Then it will delete other properties inside externalLinks object. Is there a cleaner and simpler way of avoiding this?
In short, how do I make sure nested objects are only updated and that data is not deleted.
You can use multi-path updates.
var userRef = ref.child(hashedEmail);
var updateObject = {
name: 'Rohan Dalvi',
"externalLinks/website": 'mywebsite'
};
userRef.update(updateObject);
By using the "externalLinks/website" syntax in the object literal it will treat the nested path as an update and not a set for the nested object. This keeps nested data from being deleted.
This question provides a more recent solution that works with cloud firestore.
Rather than using "/", one may use "." instead:
var userRef = ref.child(hashedEmail);
var updateObject = {
name: 'Rohan Dalvi',
"externalLinks.website": 'mywebsite'
};
userRef.update(updateObject);
To update nested object/map/dictionary in firebase database, you can use Firestore.Encoder to class/struct that is Codable.
Here is a Swift code example:
Models:
import FirebaseFirestore
import FirebaseFirestoreSwift
// UserDetails Model
class UserDetailsModel: Codable {
let name: String,
externalLinks: ExternalLinkModel
}
// UserDetails Model
class ExternalLinkModel: Codable {
let website: String
}
Calling Firebase:
import FirebaseFirestore
import FirebaseFirestoreSwift
let firestoreEncoder = Firestore.Encoder()
let fields: [String: Any] = [
// using firestore encoder to convert object to firebase map
"externalLinks": try! firestoreEncoder.encode(externalLinkModel)
]
db.collection(path)
.document(userId)
.updateData(fields, completion: { error in
...
})