I'm having trouble viewing user comments in Flutter with Firebase - firebase

I'm trying to sort the user's comments on his profile page but the code doesn't work right what's wrong here please
List<Widget> myComments = [];
posts.data.docs.forEach((post) {
cloudRef.collection('blog').doc(post.id).collection('comments').where('ownerID',isEqualTo:id).get().then((val){
val.docs.forEach((comment) {
myComments.add(CommentCard(replyContent: comment.data()));
});
});
});
return myComments;

The issue with the code is that forEach is used with an asynchronous operation. forEach does not wait for the async operations to complete for each loop.
Checkout this update to the code using for-in. I also updated the comment card list to use .map so you can have it in one list and then add all the comments at once.
List<Widget> myComments = [];
for (var post in posts.data.docs) {
var val = await cloudRef.collection('blog').doc(post.id).collection('comments').where('ownerID',isEqualTo:id).get();
var commentCardList = val.docs.map((comment) => CommentCard(replyContent: comment.data())).toList();
myComments.addAll(commentCardList);
}
return myComments;

Related

Does CefSharp implemented DOM manipulation (late 2020)?

Just got started with CefSharp, and I searched for hours about how to manipulate the DOM. All the information I was able to find is kind of outdated, and stating that the only way to do DOM Manipulation is through JavaScript injection (with methods like ExecuteJavaScriptAsync and EvaluateScriptAsync).
The most recent info is more than 2 year old: Any reason to prefer CefSharp over CefGlue (or vice-versa)?
Another one from official github page (even older): https://github.com/cefsharp/CefSharp/issues/1587
But looking at the CEF source code, we can see that there is implemented some perfectly suitable methods to do exactly that, like:
virtual CefRefPtr<CefDOMNode> GetElementById(const CefString& id)
(source code url: https://bitbucket.org/chromiumembedded/cef/src/master/include/cef_dom.h?at=master&fileviewer=file-view-default)
So, to sum up: Since the CEF implemented methods to execute DOM manipulation, and CefSharp community seems to be very active and pushing frequent updates, and all information I could find on StackOverflow and Google are (kind of) outdated, does anyone knows if CefSharp currently (year 2020, version 85.3.130) already implemented any kind of DOM manipulation besides using JavaScript?
does anyone knows if CefSharp currently (year 2020, version 85.3.130) already implemented any kind of DOM manipulation besides using JavaScript?
As of 2022 there is an alternative to using JavaScript to access the DOM when using CefSharp. You can now use CefSharp.Dom which is an asynchronous library for accessing the DOM. The package requires CefSharp 104.4.180 or greater.
It uses the Chromium DevTools Protocol and originally started out as a port of Puppeteer Sharp.
It's freely available on .
It also supports keyboard, mouse and touch emulation for automating websites.
The following examples are an excerpt from the readme.
// Add using CefSharp.Dom to access CreateDevToolsContextAsync and related extension methods.
await using var devToolsContext = await chromiumWebBrowser.CreateDevToolsContextAsync();
// Get element by Id
// https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector
var element = await devToolsContext.QuerySelectorAsync<HtmlElement>("#myElementId");
//Strongly typed element types (this is only a subset of the types mapped)
var htmlDivElement = await devToolsContext.QuerySelectorAsync<HtmlDivElement>("#myDivElementId");
var htmlSpanElement = await devToolsContext.QuerySelectorAsync<HtmlSpanElement>("#mySpanElementId");
var htmlSelectElement = await devToolsContext.QuerySelectorAsync<HtmlSelectElement>("#mySelectElementId");
var htmlInputElement = await devToolsContext.QuerySelectorAsync<HtmlInputElement>("#myInputElementId");
var htmlFormElement = await devToolsContext.QuerySelectorAsync<HtmlFormElement>("#myFormElementId");
var htmlAnchorElement = await devToolsContext.QuerySelectorAsync<HtmlAnchorElement>("#myAnchorElementId");
var htmlImageElement = await devToolsContext.QuerySelectorAsync<HtmlImageElement>("#myImageElementId");
var htmlTextAreaElement = await devToolsContext.QuerySelectorAsync<HtmlImageElement>("#myTextAreaElementId");
var htmlButtonElement = await devToolsContext.QuerySelectorAsync<HtmlButtonElement>("#myButtonElementId");
var htmlParagraphElement = await devToolsContext.QuerySelectorAsync<HtmlParagraphElement>("#myParagraphElementId");
var htmlTableElement = await devToolsContext.QuerySelectorAsync<HtmlTableElement>("#myTableElementId");
// Get a custom attribute value
var customAttribute = await element.GetAttributeAsync<string>("data-customAttribute");
//Set innerText property for the element
await element.SetInnerTextAsync("Welcome!");
//Get innerText property for the element
var innerText = await element.GetInnerTextAsync();
//Get all child elements
var childElements = await element.QuerySelectorAllAsync("div");
//Change CSS style background colour
await element.EvaluateFunctionAsync("e => e.style.backgroundColor = 'yellow'");
//Type text in an input field
await element.TypeAsync("Welcome to my Website!");
//Click The element
await element.ClickAsync();
// Simple way of chaining method calls together when you don't need a handle to the HtmlElement
var htmlButtonElementInnerText = await devToolsContext.QuerySelectorAsync<HtmlButtonElement>("#myButtonElementId")
.AndThen(x => x.GetInnerTextAsync());
//Event Handler
//Expose a function to javascript, functions persist across navigations
//So only need to do this once
await devToolsContext.ExposeFunctionAsync("jsAlertButtonClick", () =>
{
_ = devToolsContext.EvaluateExpressionAsync("window.alert('Hello! You invoked window.alert()');");
});
var jsAlertButton = await devToolsContext.QuerySelectorAsync<HtmlButtonElement>("#jsAlertButton");
//Write up the click event listner to call our exposed function
_ = jsAlertButton.AddEventListenerAsync("click", "jsAlertButtonClick");
//Get a collection of HtmlElements
var divElements = await devToolsContext.QuerySelectorAllAsync<HtmlDivElement>("div");
foreach (var div in divElements)
{
// Get a reference to the CSSStyleDeclaration
var style = await div.GetStyleAsync();
//Set the border to 1px solid red
await style.SetPropertyAsync("border", "1px solid red", important: true);
await div.SetAttributeAsync("data-customAttribute", "123");
await div.SetInnerTextAsync("Updated Div innerText");
}
//Using standard array
var tableRows = await htmlTableElement.GetRowsAsync().ToArrayAsync();
foreach (var row in tableRows)
{
var cells = await row.GetCellsAsync().ToArrayAsync();
foreach (var cell in cells)
{
var newDiv = await devToolsContext.CreateHtmlElementAsync<HtmlDivElement>("div");
await newDiv.SetInnerTextAsync("New Div Added!");
await cell.AppendChildAsync(newDiv);
}
}
//Get a reference to the HtmlCollection and use async enumerable
//Requires Net Core 3.1 or higher
var tableRowsHtmlCollection = await htmlTableElement.GetRowsAsync();
await foreach (var row in tableRowsHtmlCollection)
{
var cells = await row.GetCellsAsync();
await foreach (var cell in cells)
{
var newDiv = await devToolsContext.CreateHtmlElementAsync<HtmlDivElement>("div");
await newDiv.SetInnerTextAsync("New Div Added!");
await cell.AppendChildAsync(newDiv);
}
}

async operation within an async function

For an instance, let's suppose I am calling a function in initState which gets some documents from the Firebase collection. I am iterating on those documents using async forEach and need to perform an await operation to get some data from another collection from firebase and saving them in a list then returning after the forEach is finished. But the returned list is empty as the second await function completes after return statement. How do I handle this? I have a hard time understanding Asynchronous programming so please a detailed explanation will be highly appreciated.
The code here is just an example code showing an actual scenario.
Future getList() async {
//These are just example refs and in the actual refs it does print actual values
var collectionOneRef = Firestore.instance.collection('Collection1');
var collectionTwoRef = Firestore.instance.collection('Collection2');
List<Map<String, dynamic>> properties = [];
QuerySnapshot querySnapshot = await collectionOneRef
.getDocuments()
.then((query) {
query.documents.forEach((colOneDoc) async {
var colOneID = colOneDoc.documentID;
await collectionTwoRef.document(colOneID).get().then((colTwoDoc) {
Map<String, dynamic> someMap = {
"field1": colTwoDoc['field1'],
"field2": colTwoDoc['field2']
};
properties.add(someMap);
properties.sort((p1, p2) {
//sorting based on two properties
var r = p1["field1"].compareTo(p2["field1"]);
if (r != 0) return r;
return p1["field2"].compareTo(p2["field2"]);
});
print(properties); //this prints actual data in it
));
});
});
});
print(properties); //This prints a blank list as [] and obviously returns blank list
return properties;
}
And now when I call this function in an initState of a stateful Widget and display it somewhere, it display a blank list. BUT, after I "Hot Reload", then it displays. I want to get the data and display it without hot reloading it. Thanks in advance
Sounds like you need to await the whole function itself in the block of code where you are calling getList().
you are async'ing and awaiting inside the function, and that looks fine which is why you are getting results when hot reloading. But it might be the case that you're not actually awaiting the entire function itself in the code that is calling this. Please Check.

