2019-04-09

In this article, we are going to carry on the work from part 1 to build this view from a SharePoint list. In the first part, we built the structure that will hold all the list item column values, now it is time to add the values in so by the end of it we have a view looking like this…

 

Desktop view

 

Responsive mobile view

Referencing column values

Pulling through list item column values is the most important step in the process as we want to display the data to the users. To pull through a column value you reference it in JSON by its internal column name. To get the internal name of a column go to the list settings and click on the name of the column you want to reference.

Once you have clicked on the column name you’ll be in the edit column page for the column.

Go to the end of the URL in the address bar and you’ll find the internal name.

Note: In your production tenant it is highly unlikely you have spaces in list column internal names but I wanted to show you how it will look if you did. Notice how the space between the words are replaced with x0020 as the internal column namespaces and special characters are encoded. SharePoint does this to use them in XML as attributes.

To use it in the JSON we just add a $ sign at the start of the column name and encapsulate it in square brackets. It is case sensitive. For example –

  • [$Title]
  • [$Maximum_x0020_capacity]

This next part will be broken down div by div.
Div 1 will be the image.
Div 2 will be the title, address and capacity.
Div 3 will be the rating, price and URL link.

If you are writing JSON as we go along and it is coming up with errors you can validate it against my JSON using a code difference checker. Just paste them in side by side and hit run, it’ll tell you what the difference is. There are a lot of brackets and commas we are dealing with and characters can be missed.

DIV ONE

Image

Elmtype The elmtype will be “img”
Style
  •  “width”: “260px”,
  •  “height”: “160px”
TxtContent N/A
Attributes “src”: “[$Picture]”
Notes Because we have used the ‘img’ elmtype we can use the ‘src’ attribute. src is the link to the image.

The style properties will keep the image in the div

JSON
{
  "elmType": "img",
  "style": {
    "width": "260px",
    "height": "160px"
  },
  "attributes": {
    "src": "[$Picture]"
  }
}

 

This is what the JSON should look like for div 1, as the image element is a child of div 1 we have to place it in a child object. Review the JSON below to see how it is done.

{
  "elmType": "div",
  "__comment": "Div 1",
  "style": {
    "flex-grow": "1",
    "display": "flex",
    "flex-direction": "column",
    "flex-wrap": "nowrap",
    "align-items": "stretch",
    "max-width": "260px"
  },
  "children": [
    {
      "elmType": "img",
      "style": {
        "width": "260px",
        "height": "160px"
      },
      "attributes": {
        "src": "[$Picture]"
      }
    }
  ]
}

If you paste this JSON in the format view section, you’ll now see this…

DIV TWO

Create a child object under div 2 as we did for div 1. This is where we will pull through the title, location and capacity values, all stacked one below the other.

Title

Elmtype The title is a single line of text, a span is perfect for holding text so that will be used in this instance.
Style
  • “display”: “=if([$Title] == ”, ‘none’, ‘block’)”
TxtContent This is where the title is referenced, nothing else is required other than [$Title]
Attributes Office-UI fabric is a great resource when wanting a consistent look so we’ll use the following classes

  • ms-fontSize-l
  • ms-fontWeight-semibold
  • ms-fontColor-neutralPrimary
Notes Adding display:block in the style will make the span element stretch out to the left and right as far as it can inside div 2, also the span will stack neatly above or below other elements in the div if the page is viewed on a smaller screen i.e mobile.

We can take it one step further and add an ‘if statement’ in the display property so if there is no value then don’t display the block at all, this is remove any blank spaces in the view if there is values missing in the SharePoint list.
The way it is written is “=if([internalcolumnname] == ‘ ‘, ‘none’, ‘block’)” basically the formula is saying if the value of whatever is in [] is blank then display is equal to none, else the display will equal block.

