I got the following List<Map> in Flutter:
List<Map<String, dynamic>> recipeList = [
{
'name': 'rec1',
'id': 1,
'img': 'images/recipe.jpg',
'ingredients': [{
'name': 'salt',
'amount': '1',
'unit': '1',
},
{
'name': 'flour',
'amount': '100',
'unit': 'g',
},
{
'name': 'water',
'amount': '100',
'unit': 'g',
},
{
'name': 'milk',
'amount': '100',
'unit': 'g',
},],
},]
I pass it down through several Widgets and at some point I want to add the key value pair {'didBuy':false} to every Map inside the ingredients list (which is basically recipeList['ingredients']).
Thus I call:
List<Map<String, dynamic>> resultList = recipeList['ingredients'].map((elem) {
elem.addAll({'didBuy': false});
print(elem);
}).toList();
Unfortunately the following error message results: Dart Error: Unhandled exception:type '_InternalLinkedHashMap<String, bool>' is not a subtype of type 'Map<String, String>' of 'other'.
Does anybody know what is the correct way to add something to a map, without getting this error message?
Edited the question to be more precise.
EDIT2:
After calling the type of the List explicitly inside the Map as Hadrien suggested, I can add the key value pair with the boolean. Long term I want to fetch the data from the Internet, so I defined a RecipeObj:
class RecipeObj{
String name;
int id;
String img;
List<Map<String, dynamic>> ingredients;
RecipeObj(this.name, this.id, this.img, this.ingredients);
}
Here I explicitly state the type of the ingredients attribute, so I thought I could get of the explicit calling inside the (main) recipeList. But after passing the ingredients attribute down through some widgets, flutter recognizes it as List<Map<String, String>>, although I define it everywhere as an List<Map<String, dynamic>>, why is that?
dart infer the type of your ingredients list with Map<String, String>
you can specify the type by yourself inside your list
'ingredients': <Map<String, dynamic>>[ {
'name': 'salt',
'amount': '1',
'unit': '1',
},
or build a new Map<String, dynamic> inside your map function
List<Map<String, dynamic>> resultList = recipeList['ingredients'].map((elem) {
final map = Map<String, dynamic>.from(elem);
map.addAll({'didBuy': false});
return map;
}).toList();
This should do
List<Map<String,dynamic>> recipeList = [
at least if recipeList and ingredients point at the same collection instance.
var ingredients = recipeList;
Is this what you need?
void main() {
List<Map> recipeList = [
{
'name': 'rec1',
'id': 1,
'img': 'images/recipe.jpg',
'ingredients': [{
'name': 'salt',
'amount': '1',
'unit': '1',
},
{
'name': 'flour',
'amount': '100',
'unit': 'g',
},
{
'name': 'water',
'amount': '100',
'unit': 'g',
},
{
'name': 'milk',
'amount': '100',
'unit': 'g',
},]
},];
print("[DATA BEFORE ANY CHANGE]");
print("recipeList.length=${recipeList.length}");
print("recipeList[0][\"ingredients\"]=${recipeList[0]["ingredients"]}");
print("recipeList[0][\"ingredients\"].last=${recipeList[0]["ingredients"].last}");
print("recipeList[0][\"ingredients\"].length=${recipeList[0]["ingredients"].length}");
// no recipe is worth if it doesn't contain chocolate
recipeList[0]["ingredients"].add({
'name': 'cocoa powder',
'amount': '200',
'unit': 'g',
});
print("\n\n[DATA AFTER ADD]");
print("recipeList[0][\"ingredients\"].last=${recipeList[0]["ingredients"].last}");
print("recipeList[0][\"ingredients\"].length=${recipeList[0]["ingredients"].length}");
}
OUTPUT
[DATA BEFORE ANY CHANGE]
recipeList.length=1
recipeList[0]["ingredients"]=[{name: salt, amount: 1, unit: 1}, {name: flour, amount: 100, unit: g}, {name: water, amount: 100, unit: g}, {name: milk, amount: 100, unit: g}]
recipeList[0]["ingredients"].last={name: milk, amount: 100, unit: g}
recipeList[0]["ingredients"].length=4
[DATA AFTER ADD]
recipeList[0]["ingredients"].last={name: cocoa powder, amount: 200, unit: g}
recipeList[0]["ingredients"].length=5
Related
I am trying to show a table in my cloud function using the new #assistant/conversation with the array of data but when i test the action i am getting the error as below
Unsuccessful webhook call: Failed to translate JSON to ExecuteHttpResponse
But when i check the logs i am getting the row values like below
{
"responseJson": {
"session": {
"id": "ABwppHE5M8EGlWf3YmpUUGPQ5xxHh-cb2QYyF_YUarZbF_jXq-Ad2iKDtyI8XAyvWPp4hHnQockBWMZuQA",
"params": {},
"languageCode": ""
},
"prompt": {
"override": false,
"content": {
"table": {
"button": {},
"columns": [
"Date",
"Time",
"Place"
],
"image": {},
"rows": [
"20-10-2020",
"11:20",
"Test"
],
"subtitle": "",
"title": ""
}
}
}
}
}
Here is the implementation of my adding table in the conv
const tempDatas = ['20-10-2020', '11:20', 'Test'];
conv.add(
new Table({
dividers: true,
columns: ['Date', 'Time', 'Place'],
rows: tempDatas
})
);
I have used the same logic in google-actions plugin there it works fine.I have imported the Table like below
const { conversation, Table } = require('#assistant/conversation');
Fixed the issue. The structure provided by the new actions builder is little different with the old one
New Structure:
conv.add(new Table({
'title': 'Table Title',
'subtitle': 'Table Subtitle',
'image': ASSISTANT_LOGO_IMAGE,
'columns': [{
'header': 'Column A',
}, {
'header': 'Column B',
}, {
'header': 'Column C',
}],
'rows': [{
'cells': [{
'text': 'A1',
}, {
'text': 'B1',
}, {
'text': 'C1',
}],
}, {
'cells': [{
'text': 'A2',
}, {
'text': 'B2',
}, {
'text': 'C2',
}],
}, {
'cells': [{
'text': 'A3',
}, {
'text': 'B3',
}, {
'text': 'C3',
}],
}],
}));
You can simplify it like below that is the solution for my above issue
const tableRows = [];
tablesRows.push({ 'cells': [{ 'text': moment(data.date).format('DD-MM-YYYY') }, { 'text': data.time }, { 'text': data.place }] });
conv.add(new Table({
'columns': [{
'header': 'Date',
}, {
'header': 'Time',
}, {
'header': 'Place',
}],
'rows': tablesRows,
}));
For more info visit conversation components
I have 3 containers, say their names are A, B, C.
Their definition, now that I tried to link them together (using the key links), is attached at the end of the question.
How can I test that this linking worked properly?
{
'A': {
'run_args': {
'name': 'A',
'detach': 'True',
'volumes': {
'/var/run/datadog': {'bind': '/var/run/datadog', 'mode': 'rw'}
},
'ports': {'30000/tcp': '30000', '30010/tcp': '30010'},
'restart_policy': {'name': 'always'},
'log_config': {'type': 'syslog'},
'command': A_cmd,
'links': {'B': 'B', 'C': 'C'}
}
},
'B': {
'run_args': {
'name': 'B',
'detach': True,
'volumes': {
'/etc/salt': {'bind': '/etc/salt', 'mode': 'rw'},
'/var/run/datadog': {'bind': '/var/run/datadog', 'mode': 'rw'}
},
'ports': {'8080/tcp': '80'},
'restart_policy': {'name': 'always'},
'command': B_cmd,
'tty': True,
'links': {'A': 'A', 'C': 'C'}
}
},
'C': {
'run_args': {
'name': 'C',
'detach': True,
'volumes': {
'/etc/salt': {'bind': '/etc/salt', 'mode': 'rw'},
'/var/run/datadog': {'bind': '/var/run/datadog', 'mode': 'rw'}
},
'ports': {'33000/tcp': '33000'},
'restart_policy': {'name': 'always'},
'command': C_cmd,
'tty': True,
'links': {'A': 'A', 'B': 'B'}
}
}
}
ping container names from another container. If they are linked then it will respond.
The documentation Combine multiple requests in one HTTP call using JSON batching for sequencing requests with the dependsOn property indicates that not all calls in the sequence needs to be dependent, however, when making the following batch call I receive the error:
BadRequest - Batch should be either fully sequential or fully parallel
'requests': [
{
'id': '1',
'method': 'GET',
'url': '/me/messages?$top=1'
},
{
'id': '2',
'dependsOn': [ '1' ],
'method': 'GET',
'url': '/me/calendar/events?$top=1'
},
{
'id': '3',
'method': 'GET',
'url': 'me/contacts?$top=1'
}
]
You need to add dependsOn to 'id': '3' request too.
Like:
'requests': [
{
'id': '1',
'method': 'GET',
'url': '/me/messages?$top=1'
},
{
'id': '2',
'dependsOn': [ '1' ],
'method': 'GET',
'url': '/me/calendar/events?$top=1'
},
{
'id': '3',
'dependsOn': [ '2' ],
'method': 'GET',
'url': 'me/contacts?$top=1'
}
]
Not sure exactly how to ask this as I am learning as I go. I am creating/updating user information from my app to a third party CRM system.
I have two methods that run successfully with an after_save callback. During testing, I would comment one out so I can test the other but now I need to combine them with an if else statement.
What should happen when combined together is when User is saved, the system will see if a user exists on the CRM system - if agile_id?. If user exists, it will skip down to the update call and send over any updated contact data, but if it doesn't, it will create a new CRM contact record.
The error I am receiving in the browser is:
undefined method `agile_id?' for #<User:0x007ffe24cef318>
user.rb
...
after_save :sync_to_agilecrm
...
def sync_to_agilecrm
agile_id = AgileCRM.request :get, 'contacts/search/email/'+email, nil
if agile_id?
contact_data = {
'properties': [
{ 'type': 'SYSTEM', 'name': 'first_name', 'value': first_name },
{ 'type': 'SYSTEM', 'name': 'last_name', 'value': last_name },
{ 'type': 'SYSTEM', 'name': 'email', 'subtype': 'work', 'value': email },
{ 'type': 'SYSTEM', 'name': 'address', 'value': '{\"address\":\"225 George Street\",\"city\":\"NSW\",\"state\":\"Sydney\",\"zip\":\"2000\",\"country\":\"Australia\"}' },
]
}
parsed_contact_data = JSON.parse(contact_data.to_json)
print(AgileCRM.request :post, 'contacts', parsed_contact_data)
else
update_contact_data = {
'id': agile_id,
'properties': [
{ 'type': 'SYSTEM', 'name': 'first_name', 'value': first_name },
{ 'type': 'SYSTEM', 'name': 'last_name', 'value': last_name },
{ 'type': 'SYSTEM', 'name': 'email', 'subtype': 'work', 'value': email },
{ 'type': 'SYSTEM', 'name': 'address', 'subtype': 'work', 'value': address_line1 },
]
}
parsed_update_contact_data = JSON.parse(update_contact_data.to_json)
print(AgileCRM.request :put, 'contacts/edit-properties', parsed_update_contact_data)
end
end
...
agile_id and agile_id? aren't the same thing. You'll sometimes see ActiveRecord objects which have record.attribute? which is enabled through some meta programming.
So, when defining a variable such as agile_id, adding a question mark on the end won't just work, nor is it needed. a simple if agile_id should be sufficient.
i am working with Ext JS & Rails as backend..i am having myysql database that has parent-child relationship i.e. 1-to-many relationship...I want a "single" JSON store that can be used to load "GRID" data with parent & child records..
Also is there any way to bind both - the form as well as the grid to the same store but having different jsonreaders? i.e. form's datareader would read the string with root: 'customers' and grid will read the string with root :'products'?
The JSON looks like this :
{
"customers": [{
"amt": 8000,
"custnm": "rashmi",
"id": 2,
"purdt": "2011-04-27",
"products": [{
"amt": 40,
"customer_id": 2,
"id": 3,
"prodnm": "oil",
"qty": 1,
"rate": 40
}]
}, {
"amt": 300000,
"custnm": "bhumika",
"id": 3,
"purdt": "2011-04-14",
"products": [{
"amt": 40,
"customer_id": 3,
"id": 1,
"prodnm": "soap",
"qty": 20000,
"rate": 20
}, {
"amt": 150,
"customer_id": 3,
"id": 2,
"prodnm": "shampoo",
"qty": 3000,
"rate": 50
}, {
"amt": null,
"customer_id": 3,
"id": 14,
"prodnm": "aa",
"qty": 2,
"rate": null
}]
}, {
"amt": 15000,
"custnm": "Shruti",
"id": 13,
"purdt": "2011-04-08",
"products": []
}, {
"amt": 200000,
"custnm": "Jayesh",
"id": 14,
"purdt": "2011-03-31",
"products": []
}, {
"amt": 220000,
"custnm": "SHRUTI",
"id": 15,
"purdt": "2011-04-06",
"products": []
}, {
"amt": 10000,
"custnm": "RASHMI",
"id": 24,
"purdt": "2011-04-06",
"products": []
}],
"results": 6
}
The new Ext JS 4 has Model associations which will solve the problem (http://docs.sencha.com/ext-js/4-0/guide/data)
// each User hasMany Orders
Ext.define('User', {
extend: 'Ext.data.Model',
fields: ['id', 'name', 'email'],
proxy : {
type: 'rest',
url : '/users',
reader: 'json'
},
hasMany: 'Orders'
});
// each Order belongsTo a User, and hasMany OrderItems
Ext.define('Order', {
extend: 'Ext.data.Model',
fields: ['id', 'user_id', 'status'],
belongsTo: 'User',
hasMany: 'OrderItems'
});
// each OrderItem belongsTo an Order
Ext.define('OrderItem', {
extend: 'Ext.data.Model',
fields: ['id', 'order_id', 'name', 'description', 'price', 'quantity'],
belongsTo: 'Order'
});
Calling user.orders() returns a Store configured with the Orders model, because the User model defined an association of hasMany: 'Orders'.
User.load(123, {
success: function(user) {
//we can iterate over the orders easily using the Associations API
user.orders().each(function(order) {
console.log(order.get('status'));
//we can even iterate over each Order's OrderItems:
order.orderItems().each(function(orderItem) {
console.log(orderItem.get('title'));
});
});
}
});
You can use override the default rendering method. Define a renderer method for the column.
renderer: function(value, metaData, record, rowIndex, colIndex, store) {
// you can use record.get method to access the data you require
return the_retrived_data;
}
You need to use mapping property to map the fields of your json store to the nested json. For example, if you need to access product name you can have:
{name: 'productname', mapping: 'products.prodnm'}
You can also use the array notation instead of the dot operator. ex: "products['prodnm']".