Editable Grid SAIL

I tried the following SAIL recipe which worked well with Employees CDT as ri! parameter for adding rows. I thought deletion would also be done by the same call using write to datastore smart service in a kind of merging but it did not. I am trying to call another service for deletion with deleted ids stored in another ri! parameter. However I am getting into following two issues.

1) The deleted id is +1 more than rowid

2) When submit button is pressed I am getting error that my deleted ids array is null. 

Could somebody provide me details on what needs to be done?

 

=load(
a!formLayout(
label: "SAIL Example: Add,Update, or Remove Employee Data",
contents: {
a!gridLayout(
totalCount: count(ri!Employees),
headerCells: {
a!gridLayoutHeaderCell(label: "id" ),
a!gridLayoutHeaderCell(label: "First Name" ),
a!gridLayoutHeaderCell(label: "Last Name" ),
a!gridLayoutHeaderCell(label: "Department" ),
a!gridLayoutHeaderCell(label: "Title" ),
a!gridLayoutHeaderCell(label: "Phone Number" ),
a!gridLayoutHeaderCell(label: "Start Date", align: "RIGHT" ),
/* For the "Remove" column */
a!gridLayoutHeaderCell(label: "" )
},
/* Only needed when some columns need to be narrow */
columnConfigs: {
a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight:3 ),
a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight:3 ),
a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight:3 ),
a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight:3 ),
a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight:3 ),
a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight:3 ),
a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight:2 ),
a!gridLayoutColumnConfig(width: "ICON")
},
rows: a!forEach(
items: ri!Employees,
expression: a!gridRowLayout(
contents: {
a!integerField(
/* Labels are not visible in grid cells but are necessary to meet accessibility requirements */
label: "first name " & fv!index,
value: fv!item.id,
saveInto: fv!item.id,
required: true
),
/* For the First Name Column*/
a!textField(
/* Labels are not visible in grid cells but are necessary to meet accessibility requirements */
label: "first name " & fv!index,
value: fv!item.firstName,
saveInto: fv!item.firstName,
required: true
),
/* For the Last Name Column*/
a!textField(
label: "last name " & fv!index,
value: fv!item.lastName,
saveInto: fv!item.lastName,
required:true
),
/* For the Department Column*/
a!dropdownField(
label: "department " & fv!index,
placeholderLabel: "-- Select -- ",
choiceLabels: { "Corporate", "Engineering", "Finance", "Human Resources", "Professional Services", "Sales" },
choiceValues: { "Corporate", "Engineering", "Finance", "HR", "Professional Services", "Sales" },
value: fv!item.department,
saveInto: fv!item.department,
required:true
),
/* For the Title Column*/
a!textField(
label: "title " & fv!index,
value: fv!item.title,
saveInto: fv!item.title,
required:true
),
/* For the Phone Number Column*/
a!textField(
label: "phone number " & fv!index,
placeholder:"123-456-7890",
value: fv!item.phoneNumber,
saveInto: fv!item.phoneNumber
),
/* For the Start Date Column*/
a!dateField(
label: "start date " & fv!index,
value: fv!item.startDate,
saveInto: fv!item.startDate,
required:true,
align: "RIGHT"
),
/* For the Removal Column*/
a!imageField(
label: "delete " & fv!index,
images: a!documentImage(
document: a!iconIndicator("REMOVE"),
altText: "Remove Employee",
caption: "Remove " & fv!item.firstName & " " & fv!item.lastName,
link: a!dynamicLink(
value: fv!index,
saveInto: {
a!save(ri!Employees, remove(ri!Employees, save!value)),
a!save(ri!DeletedEmployees, append(ri!DeletedEmployees, fv!item.id))  -- ( fv!item.id -1 works logging in correct ids in to array for deletion)
}
)
),
size: "ICON"
)
},
id: fv!index
)
),
addRowlink: a!dynamicLink(
label: "Add Employee",
/*
* For your use case, set the value to a blank instance of your CDT using
* the type constructor, e.g. type!Employee(). Only specify the field
* if you want to give it a default value e.g. startDate: today()+1.
*/
value: 'type!{urn:com:appian:types}Employee'(),
saveInto: {
a!save(ri!Employees, append(ri!Employees,save!value))
}
)
)
},
buttons: a!buttonLayout(
primaryButtons: a!buttonWidgetSubmit(
label: "Submit",
saveInto:{ a!writeToDataStoreEntity(
dataStoreEntity: cons!DS_ENTITY_REF_EMPLOYEE,
valueToStore: ri!Employees
),
a!deleteFromDataStoreEntities(
entity: cons!DS_ENTITY_REF_EMPLOYEE,              -- This failes
identifiers: ri!DeletedEmployees
)
}
)
)
)
)

  Discussion posts and replies are publicly visible

