Using query in iframe

I wasn’t able to upload all pictures and links. Please see this link for the full post:
https://demo.hedgedoc.org/s/tzrcc1gYg

I am trying to make a list widget of items where each item has a category, and the list should sort and give a header above the first item of each category.

Out of the box, it doesn’t seem like appsmith can do this, and as I am really evaluating the platform right now, I want to try this iframe implementation, as it would be very useful to me in the future.

Can anyone point me in the right direction as I can’t figure out how to make this work.

Here is what I have done thus far:

  • I have set up a page here with some basic crud queries: Appsmith

  • I’ve connected to my Postgres and all is working well.

  • I’ve created a simple html/js page that builds the list I speak of via a json object

  • all is well so far, but now when I try to replace the hardcoded json with a query, it comes in with a bunch of backslashes and brackets and I’ve tried a few JS scripts but can’t seem to get the incoming data formated right to work.

    • MEDIA ITEM OMITTED
  • I’ve tried some things like LINK OMMITED, or LINK OMMITED

.
.
.

  • Additionally, the last step of my implementation is to make a button in each of the list items to edit that row in the DB. As there is obviously no way to get the currentItem or similar within this, I am looking if there is an alternative. Can I add a button to each of these list items in my HTML that saves an appsmith variable depending on which list item the button belongs to. i.e. set appsmith variable to the row ID of the
    • MEDIA ITEM OMITTED

.
.
.
.

Source HTML doc


<div></div>


<script>

const data = [
  {
    "id": 2,
    "name": "item2",
    "description": "desc2",
    "price": "2",
    "category": "food",
    "is_deleted": false,
    "order": 2,
    "img":"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQF0LjrsgkVi79EIFUmXABwDTKKS0go6rn-S1xDIU7Tmo_UecYTu81-v6nfNHFrwbZ0YcA&usqp=CAU"
  },
  {
    "id": 3,
    "name": "item3",
    "description": "desc3",
    "price": "3",
    "category": "food",
    "is_deleted": false,
    "order": 3,
    "img":"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQF0LjrsgkVi79EIFUmXABwDTKKS0go6rn-S1xDIU7Tmo_UecYTu81-v6nfNHFrwbZ0YcA&usqp=CAU"
  },
  {
    "id": 1,
    "name": "item1",
    "description": "desc1",
    "price": "1",
    "category": "drinks",
    "is_deleted": false,
    "order": 4,
    "img":"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQF0LjrsgkVi79EIFUmXABwDTKKS0go6rn-S1xDIU7Tmo_UecYTu81-v6nfNHFrwbZ0YcA&usqp=CAU"
  },
    {
    "id": 4,
    "name": "item4",
    "description": "desc4",
    "price": "7",
    "category": "Grains",
    "is_deleted": false,
    "order": 4,
    "img":"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQF0LjrsgkVi79EIFUmXABwDTKKS0go6rn-S1xDIU7Tmo_UecYTu81-v6nfNHFrwbZ0YcA&usqp=CAU"
  }
];

data.sort((a, b) => a.category.localeCompare(b.category));


function groupBy(array, property) {
    var hash = {};
    for (var i = 0; i < array.length; i++) {
        if (!hash[array[i][property]]) hash[array[i][property]] = [];
        hash[array[i][property]].push(array[i]);
    }
    return hash;
}


const categoryData = groupBy(data,"category");

let div = document.createElement("div");
div.style.width = "90%";
div.style.height = "100vh";
div.style.marginTop = "0";
div.style.marginBottom = "0";
div.style.marginLeft = "auto";
div.style.marginRight = "auto";
div.id = "container";
document.body.appendChild(div);

for (const item in categoryData) {
	let h4 = document.createElement("h4");
  h4.innerHTML = item
  div.appendChild(h4);
  
  for(const data of categoryData[item]){
  
  let holder = document.createElement("div");
	holder.style.display = "flex";
  holder.style.marginBottom = '1rem'
  holder.style.justifyContent = "space-between"
  holder.id = "holder";
  div.appendChild(holder);
  
  let leftSection = document.createElement("div");
	leftSection.style.display = "flex";
  leftSection.id = "left-section";
  holder.appendChild(leftSection);
  
  let rightSection = document.createElement("div");
	rightSection.style.display = "flex";
  rightSection.style.alignItems = "center"
  rightSection.id = "right-section";
  rightSection.innerHTML = data.price
  holder.appendChild(rightSection);
  
  let col1 = document.createElement("div");
  leftSection.id = "left-section";
  leftSection.appendChild(col1);
  
  let img = document.createElement("img");
  img.style.widht = "100px";
  img.style.height = "100px"
  img.src = data.img;
  col1.appendChild(img);
  
  let col2 = document.createElement("div");
	col2.style.marginLeft = "1.5rem";
  leftSection.appendChild(col2);
  	
  let name = document.createElement("h4");
  name.innerHTML = data.name
  col2.appendChild(name);  
  
   let desc = document.createElement("h4");
  desc.innerHTML = data.description
  col2.appendChild(desc);  
   
  
  }
  
}


</script>

@jm5522 Have you tried JSON.parse on the query data?

And for your second query regarding the Edit button, you could check out the storeValue function - Store Value - Appsmith. Haven’t tried this though. Interesting usecase

Awesome!! First part is working. I think I was just missing some single quotes…

Thank you!

I am a bit familiar with the appmith stored values, so I will try to implement something now that I have the first part working.

