flutter firestore, add new object in array - firebase

I have array of objects, I want to add new object when user enter new data in the array?
Firestore.instance.collection(city).document('Attractions').updateData(
"data", FieldValue.arrayUnion(obj)
);
This shows error, How can I achieve this with flutter?

Right Format is :
Firestore.instance.collection(city).document('Attractions').updateData({"data": FieldValue.arrayUnion(obj)});
updateData Take Map<String,dynamic> as data.
In your Code you are having , as separator between key - value instead it should be :

#anmol.majhail 's is right, but to solve #Sami Ullah's problem, you must first make a list and add the object into the list like this:
var list = [objectBeingAdded];
Firestore.instance.collection('city').document('Attractions').updateData({"data": FieldValue.arrayUnion(list)});

Null safe code:
Say this is the data you want to add
Map<String, dynamic> someData = {
'foo': 1,
'bar': true,
};
Add the data with unique auto-generated ID:
var collection = FirebaseFirestore.instance.collection('collection');
collection
.add(someData)
.then((_) => print('Added'))
.catchError((error) => print('Add failed: $error'));
Add the data with your own ID:
var collection = FirebaseFirestore.instance.collection('collection');
collection
.doc('document_id') // <-- Document ID
.set(someData)
.then((_) => print('Added'))
.catchError((error) => print('Add failed: $error'));
Add the object to an array:
var collection = FirebaseFirestore.instance.collection('collection');
collection
.doc('document_id') // <-- Document ID
.set({'data': FieldValue.arrayUnion(list)}) // <-- Add data
.then((_) => print('Added'))
.catchError((error) => print('Add failed: $error'));

This is a working function I built that adds new Maps to an array in my Firestore Services class. I'm using Json Serializable to annotate all my model classes. userTemplateSections is a data field in my userTemplate firestore documents. I take userTemplate as a constructor of the 'addUserTemplateSection' function to make sure I'm editing the correct document.
I also added the function I made to delete Maps from a firestore document array.
'''
Future<void> addUserTemplateSection(
{UserTemplate userTemplate, String title, String summary}) async {
try {
final UserTemplateSection userTemplateSection =
UserTemplateSection(title: title, summary: summary);
await _firestore
.document(FirestorePath.userTemplate(uid, userTemplate.id))
.updateData(
{
'userTemplateSections':
FieldValue.arrayUnion([userTemplateSection.toJson()])
},
);
} catch (e) {
print(e);
}
}
'''
'''
Future<void> deleteUserTemplateSection({
UserTemplate userTemplate,
UserTemplateSection userTemplateSection,
}) async {
try {
await _firestore
.document(FirestorePath.userTemplate(uid, userTemplate.id))
.updateData(
{
'userTemplateSections':
FieldValue.arrayRemove([userTemplateSection.toJson()])
},
);
} catch (e) {
print(e);
}
}
'''

Related

How to create/update and retrieve data from nested objects in Cloud Firestore in flutter?

