How to create a To Do list using SharePoint and Microsoft Lists view formatting – Part 2

With a custom view formatting you can completely transform the look and feel of a list making it work almost as a standalone application.

If you have done all the steps explained in the first part of this article now it is time to improve the layout and the interaction of users with your To Do list.

To Do list for SharePoint and Microsoft Lists

This article has 3 different sections that will guide you through the implementations of the To Do lists in detail:

  1. List definition
  2. List view customization
  3. To Do list template

Apply a To Do list view formatting

To apply the custom list view provided in this article do the following:

  1. Open the view selection menu and click on Format current view
  2. In the format view panel click on the Advanced mode link
  3. Make sure the Format view is selected
  4. In the Choose layout option select List
  5. Replace the existing code by the formatting below. You will be also able to download the JSON file used in this sample at the end of the article.
    {
      "$schema": "https://developer.microsoft.com/json-schemas/sp/v2/row-formatting.schema.json",
      "hideSelection": true,
      "hideListHeader": true,
      "rowFormatter": {
        "elmType": "div",
        "style": {
          "padding": "5px",
          "min-width": "97%",
          "border-bottom": "1px solid #e5e5e5"
        },
        "attributes": {
          "class": "ms-bgColor-neutralLight--hover ms-fontColor-neutralSecondary–hover"
        },
        "children": [
          {
            "elmType": "div",
            "customRowAction": {
              "action": "setValue",
              "actionInput": {
                "Status": "=if([$Status], '0' , '1' )"
              }
            },
            "attributes": {
              "class": "ms-fontColor-themePrimary",
              "iconName": "=if([$Status], 'SkypeCircleCheck' , 'CircleRing')"
            },
            "style": {
              "font-size": "1.2rem",
              "cursor": "pointer",
              "padding-left": "10px"
            }
          },
          {
            "elmType": "div",
            "style": {
              "text-align": "left",
              "padding": "5px",
              "margin-left": "10px",
              "width": "100%"
            },
            "children": [
              {
                "elmType": "div",
                "style": {
                  "font-weight": "400",
                  "font-size": "14px",
                  "text-decoration": "=if([$Status], 'line-through' , '')"
                },
                "txtContent": "[$Title]"
              },
              {
                "elmType": "span",
                "style": {
                  "font-size": "10px",
                  "margin-right": "5px",
                  "color": "=if([$Status], '', if(toLocaleDateString([$Duedate])==toLocaleDateString(@now),'#465efc',if([$Duedate] < @now, '#db3a29' , '')))",
                  "display": "=if([$Duedate],'','none')"
                },
                "attributes": {
                  "iconName": "Event"
                }
              },
              {
                "elmType": "span",
                "style": {
                  "font-weight": "=if([$Status], '200', if([$Duedate] < @now, '500' , '200'))",
                  "font-size": "12px",
                  "overflow": "hidden",
                  "margin-top": "2px",
                  "white-space": "nowrap",
                  "color": "=if([$Status], '', if(toLocaleDateString([$Duedate])==toLocaleDateString(@now),'#465efc',if([$Duedate] < @now, '#db3a29' , '')))",
                  "display": "=if([$Duedate],'','none')"
                },
                "txtContent": {
                  "operator": "+",
                  "operands": [
                    "=if(toLocaleDateString([$Duedate])==toLocaleDateString(@now),'Today ',if([$Duedate] > @now, 'Due, ' , 'Overdue, '))",
                    "=if(toLocaleDateString([$Duedate])!=toLocaleDateString(@now), [$Date], '')"
                  ]
                }
              },
              {
                "elmType": "span",
                "style": {
                  "font-size": "12px",
                  "margin": "0 10px",
                  "font-weight": "900",
                  "display": "=if([$Description]=='', 'none', if([$Duedate],'','none'))"
                },
                "txtContent": "·"
              },
              {
                "elmType": "span",
                "style": {
                  "font-size": "10px",
                  "display": "=if([$Description]=='', 'none', '')"
                },
                "attributes": {
                  "iconName": "QuickNote"
                }
              },
              {
                "elmType": "span",
                "style": {
                  "font-size": "12px",
                  "margin": "0 10px",
                  "font-weight": "900",
                  "display": "=if([$Category]=='', 'none', '')"
                },
                "txtContent": "·"
              },
              {
                "elmType": "span",
                "style": {
                  "font-size": "11px",
                  "display": "=if([$Category]=='', 'none', '')"
                },
                "attributes": {
                  "iconName": "Tag"
                }
              },
              {
                "elmType": "span",
                "style": {
                  "font-size": "11px",
                  "font-weight": "200",
                  "margin-left": "5px",
                  "display": "=if([$Category]=='', 'none', '')"
                },
                "txtContent": "[$Category]"
              },
              {
                "elmType": "span",
                "style": {
                  "font-size": "12px",
                  "margin": "0 10px",
                  "font-weight": "900",
                  "display": "=if([$Attachments] == '0', 'none', '')"
                },
                "txtContent": "·"
              },
              {
                "elmType": "span",
                "style": {
                  "font-size": "11px",
                  "display": "=if([$Attachments] == '0', 'none', '')",
                  "cursor": "pointer"
                },
                "attributes": {
                  "iconName": "Attach"
                },
                "customRowAction": {
                  "action": "defaultClick"
                }
              }
            ]
          },
          {
            "elmType": "span",
            "attributes": {
              "iconName": "=if([$_CommentCount], 'Comment', 'CommentAdd')",
              "class": "=if([$_CommentCount], 'ms-fontColor-themePrimary', '')"
            },
            "style": {
              "font-size": "14px",
              "padding-right": "15px",
              "cursor": "pointer"
            },
            "customRowAction": {
              "action": "defaultClick"
            }
          },
          {
            "elmType": "span",
            "attributes": {
              "iconName": "=if([$Important], 'FavoriteStarFill', 'FavoriteStar')",
              "class": "=if([$Important], 'ms-fontColor-themePrimary', '')"
            },
            "style": {
              "font-size": "16px",
              "padding-right": "10px",
              "cursor": "pointer"
            },
            "customRowAction": {
              "action": "setValue",
              "actionInput": {
                "Important": "=if([$Important], '0' , '1' )"
              }
            }
          }
        ],
        "customCardProps": {
          "formatter": {
            "elmType": "div",
            "style": {
              "padding": "20px"
            },
            "children": [
              {
                "elmType": "div",
                "children": [
                  {
                    "elmType": "div",
                    "style": {
                      "width": "350px",
                      "font-size": "14px",
                      "font-weight": "700",
                      "margin-bottom": "5px"
                    },
                    "txtContent": "[$Title]",
                    "inlineEditField": "[$Title]"
                  },
                  {
                    "elmType": "div",
                    "style": {
                      "width": "350px",
                      "font-size": "14px",
                      "font-weight": "400",
                      "margin-bottom": "5px",
                      "font-style": "=if([$Description]=='','Italic','')"
                    },
                    "txtContent": "=if([$Description]=='','Add note','[$Description]')",
                    "inlineEditField": "[$Description]"
                  },
                  {
                    "elmType": "div",
                    "style": {
                      "width": "350px",
                      "font-size": "12px",
                      "font-weight": "200",
                      "margin-bottom": "5px"
                    },
                    "children": [
                      {
                        "elmType": "span",
                        "style": {
                          "font-size": "11px"
                        },
                        "attributes": {
                          "iconName": "Event"
                        }
                      },
                      {
                        "elmType": "span",
                        "style": {
                          "font-size": "12px",
                          "margin-left": "5px",
                          "font-weight": "200",
                          "font-style": "=if('=toLocaleDateString([$Duedate])'=='','Italic','')"
                        },
                        "txtContent": "=if('=toLocaleDateString([$Duedate])'=='','Add due date', '=toLocaleDateString([$Duedate])')",
                        "inlineEditField": "[$Duedate]"
                      }
                    ]
                  },
                  {
                    "elmType": "div",
                    "style": {
                      "width": "350px",
                      "font-size": "12px",
                      "font-weight": "200",
                      "margin-bottom": "10px",
                      "padding-bottom": "10px",
                      "border-bottom": "1px solid #e5e5e5"
                    },
                    "children": [
                      {
                        "elmType": "span",
                        "style": {
                          "font-size": "11px"
                        },
                        "attributes": {
                          "iconName": "Tag"
                        }
                      },
                      {
                        "elmType": "span",
                        "style": {
                          "font-size": "12px",
                          "margin-left": "5px",
                          "font-weight": "200",
                          "font-style": "=if([$Category]=='','Italic','')"
                        },
                        "txtContent": "=if([$Category]=='','Pick a category',[$Category])",
                        "inlineEditField": "[$Category]"
                      }
                    ]
                  },
                  {
                    "elmType": "div",
                    "children": [
                      {
                        "elmType": "div",
                        "style": {
                          "font-size": "12px",
                          "font-weight": "200"
                        },
                        "txtContent": {
                          "operator": "+",
                          "operands": [
                            "Created: ",
                            "=toLocaleString([$Created])"
                          ]
                        }
                      },
                      {
                        "elmType": "div",
                        "style": {
                          "margin-bottom": "5px"
                        },
                        "children": [
                          {
                            "elmType": "img",
                            "style": {
                              "width": "12px",
                              "height": "12px",
                              "border-radius": "50%",
                              "margin-right": "5px"
                            },
                            "attributes": {
                              "src": "=getUserImage([$Author.email], 'S')",
                              "title": "[$Author.title]"
                            }
                          },
                          {
                            "elmType": "span",
                            "txtContent": "[$Author.title]",
                            "style": {
                              "font-size": "12px",
                              "font-weight": "200"
                            }
                          }
                        ]
                      },
                      {
                        "elmType": "div",
                        "style": {
                          "font-size": "12px",
                          "font-weight": "200"
                        },
                        "txtContent": {
                          "operator": "+",
                          "operands": [
                            "Updated: ",
                            "=toLocaleString([$Modified])"
                          ]
                        }
                      },
                      {
                        "elmType": "div",
                        "children": [
                          {
                            "elmType": "img",
                            "style": {
                              "width": "12px",
                              "height": "12px",
                              "border-radius": "50%",
                              "margin-right": "5px"
                            },
                            "attributes": {
                              "src": "=getUserImage([$Editor.email], 'S')",
                              "title": "[$Editor.title]"
                            }
                          },
                          {
                            "elmType": "span",
                            "txtContent": "[$Editor.title]",
                            "style": {
                              "font-size": "12px",
                              "font-weight": "200"
                            }
                          }
                        ]
                      },
                      {
                        "elmType": "span",
                        "style": {
                          "font-size": "14px",
                          "cursor": "pointer",
                          "position": "absolute",
                          "bottom": "20px",
                          "right": "20px"
                        },
                        "attributes": {
                          "iconName": "Delete"
                        },
                        "customRowAction": {
                          "action": "delete"
                        }
                      }
                    ]
                  }
                ]
              }
            ]
          },
          "openOnEvent": "hover",
          "directionalHint": "bottomCenter",
          "isBeakVisible": true,
          "beakStyle": {
            "backgroundColor": "white"
          }
        }
      },
      "groupProps": {
        "headerFormatter": {
          "elmType": "div",
          "style": {
            "flex-direction": "row"
          },
          "children": [
            {
              "elmType": "div",
              "style": {
                "padding": "4px 8px 5px 8px",
                "border-radius": "6px",
                "font-weight": "500",
                "cursor": "pointer"
              },
              "attributes": {
                "class": {
                  "operator": "+",
                  "operands": [
                    "ms-bgColor-themePrimary ",
                    "ms-fontColor-white"
                  ]
                }
              },
              "children": [
                {
                  "elmType": "div",
                  "style": {
                    "display": "inline-flex",
                    "padding-right": "9px"
                  },
                  "children": [
                    {
                      "elmType": "div",
                      "style": {
                        "padding-left": "5px"
                      },
                      "attributes": {
                        "class": "ms-fontWeight-bold"
                      },
                      "txtContent": "=if(@group.fieldData.displayValue == 'Yes', 'Completed', 'To Do')"
                    },
                    {
                      "elmType": "div",
                      "style": {
                        "padding-left": "5px"
                      },
                      "attributes": {
                        "class": "ms-fontWeight-normal"
                      },
                      "txtContent": {
                        "operator": "+",
                        "operands": [
                          " - ",
                          "=@group.count",
                          "= if(@group.count!= 1,' items',' item')"
                        ]
                      }
                    }
                  ]
                }
              ]
            }
          ]
        }
      }
    }
    
  6. Click Preview, and if your result is similar to the one shown in the following image go ahead and Save your view.

