FRIDAY TECH WORKSHOP: Building A Custom Widget Request Form With Tag Inputs! ️

FRIDAY TECH WORKSHOP: Building A Custom Widget Request Form With Tag Inputs! ️

Show Video

hey welcome back to another Friday Tech Workshop  I'm Joseph a senior developer Advocate with   Appsmith and if you're new to these workshops  every Friday at 10: a. we're going to have some   type of Workshop whether that's live coding a  community show and tell it could be demoing a new   feature or ask of The A Team where we'll bring on  other team members and go through do Q&A or maybe   look at questions from Discord so tune in every  Friday at 10: a.m. for something different today   what we're going to do is use a custom widget  to build an app to request custom widgets so   if you're not familiar with custom widgets they  came out recently and it's a single widget that   can let you build just about any other widget  we already have over 45 widgets in apps Smith   but occasionally there's a request for something  that is very specific that you know not a lot of   users need so this one widget will let you make  just about anything we've got a backlog of some   uh requests in GitHub for different widgets that  we haven't been able to build yet and this app   that we're going to build today is going to let us  prioritize those and figure out what the community   needs and then either me or somebody else from  the team or even people from the community can   look at these requests see which one has the most  votes and then submit your own custom widget to   the community portal so right now we have uh  templates that the community can submit and   we're working towards being able to uh submit your  own custom Widgets or building blocks so it's not   just templates um will also involve packages and  uh the reusability components that are coming so   lots of cool things on the way there today we're  just going to make a request form app basically   um but since it's for custom widgets we're going  to throw in a custom widget in this request form   so with that in mind uh I wanted to uh just take  a minute to talk about like the back end I'm going   to use and why I chose it uh we we want to be able  to upload some screenshots you know we uh want the   ability for the user requesting a custom widget to  give us more than just text and if they can show a   similar widget or interface from another app and  I I wanted some easy way to insert the images and   a single backend um instead of you know Google  Sheets plus uh an S3 bucket or something so I'm   using bass row because it allows the file uploads  and you don't have to go upload it somewhere else   first um with air table you can also upload files  but if you do it through the API you have to send   a URL you can't just upload the file directly  so with uh Baserow it's similar to air table in   the structure in the use case and whatnot but the  API will let you just upload the file directly so   I'm using that for the backend today I'm going  to skip past setting up a data source because   I've got some other videos out there about that so  check the link in the comments if you're looking   to use Baserow for your data source and want to  see how we got set up here to start so I already   got a connected we're going to jump right into  the build and uh with that in mind if you guys   have any ideas for custom widgets let us know drop  a comment below uh you can comment on the video   here or in the portal or feel free to reach out  to me on Discord all right let's get to building   all right so before we start building the app just  want to walk through the back end real quick I've   set up just a couple of tables for a a request  for a custom widget so like What's the title of   the widget and the description who's requesting it  and then if there's an existing issue um sometimes   it's one that's already in GitHub and we're  going to build a custom widget to solve that   so you could put the link here if there is one  and then some tags and this is what I'm going   to use for the custom widget so we're building a  form and uh we'll use a regular form like widget   but then inside of there I'm going to try using  a custom widget to create this style of tag input   and then we've got a place to put a screenshot  for whatever type of custom widget the user is   requesting they might uh they might have like  another library or like a different app they want   to take a screenshot from to show what what their  end goal is and the rest of this stuff is going   to it won't be in the form it's like follow-up  stuff um so I've got a field that I can set it   active or if it's no longer an open request if we  close it out you know then I can filter it out um   and then there is somebody can submit a widget  to uh to complete that request and so the widget   submission table here will let somebody uh upload  you know a link to their custom widget and which   request are they filling here so I have the the  one request um from the request table is selected   and this particular request I chose because it's  actually um there's already a widget for it and so   I've got a record that I can have in the request  table and in the table that's showing somebody's   completed it so uh BAGI an engineer from our team  here has created this Json editor uh Json custom   widget and so this takes care of this request I'm  I'm building this data on um this app on top of   some data here where I've got one good uh request  one submission and then votes so I've got a   separate table where people can vote on a request  you might uh you might go in there to enter yours   and you see the request has already been made so  you can just upload it so I've got those three   tables that we're going to build on top of um and  I have already connected the data source and just   set up a single query so I'm getting back that  one row for the request and we're going to build   everything else uh I skip past creating the data  source because there's already a couple videos on   that for bass row so if you need a hand setting up  your base rad data source just check out the link   in the description we we have another video on  that and uh so the UI here I've got an empty app   the only thing I have is this one query pulling  back the one table for requests and we'll go ahead   and start building on top of that real quick I  want to show um the custom widget that I'm going   to use is a tag input and this is already built  as well this um tag input I just submitted this   template to the Community Library so this is a  form this whole Outer container is a regular form   widget but this one input here is a custom widget  with tags so like rest apis is in the autocomplete   list here and graphql and so um you can Define  the list that you want to pre-populate this as   the uh drop- down values and this is already  built I'm just going to copy this custom widget   and we'll build on top of that and just like the  Bas row data source thing I'm using a few existing   things that we've already done videos on so we're  just going to going to jump into the build and uh   again check out the link in the description for  this template and for the Baserow data source   so we'll go ahead and get started with the build  then and uh also real quick I want to show this uh   library that I'm using here this is coming from  um that's not the one here we go uh J cubic on   GitHub this is a tagger library from J cubic and  uh just wanted to give him a shout out and thanks   for making this open source it's a really cool  library and it's given us a quick easy way to make   this tag input in Appsmith All right so I'm going  to start out with my blank app here I've got some   data coming back and we'll go ahead and connect  this to a table uh we could drag in a table widget   but there's this assistive binding here uh you  can just choose what type of widget you want to   connect this data to and you'll notice that the  data coming back it's an object it's not an array   of Records directly it's an object first and then  there's this results array inside of it so when   you go to connect your data you have to reference  the right part of that um this assist of assisted   binding does that for you it knows that there's uh  data do results so it just makes that connection   for you so we don't need to see the IDS here I'm  going to turn those off and so this would be where   you could vote um the table here I think we're  going to need a form for submitting a new one and   um I'm going to put that in a modal so what I'm  going to do is drag in the button for adding a new one and then we'll say that the onclick  event is to open a modal show modal and   we don't have a modal yet so you can make  a new one right here and it connects it   so this modal is where we're going  to put our form um and to go ahead   and get rid of these buttons because  we'll have our own buttons from the form so I'm using a regular form widget instead  of a Json form because we have that one field   for the tag inputs and I'm going to copy both  these inputs and the tag here even the buttons   I think just going to copy all of that and put  it inside of my form which is inside of the   modal um get rid of these because I just copied  some buttons all right so this form is going to   be to submit uh a new request you know for a  new custom widget so we'll have the the name   of it there was a description um going to change  this date picker we need the description field what am I doing I want the input  and then we'll set it to a long text okay so this will be the description and then this custom widget  here is our input for the tags um I think   that field is called Tags over here let me double check yeah so we we'll have a custom widget to  supply these values and let us pick from that um   now let's start a query to create a new record so  that we can connect it to the submit button and I   want to get at least the name and the description  and these tags working uh we may not add all the   fields during this video but basically uh I want  to at least get a new record created using this   custom widget as an input and then we can go  on to like uh the voting part maybe so we'll   see how much we can get done in an hour here  okay let's check out the Baserow API docs to   get the right format uh for the body here to  create a new row so we're sending a request to   uh the the table for requests and so in this uh  custom widget request table if we want to create   a new row this is the expected structure here  so the API endpoint is going to be zoom out a   little here so you can see all of it uh create  row here it is after SL API it's database rows   table whichever table number and then this user  field names equals true that's um so that you   can reference everything by those names that  you've given it instead of the ones that are   coming from the uh back end that's like just a  random ID so this is the right Endo now I need the body and you can also copy the entire thing  here and import import the curl request um   but I've already got a data source created and  so I want to make the API underneath that data   source so here's the the body this is what  we would send if we were doing all of the fields paste this in here and this is just the the  raw Json right now um so I can't reference and use   JavaScript in here yet so I'm going to select all  of this and just wrap it in double curly braces so   that it'll evaluate and I can write you know and  reference different things inside of this so all   right uh we'll skip the screenshots for now we are  going to get to the file upload here in a little bit and then votes tags that's what we want so I'll  leave out the GitHub issue for now and we   want to send an array of tags from that custom  widget um we have a couple inputs that we can   connect here which I should go name those before  we reference them uh because it's you know you're   trying to remember which one's input one or  input two so I like to use a prefix um always   three characters underscore and then whatever the  purpose or the use case is here so this is the name this helps with autocomplete because you  can just start typing the type of widget and   then you'll get a list of just your inputs you  know as long as you're consistent with with your prefix so then we have this custom widget for the tags and now I can reference those in the query  so the title here is coming from our input name. text the description has an input um I could go ahead and set this to True  automatically as they're created or just   leave it out and it's uh then I would have  to enable it before it shows up that's the   idea is just to have a way to filter them out  so that uh the table isn't showing ones that   have been completed so we'll go ahead and  and leave it active automatically but it   doesn't need to be on the form we'll just  uh add that value in ourselves same thing   with the email here I'm going to reference the  Appsmith user and we can get their email email   um okay so the tags let's check out the  custom widget um I kind of skipped over   this because the widget's already built it's  out there it's in the community library but   just real quick let's take a look at how it  works so I'm passing in an array a list of   tags that I want in the autocomplete list and  then this custom widget if you go to the edit source so there's not much the HTML uh it Imports  a script and uh I'm using tagger and then there's   also some CSS that goes with that and then just  an outer div for the whole widget one for the   label and a input so not a lot going on there um  I added some styling that uses the variables to   get the radius and the colors from the apps Smith  theme so it'll change with your app um and so the   Mo majority of the codes you know the JavaScript  but it's still pretty simple so you identify the   you know the tag the uh the component or the  HTML element that you want to use for your   input and then on ready when app Smith is ready  you have this function that creates a new tagger   instance so uh VAR tags is and then tagger is  a method that's available because we imported   this script here and it's uh input is the item  you know which element are we initializing here   and the rest of that's just config uh you know  it's not a a function or anything this is just   saying here's the setup options and that's all  based on the the repo that I showed a minute ago   from tagger so here I'm passing in the list of  tags from the the model so outside of the custom   widget editor there's this place to pass data in  and also read it back out and then when the input   is updated here it's going to save that value back  to selected tags so it's coming in as tags just   the available ones and when it gets edited then  it gets saved to selected tags so let's try that   out and if I put a value in there it's updated the  model and now there's this selected tags property   and it's got both values but it's a single string  um because it's in inside HTML element it's just   the value of an input right so it has to be a  string or a number um so let's look back here   and if I go ahead and edit it from this side  now we'll see that the um the model this is the   default model just what it gets initialized with  but it's not the live model so if we go back to   the query now here's an array we need to replace  this with um an array coming from that string Ren   so there's custom tags that's our custom widget it  has a model and inside of there there's the tags   array that I'm supplying and then the selected  tags of the string which gets returned we're   going to take that string and split it by commas  and that should give us an array and so if I look   at the evaluated value here you can see that the  tags property it's getting that string splitting   and now we have an array so I think we got  just about everything we could run this and   see if it creates a row um but what I want to do  is connect it to the UI so we've got this submit   button and I want that to First create the record  so create a request if that's successful we want   to see the results in the table so we have to  get the entire list again so we'll get requests   and one final call back here just to close the  modal and that is modal one I haven't named it yet so now we should be able to create  a new um request here see if the tag   part works all right so we have one  request got One queued up here let's   see what happens ah first error got  two errors so let's check out the logs here that's it okay and just in case there's nothing in  there I'm going to use optional chaining so   this doesn't give me an error when  there's no value so you can see that   there's a single value value there is  no comma to split by and it gives me an   array of just the one value and then  if I come back and we'll add another value and it's splitting that string and now  we have an array with two values so it's almost   ready I think I could send a request right  now and it would it would try but it's going   to fail because these are not the right values  um I need to pass in the values that are allow   for this one field here in my tag inputs and I I  created a couple values just based on the editor   when you're creating a new widget there's there's  sections here for um each type of widget you know   there's inputs and buttons and so if you're  building a custom widget what type would it   fall under um just as an example something to  use this U this tag input so let's take what   we're we're going to need to do is make a new  query to get those values um I could hardcode   it and you know copy and paste all the values  from the other um you know just by looking at   it from here but I want them to be dynamic so if  I change it from here I want the app to be able   to read that new drop down option and make it  a new value in the custom widget so to do that   we can use the fields endpoint instead of listing  um rows we want to list all the fields and inside   of these fields let's see is it this one or  this one here we are so this endpoint gives   us the definition of the back end and you know  what are these allowed values so I think we can   copy that request where is it right here  that's the that's the authentication one so we want to get the fields um it's  API database Fields whereas here   it's database rows right so same  table but we're going to get the fields so what I'll do is make  another git request on that same data source and we'll get the list of tags let's see if this is working so this gives us  all the fields and in one of those is that array   of the options that are allowed here we are select  options so it's a multi multiple select field with   select options and then inside of there is our  list of inputs or buttons or whatever so before   I can connect this API and make it Dynamic to  feed that um we got to drill down to this one   field find that array map over it and get just  the values so I'm going to add a new Js object now and this will be a regular function not a async so first we'll just return the  G tags. data and we want to I'll just  