Parents
  • I am not sure whether you want to delete data from database, that has been added previously or you are creating and that time only deleting it. Can you please clear it.
  • There is a removal link in the SAIL which I thought of making it work. I am also trying to parallel the analogy of how I would have done using dataset merging in other programming languages. Is there any merge CDT that would have merged both inserts and deletes at the same time? I am trying to add one row and delete another row. On submit, I am expecting either one smart service or two, one for inserts and the other deletes, take care of the commits properly. Hope that clarifies.
  • Hey, sorry I didnt check, but I dont think we can not* do both write to data store smart service and Delete from data store smart service in the same interface. Can you try triggering a process using a!startProcess and carry delete/add events there.

  • Is there any merge kind of functionality available? I would like to see my code as lean as possible. Thanks for your help.
  • I suppose you are not entering data in DB first and directly trying to delete but till the time data is not entered in DB how can we delete that data? Correct me if I am missing anything.
  • Yaa we have merge function available in Appian.
  • Editable grid functionality is to read the data from a CDT and allow adds/deletes inline. So, in may case, my grid shows 20 rows having returned from database. I delete one of it. I can submit the transaction to DB first and then add. But, decided to add a new row before hitting submit. I should end up essentially 20 rows in database, and, I should have a new row added and a old row removed. So, in my example, I deleted rowid 56 and added rowid 144 with new information. Hope that explains.
  • How do I use it in DB smart service? I essentially would like my data set bound to the grid with resulting rows after additions/deletions inline to be merged with database table(s). Could you please point me to documentation on this functionality?
  • I could make the functionality work well by doing some changes. The following code works very well for inserts/deletes at the same time and also does not depend on the surrogate keys ( entity primary keys ) for being contiguous and match the count of rows.

    =load(
    a!formLayout(
    label: "SAIL Example: Add,Update, or Remove Employee Data",
    contents: {
    a!gridLayout(
    totalCount: count(ri!Employees),
    headerCells: {
    a!gridLayoutHeaderCell(label: "id" ),
    a!gridLayoutHeaderCell(label: "First Name" ),
    a!gridLayoutHeaderCell(label: "Last Name" ),
    a!gridLayoutHeaderCell(label: "Department" ),
    a!gridLayoutHeaderCell(label: "Title" ),
    a!gridLayoutHeaderCell(label: "Phone Number" ),
    a!gridLayoutHeaderCell(label: "Start Date", align: "RIGHT" ),
    /* For the "Remove" column */
    a!gridLayoutHeaderCell(label: "" )
    },
    /* Only needed when some columns need to be narrow */
    columnConfigs: {
    a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight:3 ),
    a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight:3 ),
    a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight:3 ),
    a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight:3 ),
    a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight:3 ),
    a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight:3 ),
    a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight:2 ),
    a!gridLayoutColumnConfig(width: "ICON")
    },
    rows: a!forEach(
    items: ri!Employees,
    expression: a!gridRowLayout(
    contents: {
    a!integerField(
    /* Labels are not visible in grid cells but are necessary to meet accessibility requirements */
    label: "id " & fv!item.id,
    value: fv!item.id,
    saveInto: fv!item.id,
    required: true
    ),
    /* For the First Name Column*/
    a!textField(
    /* Labels are not visible in grid cells but are necessary to meet accessibility requirements */
    label: "first name " & fv!item.id,
    value: fv!item.firstName,
    saveInto: fv!item.firstName,
    required: true
    ),
    /* For the Last Name Column*/
    a!textField(
    label: "last name " & fv!item.id,
    value: fv!item.lastName,
    saveInto: fv!item.lastName,
    required:true
    ),
    /* For the Department Column*/
    a!dropdownField(
    label: "department " & fv!item.id,
    placeholderLabel: "-- Select -- ",
    choiceLabels: { "Corporate", "Engineering", "Finance", "Human Resources", "Professional Services", "Sales" },
    choiceValues: { "Corporate", "Engineering", "Finance", "HR", "Professional Services", "Sales" },
    value: fv!item.department,
    saveInto: fv!item.department,
    required:true
    ),
    /* For the Title Column*/
    a!textField(
    label: "title " & fv!item.id,
    value: fv!item.title,
    saveInto: fv!item.title,
    required:true
    ),
    /* For the Phone Number Column*/
    a!textField(
    label: "phone number " & fv!item.id,
    placeholder:"123-456-7890",
    value: fv!item.phoneNumber,
    saveInto: fv!item.phoneNumber
    ),
    /* For the Start Date Column*/
    a!dateField(
    label: "start date " & fv!item.id,
    value: fv!item.startDate,
    saveInto: fv!item.startDate,
    required:true,
    align: "RIGHT"
    ),
    /* For the Removal Column*/
    a!imageField(
    label: "delete " & fv!item.id,
    images: a!documentImage(
    document: a!iconIndicator("REMOVE"),
    altText: "Remove Employee",
    caption: "Remove " & fv!item.firstName & " " & fv!item.lastName,
    link: a!dynamicLink(
    value: fv!item.id,
    saveInto: {
    a!save(ri!DeletedEmployees, append(ri!DeletedEmployees, fv!item.id)),
    a!save(ri!Employees, remove(ri!Employees, wherecontains(ri!DeletedEmployees, ri!Employees.id))),
    a!deleteFromDataStoreEntities(
    a!entityDataIdentifiers(
    entity: cons!DS_ENTITY_REF_EMPLOYEE,
    identifiers: ri!DeletedEmployees
    )
    )
    }
    )
    ),
    size: "ICON"
    )
    },
    id: fv!item.id
    )
    )
    ,
    addRowlink: a!dynamicLink(
    label: "Add Employee",
    /*
    * For your use case, set the value to a blank instance of your CDT using
    * the type constructor, e.g. type!Employee(). Only specify the field
    * if you want to give it a default value e.g. startDate: today()+1.
    */
    value: 'type!{urn:com:appian:types}Employee'(),
    saveInto: {
    a!save(ri!Employees, append(ri!Employees,save!value))
    }
    )
    )
    },
    buttons: a!buttonLayout(
    primaryButtons: a!buttonWidgetSubmit(
    label: "Submit",
    saveInto:{
    a!save(ri!DeletedEmployees, {}),
    a!writeToDataStoreEntity(
    dataStoreEntity: cons!DS_ENTITY_REF_EMPLOYEE,
    valueToStore: ri!Employees
    )
    }
    )
    )
    )
    )