How to get list of documents and not listen for changes in flutter cloud firestore?

My application is again fetching list of items from firestore whenever I make a sort locally.
Due to which I am losing my sorted list and getting the original list back again.
Essentially, I am looking for a .once() alternative as I used in firebase realtime db with JS.
fetchItemsFromDb().then((itemsFromDb) {
setState(() {
items = itemsFromDb;
isProcessed = true;
});
});
fetchItemsFromDb() async {
List<Item> items = [];
await Firestore.instance.collection('items').getDocuments().then((data) {
items = data.documents.map((DocumentSnapshot item) {
var i = item.data;
return Item(
i['itemName'],
int.parse(i['quantity']),
int.parse(i['price']),
i['veg'],
i['uid'],
i['timestamp'],
LatLng(i['location'].latitude, i['location'].longitude),
);
}).toList();
});
return items;
}
FetchItemsFromDB() should be working how you expect it to, it could be that the code calling the function:
fetchItemsFromDb().then((itemsFromDb) {
setState(() {
items = itemsFromDb;
isProcessed = true;
});
});
is being run again when you do not expect it. Does that code live in a build method? If so it will run anytime the widget it is in rebuilds, which depending on how you are doing your local sort may be happening. If you only need it to run once maybe add it to the initState() function for the widget.