JSON
{
  "elmType": "span",
  "txtContent": "=[$Title]",
  "style": {
    "display": "=if([$Title] == '', 'none', 'block')"
  },
  "attributes": {
    "class": "ms-fontSize-l ms-fontWeight-semibold ms-fontColor-neutralPrimary"
  }
}

 

Location

Elmtype Span
Style
  •  “display”: “block”
  •  “padding-top”: “20px”
TxtContent “Location”
Attributes
  • ms-fontSize-l
  • ms-fontWeight-semibold
  • ms-fontColor-neutralPrimary
Notes This is just a permanent text element for UI purposes, it will help the user understand what the next element is below it which is the address. As we are not pulling any list values through with this element we do not require the if statement in the style properties, as the text will always be there.
JOSN
{
  "elmType": "span",
  "txtContent": "Location",
  "style": {
    "display": "block",
    "padding-top": "20px"
  },
  "attributes": {
    "class": "ms-fontSize-l ms-fontWeight-semibold ms-fontColor-neutralPrimary"
  }
}

 

Address

Elmtype span
Style
  • “display”: “=if([$Address] == ”, ‘none’, ‘block’)”
TxtContent [$Address]
Attributes
  • ms-fontSize-l
  • ms-fontWeight-semibold
  • ms-fontColor-neutralPrimary
Notes This will pull through the address column value
JSON
{
  "elmType": "span",
  "txtContent": "[$Address]",
  "style": {
    "display": "=if([$Address] == '', 'none', 'block')"
  }
}

 

Map icon

Elmtype a
Style
  • “font-size”: “15px”,
  • “padding-top”: “5px”,
  • “cursor”: “pointer”
TxtContent N/A
Attributes
  •  “iconName”: “MapPin”,
  •  “class”: “ms-fontColor-themePrimary”,
  • “target”: “_blank”,
  •  “href”: “=’http://maps.google.com/?q=’ + ( [$Title] + ‘ ‘)  + ( [$Address])”
Notes Using an a tag element  we can build a clickable URL link behind an icon. The href attribute is building the URL string that will be passed to google maps to search for the title and address column values when the map icon link is clicked.

Configuring the element like this saves the user who manages the data in the list having to find where the exact address and then storing that data in a new column. If the data is already there then we should try to utilise it. You can find the full range of icons that can be used here – https://developer.microsoft.com/en-us/fabric#/styles/icons

JSON
{
  "elmType": "a",
  "attributes": {
    "iconName": "MapPin",
    "class": "ms-fontColor-themePrimary",
    "target": "_blank",
    "href": "='http://maps.google.com/?q=' + ( [$Title] + ' ')  + ( [$Address])"
  },
  "style": {
    "font-size": "15px",
    "padding-top": "5px",
    "cursor": "pointer"
  }
}

 

Capacity

Elmtype Span
Style
  • “display”: “=if([$Maximum_x0020_capacity] == ”, ‘none’, ‘block’)”,
  • “padding-top”: “20px”
TxtContent “=’Capacity ‘ + [$Maximum_x0020_capacity]”
Attributes   N/A
Notes As we want the word capacity next to the capacity number so the user has a better experience we can write free text next to the list value by writing the TxtContent in the following format “ =‘your free text’ + [$internalcolumnnaem]”
JSON
{
  "elmType": "span",
  "txtContent": "='Capacity ' + [$Maximum_x0020_capacity]",
  "style": {
    "display": "=if([$Maximum_x0020_capacity] == '', 'none', 'block')",
    "padding-top": "20px"
  }
}

 

This is what Div 2 should look like, all of the elements we just created sit in one child object so they will not escape div 2.