Reply
  • I could make the functionality work well by doing some changes. The following code works very well for inserts/deletes at the same time and also does not depend on the surrogate keys ( entity primary keys ) for being contiguous and match the count of rows.

    =load(
    a!formLayout(
    label: "SAIL Example: Add,Update, or Remove Employee Data",
    contents: {
    a!gridLayout(
    totalCount: count(ri!Employees),
    headerCells: {
    a!gridLayoutHeaderCell(label: "id" ),
    a!gridLayoutHeaderCell(label: "First Name" ),
    a!gridLayoutHeaderCell(label: "Last Name" ),
    a!gridLayoutHeaderCell(label: "Department" ),
    a!gridLayoutHeaderCell(label: "Title" ),
    a!gridLayoutHeaderCell(label: "Phone Number" ),
    a!gridLayoutHeaderCell(label: "Start Date", align: "RIGHT" ),
    /* For the "Remove" column */
    a!gridLayoutHeaderCell(label: "" )
    },
    /* Only needed when some columns need to be narrow */
    columnConfigs: {
    a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight:3 ),
    a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight:3 ),
    a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight:3 ),
    a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight:3 ),
    a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight:3 ),
    a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight:3 ),
    a!gridLayoutColumnConfig(width: "DISTRIBUTE", weight:2 ),
    a!gridLayoutColumnConfig(width: "ICON")
    },
    rows: a!forEach(
    items: ri!Employees,
    expression: a!gridRowLayout(
    contents: {
    a!integerField(
    /* Labels are not visible in grid cells but are necessary to meet accessibility requirements */
    label: "id " & fv!item.id,
    value: fv!item.id,
    saveInto: fv!item.id,
    required: true
    ),
    /* For the First Name Column*/
    a!textField(
    /* Labels are not visible in grid cells but are necessary to meet accessibility requirements */
    label: "first name " & fv!item.id,
    value: fv!item.firstName,
    saveInto: fv!item.firstName,
    required: true
    ),
    /* For the Last Name Column*/
    a!textField(
    label: "last name " & fv!item.id,
    value: fv!item.lastName,
    saveInto: fv!item.lastName,
    required:true
    ),
    /* For the Department Column*/
    a!dropdownField(
    label: "department " & fv!item.id,
    placeholderLabel: "-- Select -- ",
    choiceLabels: { "Corporate", "Engineering", "Finance", "Human Resources", "Professional Services", "Sales" },
    choiceValues: { "Corporate", "Engineering", "Finance", "HR", "Professional Services", "Sales" },
    value: fv!item.department,
    saveInto: fv!item.department,
    required:true
    ),
    /* For the Title Column*/
    a!textField(
    label: "title " & fv!item.id,
    value: fv!item.title,
    saveInto: fv!item.title,
    required:true
    ),
    /* For the Phone Number Column*/
    a!textField(
    label: "phone number " & fv!item.id,
    placeholder:"123-456-7890",
    value: fv!item.phoneNumber,
    saveInto: fv!item.phoneNumber
    ),
    /* For the Start Date Column*/
    a!dateField(
    label: "start date " & fv!item.id,
    value: fv!item.startDate,
    saveInto: fv!item.startDate,
    required:true,
    align: "RIGHT"
    ),
    /* For the Removal Column*/
    a!imageField(
    label: "delete " & fv!item.id,
    images: a!documentImage(
    document: a!iconIndicator("REMOVE"),
    altText: "Remove Employee",
    caption: "Remove " & fv!item.firstName & " " & fv!item.lastName,
    link: a!dynamicLink(
    value: fv!item.id,
    saveInto: {
    a!save(ri!DeletedEmployees, append(ri!DeletedEmployees, fv!item.id)),
    a!save(ri!Employees, remove(ri!Employees, wherecontains(ri!DeletedEmployees, ri!Employees.id))),
    a!deleteFromDataStoreEntities(
    a!entityDataIdentifiers(
    entity: cons!DS_ENTITY_REF_EMPLOYEE,
    identifiers: ri!DeletedEmployees
    )
    )
    }
    )
    ),
    size: "ICON"
    )
    },
    id: fv!item.id
    )
    )
    ,
    addRowlink: a!dynamicLink(
    label: "Add Employee",
    /*
    * For your use case, set the value to a blank instance of your CDT using
    * the type constructor, e.g. type!Employee(). Only specify the field
    * if you want to give it a default value e.g. startDate: today()+1.
    */
    value: 'type!{urn:com:appian:types}Employee'(),
    saveInto: {
    a!save(ri!Employees, append(ri!Employees,save!value))
    }
    )
    )
    },
    buttons: a!buttonLayout(
    primaryButtons: a!buttonWidgetSubmit(
    label: "Submit",
    saveInto:{
    a!save(ri!DeletedEmployees, {}),
    a!writeToDataStoreEntity(
    dataStoreEntity: cons!DS_ENTITY_REF_EMPLOYEE,
    valueToStore: ri!Employees
    )
    }
    )
    )
    )
    )
Children
No Data