https://app.appsmith.com/applications/61fd8aad2cd3d95ca414c9f6/pages/61fd8aad2cd3d95ca414c9f9


<div></div>


<script>


const data = 
  
  
JSON.parse('{{SelectQuery.data}}');


data.sort((a, b) => a.category.localeCompare(b.category));


function groupBy(array, property) {
    var hash = {};
    for (var i = 0; i < array.length; i++) {
        if (!hash[array[i][property]]) hash[array[i][property]] = [];
        hash[array[i][property]].push(array[i]);
    }
    return hash;
}


const categoryData = groupBy(data,"category");

let div = document.createElement("div");
div.style.width = "90%";
div.style.height = "100vh";
div.style.marginTop = "0";
div.style.marginBottom = "0";
div.style.marginLeft = "auto";
div.style.marginRight = "auto";
div.id = "container";
document.body.appendChild(div);

for (const item in categoryData) {
	let h4 = document.createElement("h4");
  h4.innerHTML = item
  div.appendChild(h4);
  
  for(const data of categoryData[item]){
  
  let holder = document.createElement("div");
	holder.style.display = "flex";
  holder.style.marginBottom = '1rem'
  holder.style.justifyContent = "space-between"
  holder.id = "holder";
  div.appendChild(holder);
  
  let leftSection = document.createElement("div");
	leftSection.style.display = "flex";
  leftSection.id = "left-section";
  holder.appendChild(leftSection);
  
  let rightSection = document.createElement("div");
	rightSection.style.display = "flex";
  rightSection.style.alignItems = "center"
  rightSection.id = "right-section";
  rightSection.innerHTML = data.price
  holder.appendChild(rightSection);
  
  let col1 = document.createElement("div");
  leftSection.id = "left-section";
  leftSection.appendChild(col1);
  
  let img = document.createElement("img");
  img.style.widht = "100px";
  img.style.height = "100px"
  img.src = data.img;
  col1.appendChild(img);
  
  let col2 = document.createElement("div");
	col2.style.marginLeft = "1.5rem";
  leftSection.appendChild(col2);
  	
  let name = document.createElement("h4");
  name.innerHTML = data.name
  col2.appendChild(name);  
  
   let desc = document.createElement("h4");
  desc.innerHTML = data.description
  col2.appendChild(desc);  
   
  
  }
  
}


</script>```

I am close, but now I am getting this error about calling an async function in a sync field. Is there any way around this? Or any other clever ways to get the row ID of the row that I am clicking the button on?

I am also not able to access the data.id of the current row being iterated within the moustache tags.

By the way I just wanted to follow up and post the final solution here… We had to make sure any JS in our frame was synchronous.

Still it would be great to have a more robust method of creating custom html/css/js widgets in a page than the current feature set and limitations around the src iframe method. But alas, this is a GREAT start. Just testing all this out, but if the custom widgets using an iframe work, then it should be a lot easier to work around limitations. Great work.

Here is the Appsmith app:
https://app.appsmith.com/applications/61fd8aad2cd3d95ca414c9f6/pages/61fe93592cd3d95ca414cfe7

Here is the srcDoc code:




<div></div>


<script>


const data = 
  
  
JSON.parse('{{SelectQuery.data}}');


data.sort((a, b) => a.category.localeCompare(b.category));


function groupBy(array, property) {
    var hash = {};
    for (var i = 0; i < array.length; i++) {
        if (!hash[array[i][property]]) hash[array[i][property]] = [];
        hash[array[i][property]].push(array[i]);
    }
    return hash;
}


const categoryData = groupBy(data,"category");

let div = document.createElement("div");
div.style.width = "90%";
div.style.height = "100vh";
div.style.marginTop = "0";
div.style.marginBottom = "0";
div.style.marginLeft = "auto";
div.style.marginRight = "auto";
div.id = "container";
document.body.appendChild(div);

for (const item in categoryData) {
	let h4 = document.createElement("h4");
  h4.innerHTML = item
  div.appendChild(h4);
  
  for(const data of categoryData[item]){
  
  let holder = document.createElement("div");
	holder.style.display = "flex";
  holder.style.marginBottom = '1rem'
  holder.style.justifyContent = "space-between"
  holder.id = "holder";
  div.appendChild(holder);
  
  let leftSection = document.createElement("div");
	leftSection.style.display = "flex";
  leftSection.id = "left-section";
  holder.appendChild(leftSection);
  
  let rightSection = document.createElement("div");
	rightSection.style.display = "flex";
  rightSection.style.alignItems = "center"
  rightSection.id = "right-section";
  rightSection.innerHTML =
	'<button onclick="" type="button">Click Me!</button>';
	
	
		
		holder.appendChild(rightSection);
  
  let col1 = document.createElement("div");
  leftSection.id = "left-section";
  leftSection.appendChild(col1);
  
  let img = document.createElement("img");
  img.style.widht = "100px";
  img.style.height = "100px"
  img.src = data.img;
  col1.appendChild(img);
  
  let col2 = document.createElement("div");
	col2.style.marginLeft = "1.5rem";
  leftSection.appendChild(col2);
  	
  let name = document.createElement("h4");
  name.innerHTML = data.name
  col2.appendChild(name);  
  
   let desc = document.createElement("h4");
  desc.innerHTML = data.description
  col2.appendChild(desc);  
   
  
  }
 
}
</script>

@jm5522 this is really incredible! We do want to support custom widgets out of the box so that this solution is much easier to achieve. Thank you so much for sharing this. This is our custom widget feature

I’m sure a lot of folk can benefit from your solution in the interim