{
  "elmType": "div",
  "_comment_": "Div 2",
  "style": {
    "flex-grow": "1",
    "display": "flex",
    "flex-direction": "column",
    "flex-wrap": "nowrap",
    "align-items": "center",
    "max-width": "360px",
    "min-width": "205px"
  },
  "children": [
    {
      "elmType": "span",
      "txtContent": "=[$Title]",
      "style": {
        "display": "=if([$Title] == '', 'none', 'block')"
      },
      "attributes": {
        "class": "ms-fontSize-l ms-fontWeight-semibold ms-fontColor-neutralPrimary"
      }
    },
    {
      "elmType": "span",
      "txtContent": "Location",
      "style": {
        "display": "block",
        "padding-top": "20px"
      },
      "attributes": {
        "class": "ms-fontSize-l ms-fontWeight-semibold ms-fontColor-neutralPrimary"
      }
    },
    {
      "elmType": "span",
      "txtContent": "[$Address]",
      "style": {
        "display": "=if([$Address] == '', 'none', 'block')"
      }
    },
    {
      "elmType": "a",
      "attributes": {
        "iconName": "MapPin",
        "class": "ms-fontColor-themePrimary",
        "target": "_blank",
        "href": "='http://maps.google.com/?q=' + ( [$Title] + ' ')  + ( [$Address])"
      },
      "style": {
        "font-size": "15px",
        "padding-top": "5px",
        "cursor": "pointer"
      }
    },
    {
      "elmType": "span",
      "txtContent": "='Capacity ' + [$Maximum_x0020_capacity]",
      "style": {
        "display": "=if([$Maximum_x0020_capacity] == '', 'none', 'block')",
        "padding-top": "20px"
      }
    }
  ]
}

If you paste this JSON in the format view section of the list you’ll now see this

DIV THREE

In the 3rd div we need to place the rating of the venue and the star icon side by side, to achieve this we are going to create another small div inside div 3 and place the Rating and icon elements inside that div.

This is the order that the elements need to be written in JSON, we need to create another child object at step 3 then after step 4 will close the child object off and carry on with step 5.

Rating & star icon div

Elmtype Div
Style
  • “display”: “block”,
  •  “font-size”: “15px”,
  •  “font-weight”: “bold”
TxtContent N/A
Attributes N/A
Notes This will create the div that will hold the 2 elements
JSON
{
  "elmType": "div",
  "_comment_": "RATING & STAR DIV",
  "style": {
    "display": "block",
    "font-size": "15px",
    "font-weight": "bold"
  }
}

 

Now it is important the create another child element under this div that you have just created before specifying the span and elements as they are children of the new div.

Rating

Elmtype span
Style
  •  “padding-right”: “5px”,
  • “display”: “=if([$Rating] == ”, ‘none’, ‘block’)”,
TxtContent “[$Rating]”
Attributes N/A
Notes This will bring the rating list item column value through and give it 5 pixels distance from the star icon
 
{
  "elmType": "span",
  "style": {
    "padding-right": "5px",
    "display": "=if([$Rating] == '', 'none', 'block')"
  }
}

 

Star icon

Elmtype span
Style N/A
TxtContent N/A
Attributes
  • “iconName”: “FavoriteStarFill”,
  •  “class”: “ms-fontColor-themePrimary”
Notes This will bring the icon of a star. You can find the full range of icons here – https://developer.microsoft.com/en-us/fabric#/styles/icons
JSON
{
  "elmType": "span",
  "attributes": {
    "iconName": "FavoriteStarFill",
    "class": "ms-fontColor-themePrimary"
  }
}

 

Now, close the child element off with a square bracket so that only those 2 elements are encapsulated in that div element. This is important otherwise the rest of the elements that you create below would sit in the small div we created, we want them to sit below it.

Price text

Elmtype Span
Style
  • “display”: “block”,
  •  “padding-top”: “20px”
TxtContent “Price”
Attributes
  • ms-fontSize-l
  • ms-fontWeight-semibold
  • ms-fontColor-neutralPrimary
Notes Again like the location element, this is just a text element for UI purposes, it will help the user understand what the next element is which is the price. We don’t want to pull any list values through with this element.
 