Issue adding element to list

I'm a total newbie to Flutter and I'm trying to add some data from Cloud Firestore to a list in Flutter, but having issues. I try to add the element, but after executing, the element isn't there. It's not throwing an exception or anything either. Maybe someone else has some advice for me!
I have tried changing the type of list (capture the doc from Cloud Firestore instead of data within the doc, same issue), I have also debugPrinted the data I am trying to store to make sure it exists, it does. I have done basic troubleshooting like running flutter clean as well. I am on the latest version of Flutter.
Firestore db = firestore();
List<String> getString() {
var dataList = new List<String>();
db.collection('Users').get().then((querySnapshot) {
querySnapshot.forEach((doc) {
dataList.add(doc.get('First name'));
});
});
debugPrint(dataList.first);
return dataList;
The list is empty, though it should contain the "First name" field on this Cloud Firestore doc. Again, verified the data does exist and prints when calling debugPrint.
The db.collection('Users').get() is a async function, so debugPrint(dataList.first); executes before of the end of your firestores get, because that your array returns empty.
If you try it:
db.collection('Users').get().then((querySnapshot) {
querySnapshot.forEach((doc) {
dataList.add(doc.get('First name'));
});
debugPrint(dataList.first);
});
You will see your data.
You can use await to wait the call finishes, so you must return a Future and use async key word on function declaration. This is a conceipt that you must know of flutter async functions (Async Flutter). So, the code below can solve your problem.
Firestore db = firestore();
Future <List<String>> getString() async {
var dataList = new List<String>();
var result = await db.collection('Users').get();
result.forEach((doc) {
dataList.add(doc.get('First name'));
});
debugPrint(dataList.first);
return dataList;
}

How to avoid using await key in dart Map by foreach function

So, I have a map which has to do with some asynchronous processing using the items inside. I used the forEach loop construct and inside the callback is designed to be async because I call an await inside the iteration body
myMap.forEach((a, b) { await myAsyncFunc(); } );
callFunc();
I need the callFunc() to be called after all the items have been iterated. But the forEach exits immediately. Help!
Use a for loop over Map.entries instead of forEach. Provided you are in an async function, await-ing in the body of a for loop will pause the iteration. The entry object will also allow you to access both the key and value.
Future<void> myFunction() async {
for (var entry in myMap.entries) {
await myAsyncFunction(entry.key, entry.value);
}
callFunc();
}
You can also use Future.forEach with a map like this :
await Future.forEach(myMap.entries, (MapEntry entry) async {
await myAsyncFunc();
});
callFunc();
You could also use map like:
const futures = myMap.map((a, b) => myAsyncFunc());
await Future.wait(futures);
callFunc();
entries used in extract Maplist from HashMap.
products.entries.forEach((e) {
var key = e.key;
var values = e.value;
double sum = 0;
values.forEach((value) => sum += value[PlanogramShelf.SHELF_FULL]);
target.add(OrdinalSales(
key, double.parse((sum / valueslength).toStringAsFixed(2))));
});

Resources