return it all directly just so we see  what we're starting with so this is an   array and I want to find the item the first  um element in this list where the name equals tags so do find this will return a  single object for the first one that   matches a condition we want to look at all  of the fields um so a single one would be   a field and for each field if the field.  name equals tags we want to return that item so now it's just a single object  instead of the array of fields but I   want to get down to this select options  array almost there so now we'll return dot tags let's see that's the object type I don't why am I not seeing  the tags field let run this again select   options yeah that's the next level  select options and then map over the array okay now I'm getting just the array so now that  I have the list of fields uh rather the uh list   of values allowed for this field now I can map  over this one more time and get just the values   um it's getting a little long so I'm not going  to return this just yet we'll call this uh tag field it's actually the options and then we'll return that array mapped and we want to  take each option and return the option. value all right so now we have an array of  the actual values coming straight from the   back end so if I go changing and adding a whole  new Option here or rename one or even change the   color those values are getting sent to apps  Smith so we can use it so now what we'll do   is I want this to run on page load make sure  we always have that list um actually that's   the function we also want the query that gets  the tag so we want this to run on page loadad and now we can swap out this hardcoded spot and  we'll reference our jsobject 1.g tags and then  

this evaluated value here shows that it's taking  the response from the API uh filtering it for   that one field then mapping over the options  and giving us just the values so it's still   prepopulated with databases and that's part  of the custom widget code we'll go edit that source and that's just part of the  example um that I copied it from so   the input here it's default value  or when it's first initialized um   it had that example in there I don't  want that for this use case so I think   we're about there this should be able to  work and create a record now so we'll try adding a couple values here um input yep uh media  I think was an option there we go and so when I   submit I want this to um I want this button to  First create the record and then get all of the   records to fill the table so we can see it and  also close the models because a table behind this   so this button we'll do is the onclick its first  action is to create uh I've already got the create   API created um then I want to run the git requests  so I can see the value and there's this final call   back here to close the model so let's test this  out and no errors it closed the modal and I see   the second row right there cool so uh we're able  to add a row it's got the two text Fields um this   active thing is is auto stamped uh the creation  time that's like a field automatically set in   base row and it looks like let's see did our tags  work it did so we should have some tags on this   new row here oh I'm on the boats yes it worked uh  cool so we can use a custom widget inside a form   and capture those tag values so that it looks like  tags in appmi uh and then send it to Basse row to   another field that's also displaying like tags so  I think that's good for the submission form um as   far as like the regular Fields so next I want to  get this um screenshot to upload and so with bass   row you can upload files directly through the  API um air table does not support that if you   want to upload a file you have to do it through  air table's website uh if you do it through the   API you can only upload a file if you have the URL  which means you've already uploaded it somewhere   else first like S3 or whatever um so what we're  going to do is upload the file directly to the   files endpoint here and these are different  tables um in a work space and then the file   upload it's not part of a single table it's just  the whole workspace um once you get that response   it's going to give you back uh a name property  here and then you can use that name put it into   a row when you do a create or an update over  here and that one field for the screenshots um   you can send it the name um just the name and it  will link up with that file so that's what we're   going to do next we'll we'll use this file upload  endpoint so it's API SL user files and then upload file and we're going to let see we  should add a file picker to here as well so I'll grab a file picker widget and this will let us upload our screenshots all right I'm going to create  another query under the same data source a   post request and that was Slash API user fields  or user files and then upload file um and then   for the body let's look at the API docs here so  it says multiart and file so we're going to set   this to multiart uh the key is file and it is a  file right we're sending you could send text or   whatever uh data format here we're going to  send an actual file with the key of file and   then we want to reference the file picker here  the files array and we're only going to upload   one so it would be the first element there in the  array and that's just about it I think this file   picker um needs to be set to Binary based on U  Bas Row's API it just depends on where you're   sending it but for this particular one um you  want to use multi-art form uh a file sent with   that key of file and as as binary so let's see  what we once we attach a file here we need it   to be uploaded to base row get back that name and  the URL so that we can attach it to air table and   we just need to have the URL ready so that we  can add it to the form when we submit so this   file picker is going to do its own upload and get  the result and then we'll use that value in this   API when we submit the whole thing so let's  go look at the file here um I need to name this so if we upload a file we should be able to  reference that link here when we go to create um   sorry we're creating a record here we go and  so right here is the other field we want to add can't remember if that was plural yes so screenshots it's looking for an  array um with an object that has a name   property and then the value of that name  is going to come from the upload file I   have not ran this yet so there is no data but  inside of there there's going to be a name so   let's go ahead and and run it so we can get  that value and have something extra should to   bind right here um because it it doesn't  know what's going to be inside data yet so do test three add some tags and I'll attach a file all right so once this gets uploaded  um I want the action of uploading the file   right here to run the API and get that URL  for us so on the file picker I'm going to   set the on file selected to just to upload  the file we don't want to take the name and   add it to the row yet we just want to kind  of have that value on deck and ready to use   it so I need to clear out that file I just  added because there was no trigger yet let   me start over and this should upload the image  now just the file itself but not the whole um   row oh we got so it failed but it did attempt  there so let's check out the query uh upload file all right um and now it works okay maybe the file wasn't uh  being read that first time because I refreshed I   don't know so it uploaded that's uh what I was  expecting it just didn't do it the first time   um there it is the name so there's like lots  of different URLs that come back here's the   thumbnail and then that's an object um but what  we what we want is the name here and to save that   back to a row so that's on the create request  go back to the body screenshots and so after   upload file runs there's a DAT dotame and we're  going to send an object with a name property in   an array you could send more than one um but  we're just going to do the one so it's just   about there let's see when you upload a file  when you insert it into the file picker that's   going to get the name it'll upload it and get that  value back so it's ready for us and then here the   submit I think I've already got that linked and  working so yeah we're ready to test it out Let me refresh all right so let's go ahead and try  test four maybe I don't know where we're at so   this time we will see if the file upload works it  should upload the file to air table and get that   link right away and so you'll see that loading  that worked and so now can we create a row with this okay no error and there it is so we got our  file to upload it created a new row we can use the   tag input so pretty much got everything working  that I wanted here on the form side um got like   another text field or two I could add but I think  I want to go on to the voting part now and like   submitting a a widget so got uh only about 10  15 minutes to go here so we'll see how far we   can get uh let's let's work on voting so I made  a separate table for votes because I don't want   somebody to be able to vote more than once or  delete somebody else El's vote you know they   should be able to remove their vote but not  somebody else's um and I thought about just   having a column for vote just a number and then  you click a button and it increases that number   but then there's no way to track who added it  you know so we've got a separate votes table   and it's just the time it came in which request  uh is this vote for and who submitted it so that   I can only allow you to edit your own so first  we'll do a query to get all the votes and we want   to be able to um have a button here that lets  you vote on something but only if you haven't   voted on it yet so I'm going to add the button  We'll add a new column to the table call it up vote and I'm going to make it an icon button uh we don't have the query yet but  I want to put this where I can see   it so I'm dragging this button to  the top here or to the left on the table okay and when we click this  button it should take the row that   we're on and our email and create a new  vote so let's go to the new query this   will also be a post request to create  a a new record so we'll go to the vote   table here and if we wanted to create  a row in this table then it's this end point uh post request okay so we'll copy this from the docs here and  into the body so it's a request uh it's an array   but we're only going to have one and then the  one value that we're sending here is going to   be the selected Row from the table so I can go to  oh I got to wrap all this in double curly braces   so that I can reference the widgets and then  right here we'll do table one. selected row um   actually triggered row because they're clicking a  button and that row might not even be selected so   triggered row. that's the request that they're  wanting to up vote and then we'll get their email and that should be it that will create  an upvote um we also want to oh this should   be a post and then we want to get all  of the votes so I'm going to copy this API and we'll make this one get votes um I can take out the body because  I copied that from a post so now we have   an API to get the list of votes and one  to create a new vote so next let's make   that button actually create a vote and then  get all the votes again so we can see it um   also want a column here that um where you can  see the number of votes and there is this uh   relationship column from base row so it's  an array of objects representing each vat   we just want to see that as a number um  um might just be able to get the length here yeah that works um and we'll say that's a number so now on this button uh back out of  that column and go to the up vote button here   I'm going to make the onclick run our new  create vote query and on success get votes um I don't even know if I need to get  the votes or even need a separate API   for it I need to get this data again  because it has the relationship in it   um and I and I'm counting it right  here so after I add a vote I want   to get requests again that should work okay  this one has one vote um I'll vote on this one cool that works but it is going to let me  do it again and I don't want people to be able   to vote on the same one twice so the next thing  is I have to disable this button under certain   conditions if that person has already voted  on that particular request so let's work on   the disabled State here uh we'll make this JS  and I got a feeling this will end up being too   much JS to put right here but I want to do the  at least the first condition and see see how   much we can do here so we're going to look  at the current row uh for each one of these   buttons is the current row ID equal to um and  then we want to see if it's like if if any of   the votes have this ID um and we'll disable it  if it finds a match so actually I think I want   to start with the votes and then we'll put  this inside of a a find or a map um we have   get votes. dat inside of there we'll look at the  results array and then let's see if we can find a

