i hope you have a great day, i'm still learn Flutter, and i have some difficulties to display some data
so i have this List data
listData = [{someData: Lorem #1, image: linkToSomeImage}]
and i need to display the image to Image.network(image),
but i don't know why, i tried listData[0].image or listData[0]["image"] to populate the variable but it can't works, and got all the red :(
what is the correct way to do this?
As you have the list of json(Map), data will be accessed by operator [] with key
listData[0]["image"]
and usage will be
Row(
children: [
for(int i=0; i< listData.length; i++)
Image.network(listData[i]['image`]),
]
)
Related
I am building a nutrition app for practice.
Right now I got a collection named "inventar" which stores a document, containing two arrays. "product" is an array which contains all products, like milk, sausage, cheese etc. Another array, "expirationDate" contains, when the products are going to decay.
I would like to show the content of these two arrays within a table.
Sounds easy, right?
Well, I am struggling with this exercise since over 2 weeks and am stuck, so I am asking for your help.
First, I asked for the data in multiple ways, until I became totaly confused about DocumentSnapshot, QuerySnapshot, DocumentReference, AsynchSnapshot and when to use get(), snapshot() or StreamBulder()/FutureBuilder() right now. I feel like, every time I found a reasonable solution in the internet, one little tiny thing doesnt work out, leading to fail the entire approach and having to search for another way.
Searching the net for ways to query an array, which should be pretty easy, I am only finding solutions, performing a search like "give me only those entrys". I simple want to get the entire array out, no entrys from x to y (at least not right now). Or can't I query an array whichout a where() statement?
After I did not find a way which worked out for me, I tried to get the array length after my "query", so that I could do a for-loop to retrive the data. BUT I did not seem to find the length property of the query to deliver it to the for-loop.
So, after this approach, I tried to work with StreamBuilder and FutureBuilder and a seperate async method. BUT either the FutureBuilder complains about me not providing the proper Widget type (how to return a new TableRow from a TableRow type method, if there is a Future involved?). StreamBuilder did not work out for me neither.
I don't even have code to share with you right now, because I threw every approach away in hope the next one would hopefully work. I guess, there is alot of confusion to me right now because I dont see how the individuell parts of the firestore query stick together and when I have to/can use get(), when snapshot() when map() and so on.
So, in short terms:
I simply would like to retrive two seperate arrays within one doc within on collection and to put these into a text widget within a TableCell.
How would you guys do this?
Would appreciate if you could explain to me when to use StreamBuilder() and FutureBuilder() and when/in which order to use get(), snapshot(), map() related to the firestore query. I am so confused right now.
Looking forward to your replys!
Goku
Edit:
So, this is what I got from the link Frank send:
new StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection("inventar").snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return new ListView(
children: snapshot.data!.docs.map((DocumentSnapshot document) {
return new ListTile(
title: new Text(document.data()?['produkt']),
subtitle: new Text(document.data()?['verfallsdatum']),
);
}).toList(),
);
},
)
Right now I get "type 'List' is not a subtype of type 'String'", and that even though I am actualy calling for strings in an array. Tried to use .toString() but then it says "The argument type 'String?' can't be assigned to the parameter type 'String'".
And, don't I need an Index value to be able to call each individual entry in the array? Or is this done automaticaly using .map()?
Not to mention I set the date as string currently, because I already did research on transfering the firebase timestamp to an actual date which looked really complicated. So I posponed this until I got the data in the right structure first.
You are storing 'product' and 'expirationDate' array in a single document.
But in your code i can see that you are trying to fetch documents from that query of 'inventar'.
Thats why you getting "type 'List' is not a subtype of type 'String'" error because
document.data()?['produkt']
this will return a list. And you are trying to give that list to a Text widget which only accepts a string.
What you should have done was to fetch only a single document and then design a iterable widget which will go through the list inside your document.
You have done opposite, you are iterating before your document is fetched.
Lets do the way i have just explained that is iterating after fetching document-
Im following the first paragraph of what your question says and writing the code now-
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection("inventar").snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return new ListView(
children: snapshot.data.docs.first.data()['products'].map((String product){
return ListTile(leading: Text(product));
}).toList()
);
},
)
In your code you are fetching list of documents which is not the case as you explained how your database looks.
As you can see in my code, we can only fetch either product or expirationDate because they are stored in seperate list.
What i have done in this code, is that i went to the inventar collection and fetched a querysnapshot of that collection which is fetched by this code-
stream: FirebaseFirestore.instance.collection("inventar").snapshots()
Now this QuerySnapshot contains all documents in that collection.
As you said you have only one document in your collection, so i have fetched in that way-
snapshot.data.docs.first.data()['products']
now this will return a list, because you have stored a list in that document. So i have used map function to map the product list into a ListTile widget and return it.
So this way we can only fetch one list in stream builder, either products or expirationDate. If you dont want your database to be changed, then you have to fetch those two list seperately using a function(not streambuilder).
But there is another way we can fetch your data the way you want without changing your database structure. By using a listview.builder widget-
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection("inventar").snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return new ListView.builder(
itemCount: snapshot.data.docs.first.data()['products'].length,
itemBuilder: (ctx, i) {
return ListTile(
leading: Text(snapshot.data.docs.first.data()['products'][i]),
trailing:
Text(snapshot.data.docs.first.data()['expirationDate'][i]),
);
},
);
},
)
Here the length of listview.builder widget will be given by itemCount. So im assuming that both the list having the same length otherwise it will throw errors.
This will return a list-
snapshot.data.docs.first.data()['products']
And then this will return its length-
snapshot.data.docs.first.data()['products'].length
Suppose the value of length of your list is 5.
Now listview.builder widget will automatically return listTile widget 5 number of times and i is index which goes from 0 to 4 inclusive. So now inside ListTile you can show the ith data of your list.
So i guess this code should work. And i hope you get the idea of how im fetching the list first and then iterating.
I'm reading back record sets in an express server using the node mssql package. Reading back the values outputs an array of Json objects as expected.
Now I need to modify the Email propoerty value of each Json object. So I tried looping through the recordset and changing the value at each index:
var request = new sql.Request(sql.globalConnection);
request.input('p_email', sql.VarChar(50), userEmail);
request.execute('GetDDMUserProfile', function(err, recordsets, returnValue) {
for (var i = 0; i < recordsets.length; i++){
recordsets[i].Email = "joe#gmail.com";
}
console.log(recordsets);
});
But instead of modifying the Emailvalue of each Json object, this code just appends a new email property to the last Json object.
How can you map new property values to an array of JSON objects?
Example output:
An example of the output is shown below, where a new Email property has been added to the end of the array instead of changing each existing property value:
[
[
{
ID:[
4
],
UserName:"Brian",
Email:"joe#gmail.com"
},
{
ID:[
5
],
UserName:"Brian",
Email:"joe#gmail.com"
}
Email:'joe#gmail.com' ]
]
The issue here is that your dataset appears to not be an array of JSON objects but, rather, an array of arrays of JSON objects. If you know for certain that you'll always have only one array in the top-most array, then you can solve the problem like this:
recordsets[0][i].Email = "joe#gmail.com";
to always target the first array in the top-most array. However, if the top-most array could potentially have more than one array, that'll be a different kind of issue to solve.
I am looking to store an array of individual lotto drawn numbers within a MongoDB document.
Neither of the below structures work. Can anyone point out the correct way to form JSON to facilitate this type of storage?
[{
"date":"2014-02-10T10:50:42.389Z",
"numbers":{
[10,20,30,40,12,31,35]
}
}]
[{
"date":"2014-02-10T10:50:42.389Z",
"numbers":{
10,20,30,40,12,31,35
}
}]
I am looking to import this document as a Mongo document and ultimately build on it after that.
db.x.insert({"date" : new ISODate() ,"numbers":[1,2,3]}) should work.
This is sort of a three part question. I have a JSON file. A few of the values in the JSON file are arrays. Keeping that in mind:
1) On any given page, I'd only want one set of values coming out of the JSON file. For example (as you'll see in code below) my JSON file is a list of attorneys. On any given bio page, I'd obviously only want one attorney's information. I'm currently, successfully, doing this by pulling back the entire JSON and then using ng-show. But this is causing some other issues that I'll explain in later points, so I'm wondering if there's something to put in the app.factory itself to only bring back the one set in the first place.
2) As mentioned, some of the values are arrays. This comes into play two ways in this situation. One of the ways is that there is an array of quotes about the attorney that I'll need to drop into a JS array so that my JS function can loop through them. Currently, I'm hardcoding the quotes for the one test attorney but I'm really trying to figure out how to make this dynamic. This is one reason I'm trying to figure out how to bring back only one attorney's information so I can then, somehow, say his quotes go into this array.
3) Another array value is a list of his specialty areas. I have another, hardcoded, JS object, associating the short terms with the display names. I realized though, that this has two issues.
a) The JS renders after the Angular, so I can't reference that JS in the Angular code
b) I have no way , anyway, to display the JS dynamically inside the Angular code.
My solution to that aspect was to create a second JSON file holding the area hash but besides being a little cumbersome, I'm also not sure how to dynamically display just the ones I want. e.g: If my attorney only specializes in securities and litigation, how would I tell the code to only display {{areas.securities}} and {{areas.litigation}}? So,I'm open to thoughts there as well.
Here is the current, relevant code. If you need more, just ask.
Thanks.
attorneys.json (irrelevant lines removed)
{"attorneys":
[
{
"id":1,
"name":"Bob Smith",
"quotes":
[
{
"id": 1,
"quote": "Wonderful guy!",
"person": "Dovie"
},
{
"id": 2,
"quote": "If ye be wanting a haggis like no other, Bob be yer man!",
"person": "Angus McLoed"
},
{
"id": 3,
"quote": "Wotta Hottie!",
"person": "Bob's wife"
}
],
"areas": ["altdispute", "litigation", "securities"],
}
]
}
...and the relevant current JS object that I'm not sure what to do with:
var practiceareas = {
altdispute: "Alternative Dispute Resolution",
businesscorp: "Businesses & Corporations",
estateplanning: "Estate Planning",
futures: "Futures & Derivatives",
litigation: "Litigation",
productliability: "Product Liability",
realestate: "Real Estate",
securities: "Securities"
}
script.js (relevant function)
var idno = 0;
/* This is what I want replaced by the Angular pull */
var quotelist = ["\"Wonderful guy!\"<br/>-Dovie", "\"If ye be wanting a haggis like no other, Bob be yer man!\"<br/>-Angus McLoed", "\"Hubba, Hubba! What a hottie!\"<br/>-Bob's wife"];
$("#bio_quotes").html(quotelist[0]);
function quoteflip(id, total){
var src1 = quotelist[idno];
$("#bio_quotes").fadeOut(500, function(){
$("#bio_quotes").html(src1).fadeIn(500);
});
idno = (id + 1) % total;
window.setTimeout(function(){quoteflip(idno, quotelist.length);}, 5000);
}
window.setTimeout(function(){quoteflip(idno, quotelist.length);}, 500);
By the way, as far as the quotes, I'm even happy to turn the JSON into a more condensed version by removing the id and consolidating the quote and author - making it an array of strings instead of mini-objects - if that makes it easier. In fact, it might be easier as far as the function anyway.
Can definitely filter things out at the service / factory using Array.filter. If you want to filter it server side, you have to have the code at server side that will do that.
Not sure what your backend store is but definitely doable.
Again, you can do this pretty easily with Array.map which let you pull specific values into a new Array. If you just want the name and quotes' quote and person name, you can definitely do this using Array .filter and .map and bind the new array to your viewmodel / scope.
Hmm.. again, I'd disagree, this look like the same issue with JavaScript array manipulation. You can definitely as part of the transformation in point 1 and 2, include this so it will transfer area to the long practice area names. The easiest way to show the relevant practice area is to map it to the long name during the transformation in the service layer.
//get matching attorney from the store by id
var matches = data.attorneys.filter(function(a) {
return a.id === id;
});
//If match found,
if (matches.length === 1) {
var result = matches[0];
//map the long name for practicing area
//use the matching attorney's area name as key
//and overwrite the result areas with the map
result.areas = result.areas.map(function(a) {
return practiceareas[a];
});
return result;
}
See this solution: http://embed.plnkr.co/xBPju7/preview
As for the fade in and fade out, I'll let you figure it out...
My form in the html DOM is a checkbox to click (there can be more than one). The problem occurs in the description string when ever I use an apostrophe, since my list object is single-quote deliniated. This is one of the checkboxes in the form:
<input type="checkbox" id="cbx" name="cbx" value="{'getPic': 'url', 'picsrc': 'http://lh3.ggpht.com/_ZB3cttqooN0/SVmJPfusGWI/AAAAAAAADvA/GuIRgh6eMOI/Grand%20Canyon%201213_121508214.JPG', 'pos': None, 'description': 'Here's what it REALLY looks like at 5:30am! Bring your headlight!'}">
The javascript that reads the values of the checked checkboxes and pushes them into an array (list):
var pylist = [];
for (i=0; i<document.picList.cbx.length; i++) {
if (document.picList.cbx[i].checked) {
pylist.push( document.picList.cbx[i].value );
}
}
var getstr = JSON.stringify(pylist);
The problem is always that getstr at this point has chopped off everthing after the single quote in the description property.
I've tried different ways of escaping it to little avail.
The problem is that the value of the checkbox already is a JSON string. One solution would be to call JSON.parse() on the value:
var pylist = [];
for (i=0; i<document.picList.cbx.length; i++) {
if (document.picList.cbx[i].checked) {
pylist.push( JSON.parse( document.picList.cbx[i].value) );
}
}
var getstr = JSON.stringify(pylist);
I've run into the same issue - we stick json in a hidden field so we don't have to query the server on every page. We replace apostrophes with a "code" before putting into the html - we added a javascript function that replaces the code with an apostrophe.
Really hacky, but it works really well. Of course, we have one place in the code that gets json from the server and one place where javascript needs to parse it - if you find you're repeating the methods throughout your code, your mileage will vary.