Tackling Recursion in FHIR Using Blazor Components

This post has been republished via RSS; it originally appeared at: Healthcare and Life Sciences Blog articles.

MicrosoftTeams-image.png

Overview

Many FHIR include complex nesting.  For example the Item component of Questionnaire can either be a single item or an array of more items, which can themselves also be arrays of more items, with even more arrays below.  Arrays all the way down!  A previous post (FHIR + Blazor + Recursion = Rendering a Questionnaire ) showed a method to render objects with complex nesting: using Dynamic Fragments.  This article shows an alternative method: using self-referential components.

Why use components?

Components will allow easier reuse than Dynamic Fragment rendered pages.  Imagine a case where we want to both create new and allow updates of complex nested children.  If we use components, we can easily adapt a component for both EDIT and CREATE.  More reuse means less code!

How it’ll work

We’ll create a parent component.  The parent component will render the child component. 

ParentChildComponents.png

Parent Component;

 

 

<span>@Parent.Title</span> <p>@Parent.Description</p> @{ int childNum=1; } @foreach(var child in Parent.children){ Child #@childNum ChildComponent child=child /> childNum++; }

 

 

AND HERE’s THE MAGIC:

Child Component:

 

 

<span>@Child.Title</span> @foreach(var child in Child.children){ <ChildComponent child=child /> }

 

 


The child component will render additional child components.

FHIR Specific Example

Check out the code below from (FHIRBlaze)

Parent Component (QuestionnaireComponent)

 

 

<EditForm Model=Questionnaire OnValidSubmit=Submit> <label class="col-sm-2 form-label">Title:</label> <InputText @bind-Value=Questionnaire.Title /> @foreach(var item in Questionnaire.Item) { <div class="border border-primary rounded-left rounded-right p-2"> <div class="row"> <div class="col-sm-12">@GetHeader(item)</div> </div> <div class="row"> <div class="col-sm-11"> <ItemDisplay ItemComponent=item/> </div> <div class="col-sm-1"> <button type="button" class="btn btn-primary" @onclick="()=>RemoveItem(item)"> <span class="oi oi-trash" /> </button> </div> </div> </div> } <div> <ItemTypeComponent ItemSelected="AddItem" /> </div> <br/> <button type="submit">Submit</button> </EditForm>

 

 

 

Note the ItemDisplay component.

Child Component  ( ItemDisplay )

 

 

<div class="card-body"> <label class="sr-only" >@GetTitleText(ItemComponent)</label> <input type='text' required class='form-control' id='question' placeholder=@GetTitleText(ItemComponent) @bind-value='ItemComponent.Text' > <label class="sr-only">LinkId:</label> <input type='text' required class='form-control' placeholder='linkId' @bind-value='ItemComponent.LinkId'> @switch (ItemComponent.Type) { case Questionnaire.QuestionnaireItemType.Group: foreach(var groupitem in ItemComponent.Item) { <div class="border border-primary rounded-left rounded-right p-2"> <div class="row"> <div class="col-sm">@GetHeader(groupitem)</div> </div> <div class="row"> <div class="col-sm-11"> <ItemDisplay ItemComponent=groupitem/> </div> <div class="col-sm"> <button type="button" class="btn btn-primary" @onclick="()=>ItemComponent.Item.Remove(groupitem)"> <span class="oi oi-trash" /> </button> </div> </div> </div> } break; case Questionnaire.QuestionnaireItemType.Choice: int ansnum= 1; @foreach (var opt in ItemComponent.AnswerOption) { <div class="row"> <form class="form-inline"> <div class="col-sm-1">#@ansnum</div> <div class="col-sm-10"><AnswerCoding Coding=(Coding)opt.Value /></div> <div class="col-sm-1"><button type="button" class="btn btn-primary" @onclick="()=>ItemComponent.AnswerOption.Remove(opt)"><span class="oi oi-trash" /></button></div> </form> </div> ansnum++; } <button type="button" @onclick=AddAnswer >Add Choice</button> break; default: break; } @if (ItemComponent.Type.Equals(Questionnaire.QuestionnaireItemType.Group)) { <div> <ItemTypeComponent ItemSelected="AddItem" /> </div> } </div>

 

 

Line 58 is the key line.   This component renders itself!

 

Caveats

With this you should be able to quickly render nested Item after nested Item.  But there are some caveats you should know.

 

#1: Memory Consumption

              As you render each nesting- all those objects are loaded into memory.  If a FHIR has an unknown number of children- each of which could have its own children, you could potentially consume large amounts of memory.  This is a particular problem because you could be rendering on a phone.

Suggested Mitigation: Consider rendering to a max depth and a max number of children.  Render “see more” type links to allow the user to see remaining children

#2: Display Clipping

              The standard approach is to indent children.  But if you have 5 levels of nesting and the app is rendered on mobile than your 5th level children may show up in a single character column.  If you render 100 levels of nested children, your final level may not render at all.

UIClipping.png

Suggested Mitigation:  Consider an alternative to displaying all children.  For example- consider using collapsible sections to show children.

#3: Labeling Problems

              If you’re allowing Edit of a component with complex nesting it may be difficult for a user to remember which level of children, they are in. For example, imagine the following are rendered as cards: Parent 1: Child 1: Child 2: Child 3: Child 4: Child 5.

Suggested Mitigation:  Consider using borders and labeling to help users determine which child is currently selected       

REMEMBER: these articles are REPUBLISHED. Your best bet to get a reply is to follow the link at the top of the post to the ORIGINAL post! BUT you're more than welcome to start discussions here:

This site uses Akismet to reduce spam. Learn how your comment data is processed.