vote where the vote dot which request it goes  with which is an array so if the first request   because there should only be one if its ID equals  the current row do ID that would be the condition   so if it finds something here um I want the button  to be disabled this is returning an object instead   of true or false but if it does return an object  I can negate it and turn something that's truthy   into two false and then negate it one more  time and now it's actually true or false so   since there's a vote for this one it's disabled  um that one should be disabled too why is it not   doing both so if it looks through and finds  a request where the first one's ID is equal   to the current row ID that looks right the other  condition would be that um the request Z do user   or email or something is going to be yours request  it's just email so the other condition here is and the vote. request first element no vote. email equals and  and then we'll say the appmi user. email all right so if it finds any object in the  whole list of votes um where the vote request   that it goes to is this the one that we're  on here then if that's true and it's your   email then that button should be disabled um I  could make a toggle button so that it like unv   maybe but that would get kind of complicated  so I think I'll just have a separate button   to delete your votes so this is like a list  of uh the requests and it is showing how many   votes it has and it's not letting me vote  on that one why is it still letting me do   this one that one should be disabled too let's  look at the back end getting rid of this test junk okay so let's refresh that and try one from  the start here so this already has a vote there   it is it took a second and then it showed disabled  um I think maybe the query to get new votes just   hadn't ran that's what it is so once I do a vote  I do have to to run that query so it has so it   can look that uh that find my condition that I  just added to Loop through and decide if this is   disabled it has to be up to date um with all the  votes which means right here when I create when   I do the upvote I need to get the requests and  get the votes um so I'll have one more call back here to get votes. run that  should work so now let's add  

a new request and then I'll vote on  it and I should only get the vote once okay let's see what happens okay it added the new row got the image working so now I can vote on this new one yes  and now it's disabled so uh we're about out of   time I'm going to end up adding a button to  delete your own vote um maybe like some more   work on the UI and whatnot but I think we got  everything working that uh you know functional   wise you can create a row you can upload a  file and the tags are working so that was   the main thing I wanted to work on today um  One More Time wanted to give a shout out to   uh J cubic on GitHub for making this tagger  uh input library that I used um it's really   cool to be able to add this feature to our apps  and make it in a easy way that people can just   copy this custom widget so uh thanks again for  making that open source thanks for joining the   workshop today and look forward to seeing you  next week let us know where you're interested   in for the next Workshop feel free to uh comment  below or reach out on Discord and uh just give   us some ideas for the next event it could be a  community show in tell or a a Q&A with the team   so kind of an open mix to different topics  for these workshops but every Friday at 10:   a.m. eastern we'll have something uh so we will  see you next week thanks again and don't forget to [Music] subscribe

2024-03-13 13:54

Show Video

Other news