12. Data Manipulation

Delete an Object

Let's see what we want to add to our punch List app next:

App Design: Construction Punch List

Sometimes our users will add incorrect punches and would need to remove them again. Therefore, we need a feature allowing us to delete punch items.

Let's add a "Delete Selected Item" button to our Main View XML similar to the "View Selected Item" button we already have. We will specify that the button should call the deleteItem function, implying that we'll add a JavaScript function that will delete the selected item. So, on your Main view in your main.view.xml file, add the button as follows.

<button label="View Selected Item" on-press="viewItem" validate="true" style="solid" />
<button label="Delete Selected Item" on-press="deleteItem" validate="true" style="solid" />
<button label="Add New Item" on-press="goToNew" validate="false" style="solid" />

Now add the deleteItem function to the Main view JS. Note that to destroy an object, we simply call .destroy() on the object:

function deleteItem() {
    view.selected_item.destroy();
}

If you test this on a device, you'll see that the item is immediately deleted from the list once you press the button. Normally, it would be advisable to first get the user to confirm before deleting the item, so let's get that confirmation now using a component that is triggered from JavaScript called a confirm dialog. Update your deleteItem function to the following.

function deleteItem() {
    var confirmed = journey.dialog.confirm({
        title: "Delete Item?",
        message: "Are you sure?",
        okButton: { text: "YES", color: "positive" },
        cancelButton: "NO"
    });
    if (confirmed) {
        view.selected_item.destroy();
    }
}

Modify an Existing Object

We head on to our next requirement:

App Design: Construction Punch List

Our app allows us to add new punch list items which have a status of "Open", but we need a feature to allow the user to mark a punch item as "Completed" or "Closed" when the punch has been taken care of.

Let's add this feature to the "View Item" screen of our app. Open up the view_item view and add a new button to the View XML that will allow the user to 'Mark as Closed'. To add some variety and complexity, let's navigate to a new view that we will use as a confirmation page.

<button label="Mark as Closed" on-press="markClosed" validate="false" style="solid" />

We specified on-press="markClosed" above, so let's add that function on the JavaScript side. So, on your "View Item" view, in the view_item.js file, add the following.

function markClosed() {
    view.item.status = "Closed";
    view.item.save();
    notification.success("Item marked as closed");
    navigate.link('completed_item', view.item);
}

At this point, your View Item code should look like this

XML

<?xml version="1.0" encoding="UTF-8"?>
<view title="View Punch Item">
    <!-- Parameters go here: -->
    <param name="item" type="item" />

    <!-- Variables go here: -->

    <!-- Components go here: -->
    <heading>{item.comments}</heading>
    <info-table>
        <row label="Item Comments" bind="item.comments" />
        <row label="Item Status" bind="item.status" />
        <row label="Current Time" value="{$:showTime()}" />
    </info-table>

    <display-photo bind="item.photo" />
    
    <button label="Mark as Closed" on-press="markClosed" validate="false" style="solid" />
    <button label="Go back" on-press="dismiss" validate="false" style="solid" />

</view>

JS

// This function is called when the app navigates to this view (using a link)
function init() {
    // initialize any data here that should be available when the view is shown
}

// This function is called when the user returns to this view from another view
function resume(from) {
    // from.back       (true/false) if true, the user pressed the "Back" button to return to this view
    // from.dismissed  (true/false) if true, the app dismissed to return to this view
    // from.path       contains the path of the view that the user returned from
    // if any data needs to be refreshed when the user returns to this view, you can do that here:
}

function showTime() {
    var now = new Date(); // standard JS function to get the current date and time
    return now.toLocaleString(); // changes the DateTime object into a string according to local conventions
}

function markClosed() {
    view.item.status = "Closed";
    view.item.save();
    notification.success("Item marked as closed");
    navigate.link('completed_item', view.item);
}

Let's create the Completed Item view, remember the name of the view should be completed_item and once created update the title of the view to "Completed Punch Item". Then we will also add some components to use for the confirmation.

<?xml version="1.0" encoding="UTF-8"?>
<view title="Completed Punch Item">
    <!-- Parameters go here: -->
    <param name="item" type="item" />
    <!-- Variables go here: -->

    <!-- Components go here: -->
    <heading>Item Completed!</heading>
    <info>This item is now completed and will no longer show up in the list of Open Snags</info>    

    <button label="Go Home" on-press="goHome" validate="false" style="solid" />
</view>

Finally, we need to create the JS function goHome to take us all the way back to the Main view. Up to now we have been using the discard and dismiss navigation actions to pop the current view off of the view stack and return to the previous one. But this will not work for the Completed Punch Item view, as we will have more than one view to "pop" off the stack to get to back to the main view. To do this, we need to dismiss or discard all the way back to a specific view currently on the view stack.

So, to do this we simply pass the name of the view we want to dismiss/discard to as an argument to the navigate.dismiss (or navigate.discard) action. Note: This will dismiss/discard views until it reaches the specified view. If the specified view is not on the stack then the operation will error. Therefore, in your completed_item.js file define the following function.

function goHome() {
    navigate.dismiss("main");
}

So, the code on your completed_item view should now look like this.

XML

completed_item.xml
<?xml version="1.0" encoding="UTF-8"?>
<view title="Completed Punch Item">
    <!-- Parameters go here: -->
    <param name="item" type="item" />
    <!-- Variables go here: -->

    <!-- Components go here: -->
    <heading>Item Completed!</heading>
    <info>This item is now completed and will no longer show up in the list of Open Snags</info>    

    <button label="Go Home" on-press="goHome" validate="false" style="solid" />
</view>

JS

// This function is called when the app navigates to this view (using a link)
function init() {
    // initialize any data here that should be available when the view is shown
}

// This function is called when the user returns to this view from another view
function resume(from) {
    // from.back       (true/false) if true, the user pressed the "Back" button to return to this view
    // from.dismissed  (true/false) if true, the app dismissed to return to this view
    // from.path       contains the path of the view that the user returned from
    // if any data needs to be refreshed when the user returns to this view, you can do that here:
}

function goHome() {
    navigate.dismiss("main");
}

Test on Device

Let's test it out. Your app should now look like this.

Good Work! The first part of the Punch List App is all done. Next up we are going to make the app more user friendly for tablet, desktop and web users.

Last updated