{
  "elmType": "span",
  "txtContent": "Price",
  "style": {
    "display": "block",
    "padding-top": "20px"
  },
  "attributes": {
    "class": "ms-fontSize-l ms-fontWeight-semibold ms-fontColor-neutralPrimary"
  }
}

 

Price – list item value

Elmtype Span
Style
  • “display”: “=if([$price] == ”, ‘none’, ‘block’)”
  • “padding-top”: “20px”
TxtContent “=’£ ‘ + [$Price]”
Attributes  

N/A

Notes Adding the currency symbol as free text next to the value will make the UI look better in this instance. Always try to think how a user will perceive the data being shown and how you can help.
JSON
{
  "elmType": "span",
  "txtContent": "='£' +[$Price]",
  "style": {
    "display": "=if([$price] == '', 'none', 'block')"
  }
}

 

Button

Note: Although I have called a button it is more of a clickable link, buttons are limited as to what they can do in modern view formatting and being able to link to an external URL is one of those limitations.

Elmtype a
Style
  • “border”: “none”       
  • “padding-top”: “32px”        
  • “background-color”: “transparent”      
  • “color”: “#0078d7”     
  • “padding-left”: “0px”      
  • “text-align”: “left”       
  •  “cursor”: “pointer”
TxtContent “txtContent”: “View venue details”,
Attributes
  • “target”: “_blank”
  • “href”: “[$Venue_x0020_link]”
Notes when clicked, the button opens the link specified in the venue link. setting the target to blank in the attributes means it will open the link up in a new browser window.
 
{
  "elmType": "a",
  "txtContent": "View Venue Details",
  "style": {
    "border": "none",
    "padding-top": "32px",
    "background-color": "transparent",
    "color": "#0078d7",
    "padding-left": "0px",
    "text-align": "left",
    "cursor": "pointer"
  },
  "attributes": {
    "target": "_blank",
    "href": "[$Venue_x0020_link]"
  }
}

 

That is all the formatting complete, your JSON should now look like this and display the view.

