View this page in the latest version of Appian. Try our pilot Ask AI feature to get answers from both Docs and the Community Knowledge Base, then share your feedback Upload Multiple Documents and Edit Document Properties Share Share via LinkedIn Reddit Email Copy Link Print On This Page Tip: Interface patterns give you an opportunity to explore different interface designs. Be sure to check out How to Adapt a Pattern for Your Application. Goal Build a form that allows users to create a record and upload related documents. Users can edit the document name and add a document description before submitting the form. Documents are often related to other application data, like system logs attached to a specific support case, so this recipe illustrates how to create a support case and related documents in a wizard layout. Elements of this recipe This recipe contains a wizard with two steps: Step 1: Create or update a case title and description. This information is written to the SCA Case record type. Step 2: Upload related case documents and edit the document properties as needed. This information is written to the SCA Case Document record type, which is a document management record type. In step 2 of the wizard, each uploaded document will be displayed in a card with the option to show or hide the document properties. Users can edit the uploaded document's name and add a description before submitting the form. Tip: While this recipe shows you how to create a custom card layout where users can edit properties inline, you could use the documentActions parameter in the file upload component instead to allow users to edit document properties in a dialog. Example data model To get the most out of the recipe, it helps to understand how the data is related. The expression below references the following record types: SCA Case record type: Contains support cases submitted by customers. SCA Case Document record type: Manages the documents associated with each support case. SCA Document Type record type: Contains a list of document types that can be uploaded with a support case. This lookup record type allows you to categorize the different documents by type in your queries and interfaces. For example, system logs, screenshots, or invoices. Document record type: Contains information about all documents managed by record types in your environment. The Case record type has a relationship to the Case Document record type, and the Case Document record type has a relationship to the Case, Document Type, and Document record types. Rule inputs This expression uses the following rule inputs to store user inputs and pass information to a process model: Rule Input Name Type Description case Case record type Stores the user input provided in the Details step of the wizard, and the Related Documents step in the wizard.Since Case has a one-to-many relationship with Case Document, we can use this rule input to store data for both record types. isUpdate Boolean Determines if the form is used to create or update a support case.When true, the form will use "Update" instead of "Create" in the header and button, and display the existing information in the form inputs. cancel Boolean Stores true or false to indicate if the action was canceled. Expression 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 a!wizardLayout( titleBar: a!headerTemplateFull( title: if( ri!isUpdate, "Update Case", "Create Case" ), secondaryText: if( ri!isUpdate, "Update case details and documents", "Provide case details and related documents" ) ), isTitleBarFixed: true, showTitleBarDivider: false, backgroundColor: "#F6F7F9", style: "DOT_VERTICAL", steps: { /* Step 1 in Wizard: Add case information */ a!wizardStep( label: "Details", contents: { a!cardLayout( contents: { a!textField( label: "Title", labelPosition: "ABOVE", value: ri!case[recordType!SCA Case.fields.title], saveInto: ri!case[recordType!SCA Case.fields.title], refreshAfter: "UNFOCUS", required: true, ), a!paragraphField( label: "Description", labelPosition: "ABOVE", value: ri!case[recordType!SCA Case.fields.description], saveInto: ri!case[recordType!SCA Case.fields.description], characterLimit: 4000, height: "TALL", required: true ) }, style: "NONE", shape: "SEMI_ROUNDED", padding: "MORE", marginBelow: "STANDARD" ) } ), /* Step 2 in Wizard: Upload related documents */ a!wizardStep( label: "Case Documents", contents: { a!cardLayout( contents: { a!localVariables( local!newFilesAddedInEdit: {}, { a!fileUploadField( labelPosition: "COLLAPSED", target: recordType!SCA Case Document, maxSelections: null(), saveInto: { if( ri!isUpdate = true(), a!save( local!newFilesAddedInEdit, /*Check to see if the uploaded document already exists in the document record type.*/ /*Results in a list of unique new documents uploaded*/ difference( tointeger(save!value), tointeger( ri!case[recordType!SCA Case.relationships.caseDocuments][recordType!SCA Case Document.fields.documentId] ) ) ), null() ), a!save( ri!case, a!update( ri!case, recordType!SCA Case.relationships.caseDocuments, append( ri!case[recordType!SCA Case.relationships.caseDocuments], a!forEach( items: /*List of unique new documents*/ difference( tointeger(save!value), tointeger(ri!case[recordType!SCA Case.relationships.caseDocuments][recordType!SCA Case Document.fields.documentId] ) ), expression: /*Cast list of unique document ids to document record type*/ { recordType!SCA Case Document( recordType!SCA Case Document.fields.documentId: fv!item, recordType!SCA Case Document.relationships.documentProperties: recordType!Document( recordType!Document.fields.name: document(fv!item, "name"), recordType!Document.fields.description: document(fv!item, "description") ) ) } ) ) ) ) } ), a!forEach( items: ri!case[recordType!SCA Case.relationships.caseDocuments], expression: a!localVariables( local!expandProperties: false(), local!isNewlyUploaded: contains( tointeger(local!newFilesAddedInEdit), tointeger( fv!item[recordType!SCA Case Document.fields.documentId] ) ), local!isUpdatedFile: false(), a!cardLayout( contents: { a!sideBySideLayout( items: { a!sideBySideItem( item: a!fileUploadField( label: "Document", labelPosition: if( a!isNullOrEmpty( fv!item[recordType!SCA Case Document.fields.documentId] ), "ABOVE", "COLLAPSED" ), target: recordType: recordType!SCA Case Document, fileNames: fv!item[recordType!SCA Case Document.relationships.documentProperties][recordType!Document.fields.name], fileDescriptions: fv!item[recordType!SCA Case Document.relationships.documentProperties][recordType!Document.fields.description], maxSelections: 1, value: fv!item[recordType!SCA Case Document.fields.documentId], saveInto: { if( ri!isUpdate = true(), if( a!isNotNullOrEmpty( fv!item[recordType!SCA Case Document.fields.documentId] ), { a!save( fv!item[recordType!SCA Case Document.fields.documentId], null() ), a!save(local!isUpdatedFile, true()) }, { a!save( fv!item[recordType!SCA Case Document.fields.documentId], save!value ), a!save( fv!item[recordType!SCA Case Document.relationships.documentProperties][recordType!Document.fields.name], document(save!value[1], "name") ), a!save( fv!item[recordType!SCA Case Document.relationships.documentProperties][recordType!Document.fields.description], document(save!value[1], "description") ) } ), null() ) }, required: true(), disabled: if(ri!isUpdate, false(), true()), marginAbove: "NONE", marginBelow: "NONE" ) ), a!sideBySideItem( item: a!imageField( images: a!documentImage( document: a!iconIndicator(icon: "REMOVE"), altText: "Remove Document", link: a!dynamicLink( saveInto: { a!save( ri!case[recordType!SCA Case.relationships.caseDocuments], remove( ri!case[recordType!SCA Case.relationships.caseDocuments], fv!index ) ), a!save(local!expandProperties, false()) } ) ), size: "ICON" ), width: "MINIMIZE", showWhen: a!isNotNullOrEmpty( fv!item[recordType!SCA Case Document.fields.documentId] ) ) }, alignVertical: "MIDDLE", spacing: "STANDARD", marginAbove: "NONE", marginBelow: "NONE" ), /* Section containing document properties */ /* By default, this section is collapsed */ a!sectionLayout( contents: { a!richTextDisplayField( labelPosition: "COLLAPSED", value: { a!richTextIcon( icon: if( local!expandProperties, "angle-down-bold", "angle-right-bold" ), link: a!dynamicLink( saveInto: a!save( local!expandProperties, not(local!expandProperties) ) ), linkStyle: "STANDALONE" ), " ", a!richTextItem( text: "Properties", link: a!dynamicLink( saveInto: a!save( local!expandProperties, not(local!expandProperties) ) ), linkStyle: "STANDALONE" ) } ) }, divider: "ABOVE", marginAbove: "NONE", marginBelow: "NONE" ), a!columnsLayout( columns: { a!columnLayout( contents: { a!textField( label: "Name", value: fv!item[recordType!SCA Case Document.relationships.documentProperties][recordType!Document.fields.name], saveInto: a!save( fv!item[recordType!SCA Case Document.relationships.documentProperties][recordType!Document.fields.name], save!value ), required: true(), readOnly: and( a!defaultValue(ri!isUpdate, false()), not(local!isNewlyUploaded), not(local!isUpdatedFile) ), marginAbove: "LESS", marginBelow: "STANDARD" ), a!paragraphField( label: "Description", value: fv!item[recordType!SCA Case Document.relationships.documentProperties][recordType!Document.fields.description], saveInto: a!save( fv!item[recordType!SCA Case Document.relationships.documentProperties][recordType!Document.fields.description], save!value ), readOnly: and( a!defaultValue(ri!isUpdate, false()), not(local!isNewlyUploaded), not(local!isUpdatedFile) ), marginAbove: "LESS", marginBelow: "STANDARD" ) }, width: "MEDIUM_PLUS" ) }, showWhen: local!expandProperties = true(), marginAbove: "LESS", marginBelow: "NONE" ) }, height: "AUTO", style: "NONE", shape: "SEMI_ROUNDED", padding: "STANDARD", marginAbove: "STANDARD", marginBelow: "STANDARD", showBorder: true(), showShadow: false() ) ) ) } ) }, style: "NONE", shape: "SEMI_ROUNDED", padding: "MORE", marginBelow: "STANDARD" ) } ) }, contentsWidth: "FULL", primaryButtons: a!buttonWidget( label: if(ri!isUpdate, "Save", "Create"), submit: true, style: "SOLID", showWhen: fv!isLastStep, validate: true ), secondaryButtons: a!buttonWidget( label: "Cancel", value: true, saveInto: ri!cancel, submit: true, style: "LINK", validate: false ), showButtonDivider: true ) Test it out Record type references are specific to each environment. If you copy and paste the expression above into an interface, it will not evaluate. To test this expression in your application: Create an interface. Create the necessary rule inputs. Copy and paste the expression in the interface. Replace all record type references with references to the document management record type in your environment. Notable implementation details To allow users to interact with this form, you can set the interface as a start form in a process model. For example, you could use this interface as the start form in a process model like the one below: In the a!columnsLayout() on line 247, you can add other components like a document viewer or a document download link to allow users to further interact with the uploaded document. For example, you could add the following after the paragraphField() function on line 267 to display a document preview: 1 2 3 4 5 a!documentViewerField( document: ri!case[recordType!SCA Case.relationships.caseDocuments.fields.documentId], height: "SHORT", label: "Preview" ) This would display the following: If you do not want users to edit document properties, you can use an editable grid to upload documents instead. See theUpload Multiple Documents in an Editable Grid recipe to learn how. If you want to upload files outside of a start form or task, update the saveInto parameter in the Submit button to saveInto: a!submitUploadedFiles(). Feedback Was this page helpful? SHARE FEEDBACK Loading...