17. Relationships

Punch List App: Next Steps

Let's look at further requirements for our Punch List app:

App Design: Construction Punch List

When more and more punches are captured in the punch list app, it will become problematic to browse all the punches in a single list. We want to allow users to organize punches into categories, and then to filter the lists of punches by category.

Relationships

Therefore, we want to make it possible for categories to be created in our database, and then each punch list item must be assigned to a particular category. In order to implement this functionality, we'll make use of Relationships in our Data Model. Relationships allow us to define how our different object types relate to each other. If we create a new Category object type, we'll say that each category has many punch list items that belong to that category. This is called a one-to-many relationship.

Add a New Object Type

Let's head over to the Data Model workspace and update the schema.xml to include a new Category model. This model will only have one field for now, a field that will store the 'name' of the category - we will also use this field as the default display value. Like this.

<model name="category" label="Category">
    <field name="name" label="Name" type="text" />
    
    <display>{name}</display>
</model>

Add a New Relationship

Next, we'll create the one-to-many relationship between the two object types. This is done by adding a belongs-to tag to the Item object type, and a has-many to the Category object type as shown below. Relationship tags should be listed after all the fields in a model but before the <display> tag. Note that the name="" property on the has-many allows you to specify a name for the whole collection of items that belong to a category. Using the pluralized form of the object type (in this case, "items") is a good convention to use for the collection name. On the belongs-to side you can also specify a name="" for the relationship, but if you don't the system will default the name to the name of the associated model - in our case category.

Your ERD should also visualize these changes, like this.

While we are at it, let's also add a relationship between user and item to signify which user created which punch items. To do this we add another belongs-to tag on our item model, this time pointing to the user model, and we add the has-many tag to the user model. For this belongs-to relationship let's not use the system default name, and instead specify it as name="created_by". At this point your schema.xml file should look like this.

See Changes in Data Browser

If you go to the Data Browser for the "Testing" environment, you'll notice that there is a new Category object type shown on the left, and that clicking on an Item shows a Belongs to Category field and a Belongs to User - created_by field at the bottom (if your Data Browser was already open in a different tab then you will need to first refresh that browser tab before you will see the changes):

Click on the Category tab on the left and let's create a couple of categories:

Set Category on "Add New Item" Screen

Now let's add the category selection to the Add New Item view. Under the "Variables go here" section of the add_new.view.xml file, add a new variable called all_categories which will be of type query:category. Like this.

Then change the init() function to populate the categories using a Query and also populate the relationship to the user model. To set the belongs-to relationship in JS we use a helper method that is created for us on the model in question, this method will use the value of name that is specified in the in the Data Model in the <belongs-to> tag. So in our case, we specified the relationship as <belongs-to model="user" name="created_by" /> and so we will use the created_by method to set the relationship to the user model, like this.

Finally, add a <object-dropdown> View Component, above the <text-input> component, allowing the user to select the category. Note that:

  • We specify query="all_categories" so that the list will show all the categories that we've pulled using our Query.

  • We specify bind="item.category" so that the category that the user selects will be stored in the category relationship on our item variable.

Test on Mobile Device

At "Add New Item", you should now see a drop-down list to select a category:

View Relationship on App Backend Data Browser

After you've added a new item, if you open up the Data Browser for the "Testing" environment again, you'll see "Belongs to Category" for the item shows the category that you've selected. Go ahead and update the other punch items and make each of them belong-to a category as well. Like this:

Display Item Category on "Main" and "View Punch Item"

Let's update the list component on our Main view to display the category inside of the <content> tag, and while we are at it, let's also add the created_at timestamp in the <footer> tag. So, in your main.view.xml, update the list component like this.

If we want to show the category on the View punch Item view, we could adjust our table component there as follows:

Test on Device

Enable Filtering by Category

Lastly, we want the user to be able to browse punch items by category. We will do this in a new view called browse_items, but first let's add a button on our Main view to allow the user to navigate to the Browse Items view. So, on your main.view.xml, add a new button to the <button-group> below the 'Add New Item' button, and hook it up like this:

You will notice we have included the necessary JS navigation logic directly in the on-press attribute by using the $: notation to execute raw JS from our View XML.

Next, let's create a new View with the name browse_items (remember, you can create views using the command palette, ctrl+shift+p / cmd+shift+p to open the command palette and then start typing create and choose the Create view action), and then once created update the <view title="Browse Punch Items by Category"> as before.

Now we can add some components to our new view. Firstly, we are going to need to present the user with a list of all categories (we will need a query for this), let the user select a category (we will need a category variable for this), and then show the user all the punch items that belong to the selected category (we will need another query for this). So, in your newly created browse_items.view.xml specify all these variables, like this.

Now let's add the necessary view components. We want to display all the the categories to the user and then once they select one, show all the related punch items for the selected category. A nice way to do this is to make use of the JourneyApps show-if and hide-if logic available to any view component (syntax reference for show-if and hide-if available here). We will display an <object-list> for all the categories but once the user selects one we will hide the <object-list> and display a <list> of related items instead (re-using the list we defined on our main view). So, in your browse_items.view.xml, add the following code:

Next we need to add a way for the user to clear their selection (we will use a button for that - using show-if to only display the button if the user has already made a selection). After that we also need to populate the various queries from our JS. Populating the all_categories variable can be done from the init() function, but we can only populate filtered_items once the user makes a selection, and we need to re-populate it every time the user makes a new selection. To do this we will make use of the on-change="" attribute of our <object-list> component. (Note, the on-change="" attribute is available on all input components, but note that for capture-photo and scan-barcode it is referenced as on-capture and on-scan instead). So, update your browse_items.view.xml to the following:

Let's update our browse_items.js file next to populate our queries and handle the on-change event, as well as the clearSelection and selectItem function calls (let's reuse the selectItem functions from the main view). Like this

Test on Device

Let's test these changes. Your app should now look like this:

Well done, you have effectively completed the Punch List App for the scope we originally envisioned. A user can view open punches, capture new punches, view details for existing punches and mark them as closed. Next up we are going to add a new user role to our app, an App Admin, that should be able to easily manage all the data in the application.

Last updated