{
  "$schema": "https://developer.microsoft.com/json-schemas/sp/view-formatting.schema.json",
  "hideSelection": true,
  "hideColumnHeader": true,
  "rowFormatter": {
    "elmType": "div",
    "_comment_": "MAIN DIV",
    "attributes": {
      "class": "ms-bgColor-neutralLighter"
    },
    "style": {
      "display": "flex",
      "flex-wrap": "wrap",
      "align-items": "stretch",
      "flex-direction": "row",
      "padding": "20px",
      "margin-bottom": "16px",
      "max-width": "850px",
      "border-radius": "8px"
    },
    "children": [
  {
        "elmType": "div",
        "__comment": "DIV 1",
        "style": {
          "flex-grow": "1",
          "display": "flex",
          "flex-direction": "column",
          "flex-wrap": "nowrap",
          "align-items": "stretch",
          "max-width": "260px"
        },
        "children": [
          {
            "elmType": "img",
            "style": {
              "width": "260px",
              "height": "160px"
            },
            "attributes": {
              "src": "[$Picture]"
            }
          }
        ]
      },
                    
      {
        "elmType": "div",
        "__comment": "DIV 2",
        "style": {
          "flex-grow": "1",
          "display": "flex",
          "flex-direction": "column",
          "flex-wrap": "nowrap",
          "align-items": "center",
          "max-width": "360px",
          "min-width": "205px"
        },
        "children": [
          {
            "elmType": "span",
            "txtContent": "=[$Title]",
            "style": {
              "display": "block"
            },
            "attributes": {
              "class": "ms-fontSize-l ms-fontWeight-semibold ms-fontColor-neutralPrimary"
            }
          },
          {
            "elmType": "span",
            "txtContent": "Location",
            "style": {
              "display": "block",
              "padding-top": "20px"
            },
            "attributes": {
              "class": "ms-fontSize-l ms-fontWeight-semibold ms-fontColor-neutralPrimary"
            }
          },
          {
            "elmType": "span",
            "txtContent": "[$Address]"
          },
          {
            "elmType": "a",
            "attributes": {
              "iconName": "MapPin",
              "class": "ms-fontColor-themePrimary",
              "target": "_blank",
              "href": "='http://maps.google.com/?q=' + ( [$Title] + ' ')  + ( [$Address])"
            },
            "style": {
              "font-size": "15px",
              "padding-top": "5px",
              "cursor": "pointer"
            }
          },
          {
            "elmType": "span",
            "txtContent": "='Capacity ' + [$Maximum_x0020_capacity]",
            "style": {
              "display": "block",
              "padding-top": "20px"
            }
          }
        ]
      },
      {
        "elmType": "div",
        "__comment": "DIV 3",
        "style": {
          "flex-grow": "1",
          "display": "flex",
          "flex-direction": "column",
          "align-items": "center",
          "margin-top": "10px",
          "max-width": "310px",
          "min-width": "155px"
        },
        "children": [
          {
            "elmType": "div",
            "__comment": "RATING & STAR DIV",
            "style": {
              "display": "block",
              "font-size": "15px",
              "font-weight": "bold"
            },
            "children": [
              {
                "elmType": "span",
                "txtContent": "[$Rating]",
                "style": {
                  "padding-right": "5px"
                }
              },
              {
                "elmType": "span",
                "attributes": {
                  "iconName": "FavoriteStarFill",
                  "class": "ms-fontColor-themePrimary"
                }
              }
            ]
          },
          {
            "elmType": "span",
            "txtContent": "Price",
            "style": {
              "display": "block",
              "padding-top": "20px"
            },
            "attributes": {
              "class": "ms-fontSize-l ms-fontWeight-semibold ms-fontColor-neutralPrimary"
            }
          },
          {
            "elmType": "span",
            "txtContent": "='£' +[$Price]",
            "style": {
              "display": "block"
            }
          },
          {
            "elmType": "a",
            "txtContent": "View Venue Details",
            "style": {
              "border": "none",
              "padding-top": "32px",
              "background-color": "transparent",
              "color": "#0078d7",
              "padding-left": "0px",
              "text-align": "left",
              "cursor": "pointer"
            },
            "attributes": {
              "target": "_blank",
              "href": "[$Venue_x0020_link]"
            }
          }
        ]
      }
    ]
  }
}

You can also find the source code here – https://github.com/Jamie-Bray/JSON-view-tutorial

I hope you found this tutorial useful.  I wanted to build something that incorporates most, if not all, scenarios you’ll face when building a JSON view so when the time comes you’ll be able to achieve a range of different possibilities by re-using parts of this tutorial or the whole thing. To recap we have done the following- 

  • Arranged a view into different sections using divs
  • Pulled through column values
  • Managed images
  • Made sure the view is responsive for mobile using flexbox
  • Added text so it can sit beside the column values
  • Added descriptions in the view for UI purposes
  • Added buttons that link to external URLs
  • Built href links using column values and using an icon as a clickable link
  • Placed elements side by side

I had the opportunity to work with JSON view formatting when trying to find a solution for a team at the start of the year, before that, I was unfamiliar with how to read or write JSON.
This article has only been possible to write due to other people in the Office 365 community who have shared their knowledge via articles before me, these saved some potentially frustrating evenings for me while I studied to gain a better grasp of JSON and for that I want to say thank you to everyone who contributes to the community, I hope my efforts have the same impact.

I have listed some great Microsoft resources below which may be of use.

https://docs.microsoft.com/en-us/sharepoint/dev/declarative-customization/view-formatting

https://github.com/SharePoint/sp-dev-docs/blob/master/docs/declarative-customization/view-formatting.md

Any questions please leave them below in the comments…

About the author 

Jamie Bray

Office 365 Collaboration Specialist at Parliamentary Digital Service