Save To Do list custom view formatting

Understanding the To Do list formatting components

With the custom view formatting applied to the list, now you need to understand everything it does. In the following list you have a detailed explanation of the features an end user will get followed by an explanation of the formatting code that was used to implement it.

To Do list formatting detail
  1. Groups – These 2 collapsible sections group all the pending and completed tasks. As the status column only stores Yes/No values the group headers were formatted to display a background color, a custom string to indicate the status of the tasks, and the number of items in each one of the groups. In the following JSON excerpt you can observe the formatting applied to the group headers using the groupProps.
      "groupProps": {
        "headerFormatter": {
          "elmType": "div",
          "style": {
            "flex-direction": "row"
          },
          "children": [
            {
              "elmType": "div",
              "style": {
                "padding": "4px 8px 5px 8px",
                "border-radius": "6px",
                "font-weight": "500",
                "cursor": "pointer"
              },
              "attributes": {
                "class": {
                  "operator": "+",
                  "operands": [
                    "ms-bgColor-themePrimary ",
                    "ms-fontColor-white"
                  ]
                }
              },
              "children": [
                {
                  "elmType": "div",
                  "style": {
                    "display": "inline-flex",
                    "padding-right": "9px"
                  },
                  "children": [
                    {
                      "elmType": "div",
                      "style": {
                        "padding-left": "5px"
                      },
                      "attributes": {
                        "class": "ms-fontWeight-bold"
                      },
                      "txtContent": "=if(@group.fieldData.displayValue == 'Yes', 'Completed', 'To Do')"
                    },
                    {
                      "elmType": "div",
                      "style": {
                        "padding-left": "5px"
                      },
                      "attributes": {
                        "class": "ms-fontWeight-normal"
                      },
                      "txtContent": {
                        "operator": "+",
                        "operands": [
                          " - ",
                          "=@group.count",
                          "= if(@group.count!= 1,' items',' item')"
                        ]
                      }
                    }
                  ]
                }
              ]
            }
          ]
        }
      }
    
  2. Status – These icons indicate the status of a task, by clicking on it an end user can change the status of the task. This part of the formatting makes use of the customRowAction property with the option setValue as you can see in the following JSON excerpt.
    {
    "elmType": "div",
    "customRowAction": {
      "action": "setValue",
      "actionInput": {
    	"Status": "=if([$Status], '0', '1')"
      }
    },
    "attributes": {
      "class": "ms-fontColor-themePrimary",
      "iconName": "=if([$Status], 'SkypeCircleCheck', 'CircleRing')"
    }
    
  3. Title – The title displays everything that is stored in the title column, it has a maximum capacity of 255 characters.
  4. Date – The date displays the information stored in the calculated column Date, besides this information it also informs the user if a task is Overdue, this happens when the due date is greater than the current day. Overdue tasks that are not completed display the date in red. Tasks scheduled for the current day do not display the date, replacing it instead by a blue today word. Due date is not a mandatory field in the To Do list and tasks without it, do not display any information in this area. This formatting is displayed conditionally and in following JSON excerpt you can observe how it was implemented.
      {
    	"elmType": "span",
    	"style": {
    	  "font-weight": "=if([$Status], '200', if([$Duedate] < @now, '500' , '200'))",
    	  "font-size": "12px",
    	  "overflow": "hidden",
    	  "margin-top": "2px",
    	  "white-space": "nowrap",
    	  "color": "=if([$Status], '', if(toLocaleDateString([$Duedate])==toLocaleDateString(@now),'#465efc',if([$Duedate] < @now, '#db3a29' , '')))",
    	  "display": "=if([$Duedate],'','none')"
    	},
    	"txtContent": {
    	  "operator": "+",
    	  "operands": [
    		"=if(toLocaleDateString([$Duedate])==toLocaleDateString(@now),'Today ',if([$Duedate] > @now, 'Due, ' , 'Overdue, '))",
    		"=if(toLocaleDateString([$Duedate])!=toLocaleDateString(@now), [$Date], '')"
    	  ]
    	}
      }
    
  5. Notes/Description – This icon is only shown for the tasks that include a note or description in the list.
  6. Tags – The tag icon and name are only shown for the tasks that include this value defined in the list.
  7. Attachments – This icon like number 5 and 6 is only displayed when there are files attached to the list item. Unlike the other icons this one opens the item detail panel on click, giving the user access to the attached files. In the following JSON you can observe the conditional display and the action to open the item detail implemented using the customRowAction property with the value defaultClick.
      {
    	"elmType": "span",
    	"style": {
    	  "font-size": "11px",
    	  "display": "=if([$Attachments] == '0', 'none', '')",
    	  "cursor": "pointer"
    	},
    	"attributes": {
    	  "iconName": "Attach"
    	},
    	"customRowAction": {
    	  "action": "defaultClick"
    	}
      }
    
  8. Comments – This icon has two different representations, if displayed in color and with quotation marks it means that the list item has comments associated to it, if displayed in black with the plus sign it means that the item has not received any comments yet. When the user clicks on it, it opens the default item detail panel which gives access the comment panel. This feature like number 7 was implemented using the customRowAction property with the value defaultClick.
  9. Favorites – These icons indicate the favorite status of a task, by clicking on it, the task gets moved to the top of the list and changes the icon. This part of the formatting makes use of the customRowAction property with the option setValue similar to the one already explained in number 2.
  10. Item detail – This area of the formatting is displayed when the user hovers an item. It displays the complete detail of a task including the metadata related with the created and updated dates. This popup was implemented using the customCardProps property as exemplified in the following JSON.
        "customCardProps": {
          "formatter": {
            "elmType": "div",
            "style": {
              "padding": "20px"
            },
            "children": [
              ...
            ]
          },
          "openOnEvent": "hover",
          "directionalHint": "bottomCenter",
          "isBeakVisible": true,
          "beakStyle": {
            "backgroundColor": "white"
          }
        }
    
  11. Edit Form – The edit form allows the end user to edit all the fields of a task. The process is simple as clicking in one of item values in the pop up, depending of the type of column the input to introduce or modify the existing value is shown differently. In the following picture you can see the selector for the category.
    Select Microsoft To Do List category

    This feature was implemented using the inlineEditField property as shown in the following JSON excerpt.

     {
    	"elmType": "span",
    	"style": {
    	  "font-size": "12px",
    	  "margin-left": "5px",
    	  "font-weight": "200",
    	  "font-style": "=if([$Category]=='','Italic','')"
    	},
    	"txtContent": "=if([$Category]=='','Pick a category',[$Category])",
    	"inlineEditField": "[$Category]"
      }
    
  12. Create and Update details – This information is generated automatically for each list item when it is created and modified, despite not being visible by default in a list view you will be able to access to each one of the fields using the internal name as described in the following list:
    • Created by – [$Author]
    • Creation date – [$Created]
    • Modified by – [$Modified]
    • Modification date – [$Editor]
  13. Delete – This button opens a confirmation pop-up to remove the list item. It was implemented using the property customRowAction with the action delete as exemplified in the following JSON.
    {
    	"elmType": "span",
    	"style": {
    	  "font-size": "14px",
    	  "cursor": "pointer",
    	  "position": "absolute",
    	  "bottom": "20px",
    	  "right": "20px"
    	},
    	"attributes": {
    	  "iconName": "Delete"
    	},
    	"customRowAction": {
    	  "action": "delete"
    	}
      }
    

With this explanation we conclude the implementation of a To Do list using Microsoft Lists, if you would like to install this list as a template so all members of your organization can take advantage of it check out Part 3 of this article.

If you want to see this formatting in action before executing it in your tenant have a look to the video I’ve recorder explaining the entire process.

Download Template


No comments yet

Leave a Reply


I've been working with Microsoft Technologies over the last ten years, mainly focused on creating collaboration and productivity solutions that drive the adoption of Microsoft Modern Workplace.