[![enter image description here][1]][1]I have a class that has one embedded array as well as a couple of objects. I can't figure out how to create/update and read to and from Cloud Firestore. I'm using flutter.
class ArtistModal {
final String artistName;
final String artistImage;
final String role;
ArtistModal({
required this.artistName,
required this.artistImage,
required this.role,
});
I am trying to embedded artistModal in test modal. I want to read it as a list in UI side. I can't quite figure it out how?
class TestModal {
final String id;
final String venueName;
final String eventName;
List<ArtistModal> artistModal = <ArtistModal>[];
TestModal({
required this.venueName,
required this.id,
required this.eventName,
required this.artistModal,
});
factory TestModal.fromJson(Map<String, dynamic> json) {
return TestModal(
venueName: json['venueName'] ?? '',
eventName: json['eventName'] ?? '',
artistModal: List.from(json['artistModal'] ?? ''),
id: json['id'] ?? '');
}
Map<String, dynamic> toMap() {
return {
'venueName': venueName,
'eventName': eventName,
'artistModal': artistModal,
'id': id,
};
}
}
Edited
Alright. And after this can I use this to create/update and read data from Cloud Firestore.
Stream<List<TestModal>> fetchListOfTest() {
return _testCollection.snapshots().map((event) => event.docs
.map((e) => TestModal.fromJson(e.data() as Map<String, dynamic>))
.toList());
}
Future<void> setTest(TestModal testModal) {
var options = SetOptions(merge: true);
return _testCollection.doc(testModal.id).set(testModal.toMap(), options);
}
Since I do not have enough reputation to write a comment, I couldn't ask you what you want to achieve here exactly. I'm assuming that you want to generate a list of ArtistModal and store it in artistModal attribute of class TestModal. If that's the case, you can change your factory method to something like this:
factory TestModal.fromJson(Map<String, dynamic> json) {
return TestModal(
venueName: json['venueName'] ?? '',
eventName: json['eventName'] ?? '',
artistModal: json['artistModal'] != null ?
List.generate(json['artistModal'].length, (index) => ArtistModal(
artistName: json['artistModal'][index]['name'],
artistImage: json['artistModal'][index]['image'],
role: json['artistModal'][index]['role'],
)) : [],
id: json['id'] ?? '');
}
I am assuming that json['artistModal'] contains a list of Map, where the map contains data related to artist modal.

Flutter firebase get a field from document

I'm trying to get the message from a field in a collection. It is a read only data, i have modeled it like this
class SocialShare {
final String message;
SocialShare({
this.message,
});
factory SocialShare.fromJson(Map<String, dynamic> json) {
return SocialShare(
message: json['message'],
);
}
}
I have a collection named 'Social Share and contains a doc with a single field called message..
Here is how i call it
class SocialShares {
final CollectionReference _socialMessage =
FirebaseFirestore.instance.collection('socialShare');
Future<SocialShare> fetchsocial() {
return _socialMessage.get().then((value) {
return SocialShare.fromJson(value); // how can i call it
});
}
}
How can i get a that value from firebase
You can do fetchSocial async and await the result to return:
fetchSocial() async{
var value = await _socialMessage.get();
return SocialShare.fromJson(value);
}
then you have to call fetchSocial method with await or then where you need it.
await fetchSocial() or fetchSocial.then ...
The value in _socialMessage.get().then((value) { is a QuerySnapshot object, which contains the DocumentSnapshots of all documents in the socialShare collection.
To get a field, or the Map<String, dynamic> of all fields, you need the data from a single document. For example, to get the message field fro the first document from the collection, you can do:
return SocialShare.fromJson(value.docs[0].data());

how to access a collection inside a firestore document and assign it as a list to my dart list inside my model?

how can i access a firestore collection inside a document and assign it to a list in my model, i tried accessing it like this snap.reference.collection('submittedUsers').get(); but i can't use async/await in constructors so i didn't knew what to do, this my model code:
final String fileUrl;
final String title;
final String description;
final List<String> submittedUsers;
LectureModel({
#required this.fileUrl,
#required this.title,
#required this.description,
this.submittedUsers,
}) : super(
fileUrl: fileUrl,
title: title,
description: description,
submittedUsers: submittedUsers,
);
Map<String, dynamic> toDocument() {
return {
'fileUrl': fileUrl,
'title': title,
'description': description,
};
}
factory LectureModel.fromSnapshot(DocumentSnapshot snap) {
// my submittedUsers collection is inside this `snap` document
// i want to get that collection and i assign it's memebers to my model submittedUsers list
final data = snap.data();
return LectureModel(
fileUrl: data['fileUrl'] as String,
title: data['title'] as String,
description: data['description'] as String,
);
}
}
Your data class looks just fine., but in your current structure, also add a list to your factory in the model, like this:
factory LectureModel.fromSnapshot(DocumentSnapshot snap, List<String> submittedUsersList) {
final data = snap.data();
return LectureModel(
fileUrl: data['fileUrl'] as String,
title: data['title'] as String,
description: data['description'] as String,
submittedUsers: submittedUsersList,
);
}
}
But you need to call your method LectureModel.fromSnapshot inside a function, or a future builder or stream builder for example. And also fetch the subcollection after getting the parent document
For example, you need a function like this, and put it in your widget where you need it.
Future<List<LectureModel>> getLectures() async {
QueryDocumentSnapshot snap = await FirebaseFirestore.instance.collection('NAME_OF_PARENT_COLLECTION').get();
List<LectureModel> lectureList=[];
//this will check that there actually is documents in firebase
if(snap.docs.isNotEmpty){
for(var singleSnapDocument in snap.docs){
//then you have to get the subcollection seperately for every
//document.
List<String> listOfsubmittedUsers =[];
listOfsubmittedUsers = await
FirebaseFirestore.instance.collection('NAME_OF_PARENT_COLLECTION')
.doc(singleSnapDocument.id).collection('submittedUsers')
.get().then((result)=> result.docs.map((e) => e.data().toString()).toList());
//this will add a LectureModel object into our list lectureList
lectureList.add(LectureModel.fromSnapshot(singleSnap, listOfsubmittedUsers));
}
}
print('Length of lectureList is: ' + lectureList.length.toString());
return lectureList;
}
Now, anywhere in your code, you can use onPressed or in initState and just call your function getLectures. i.e
onPressed: () async {List<LectureModel> listOfLecture = await getLectures();}
Your problem should be solved.

How to update collection documents in firebase in flutter?

I want to update a document field and I've tried the following code but it doesn't update.
can anyone give me a solution, please?
My Code:
var snapshots = _firestore
.collection('profile')
.document(currentUserID)
.collection('posts')
.snapshots();
await snapshots.forEach((snapshot) async {
List<DocumentSnapshot> documents = snapshot.documents;
for (var document in documents) {
await document.data.update(
'writer',
(name) {
name = this.name;
return name;
},
);
print(document.data['writer']);
//it prints the updated data here but when i look to firebase database
//nothing updates !
}
});
For cases like this, I always recommend following the exact types in the documentation, to see what options are available. For example, a DocumentSnapshot object's data property is a Map<String, dynamic>. To when you call update() on that, you're just updating an in-memory representation of the document, and not actually updating the data in the database.
To update the document in the database, you need to call the DocumentReference.updateData method. And to get from the DocumentSnapshot to a DocumentReference, you call the DocumentSnapshot.reference property.
So something like:
document.reference.updateData(<String, dynamic>{
name: this.name
});
Unrelated to this, your code looks a bit non-idiomatic. I'd recommend using getDocuments instead of snapshots(), as the latter will likely result in an endless loop.
var snapshots = _firestore
.collection('profile')
.document(currentUserID)
.collection('posts')
.getDocuments();
await snapshots.forEach((document) async {
document.reference.updateData(<String, dynamic>{
name: this.name
});
})
The difference here is that getDocuments() reads the data once, and returns it, while snapshots() will start observing the documents, and pass them to us whenever there's a change (including when you update the name).
Update 2021:
Lot of things have changed in the API, for example, Firestore is replaced by FirebaseFirestore, doc is in, etc.
Update a document
var collection = FirebaseFirestore.instance.collection('collection');
collection
.doc('some_id') // <-- Doc ID where data should be updated.
.update({'key' : 'value'}) // <-- Updated data
.then((_) => print('Updated'))
.catchError((error) => print('Update failed: $error'));
Update nested value in a document:
var collection = FirebaseFirestore.instance.collection('collection');
collection
.doc('some_id') // <-- Doc ID where data should be updated.
.update({'key.foo.bar' : 'nested_value'}) // <-- Nested value
.then((_) => print('Updated'))
.catchError((error) => print('Update failed: $error'));
To update some fields of a document without overwriting the entire document, use the following language-specific update() methods:
final washingtonRef = FirebaseFirestore.instance.collection("cites").doc("DC");
washingtonRef.update({"capital": true}).then(
(value) => print("DocumentSnapshot successfully updated!"),
onError: (e) => print("Error updating document $e"));
Server Timestamp
You can set a field in your document to a server timestamp which tracks when the server receives the update.
final docRef = FirebaseFirestore.instance.collection("objects").doc("some-id");
final updates = <String, dynamic>{
"timestamp": FieldValue.serverTimestamp(),
};
docRef.update(updates).then(
(value) => print("DocumentSnapshot successfully updated!"),
onError: (e) => print("Error updating document $e"));
Update fields in nested objects
If your document contains nested objects, you can use "dot notation" to reference nested fields within the document when you call update():
// Assume the document contains:
// {
// name: "Frank",
// favorites: { food: "Pizza", color: "Blue", subject: "recess" }
// age: 12
// }
FirebaseFirestore.instance
.collection("users")
.doc("frank")
.update({"age": 13, "favorites.color": "Red"});
Update elements in an array
If your document contains an array field, you can use arrayUnion() and arrayRemove() to add and remove elements. arrayUnion() adds elements to an array but only elements not already present. arrayRemove() removes all instances of each given element.
final washingtonRef = FirebaseFirestore.instance.collection("cities").doc("DC");
// Atomically add a new region to the "regions" array field.
washingtonRef.update({
"regions": FieldValue.arrayUnion(["greater_virginia"]),
});
// Atomically remove a region from the "regions" array field.
washingtonRef.update({
"regions": FieldValue.arrayRemove(["east_coast"]),
});
Increment a numeric value
You can increment or decrement a numeric field value as shown in the following example. An increment operation increases or decreases the current value of a field by the given amount.
var washingtonRef = FirebaseFirestore.instance.collection('cities').doc('DC');
// Atomically increment the population of the city by 50.
washingtonRef.update(
{"population": FieldValue.increment(50)},
);

Firebase - Firestore - get key with collection.add()

I am facing a problem with the new Firestore from Firebase.
Situation: I have a collection('room')
I create room with collection('room').add(room)
What I'm trying to do: I need to update a room.
For this, I use: collection('room').doc(ROOM_ID).update(update)
So I need to add ROOM_ID in the document in my collection:
|room
ROOM_ID
id:ROOM_ID,
someContent: ForTheQuery
Is there a possible way to achieve that?
An alternative is to create myself a generated ID with:
collection('room')
.doc(someId)
.set({
id: someId,
someContent: ForTheQuery
});
but i want to avoid it.
You can use doc() to create a reference to a document with a unique id, but the document will not be created yet. You can then set the contents of that doc by using the unique id that was provided in the document reference:
const ref = store.collection('users').doc()
console.log(ref.id) // prints the unique id
ref.set({id: ref.id}) // sets the contents of the doc using the id
.then(() => { // fetch the doc again and show its data
ref.get().then(doc => {
console.log(doc.data()) // prints {id: "the unique id"}
})
})
ANGULARFIRE:
get ID before add database:
var idBefore = afs.createId();
console.log(idBefore );
ANDROID FIRESTORE:
String idBefore = db.collection("YourCol").document().getId();
Firebase Javascript SDK:
Just use .id to get the key, here is an example using async/ await :
const KEYID = async() => (await fs.collection("testing").add({ data: 'test'})).id;
You can get the ID from the created document by using collection.ref.add(your item without id) and the response (res) will contain the new document reference created with the ID inside it. So get the ID by simply doing res.id.
createOne(options: { item: any, ref: AngularFirestoreCollection<any> }) {
const promise = new Promise((resolve, reject) => {
if (options.item) {
// Convert object to pure javascript
const item = Object.assign({}, options.item);
console.log('dataService: createOne: set item: ', item);
options.ref.ref.add(item)
.then((res) => {
console.log('dataService: createOne success: res: ', res);
resolve(res);
}).catch(err => {
console.error('dataService: createOne: error: ', err);
reject(err);
});
} else {
console.log('dataService: createOne: wrong options! options: ', options);
reject();
}
})
return promise;
}

Resources