FHIR + Blazor + Recursion = Rendering a Questionnaire

Published Nov 16 2021 05:33 PM 1,313 Views
Microsoft

FHIRBlazor.png

 

Rendering a HL7 FHIR Questionnaire component isn't as simple as creating a normal HTML form as each Item component can both be a parent element or a child element.

Item can be of type: String, Textarea, Choice, Boolean, Date... etc.... These are easy enough. But an Item can also be a type of Group, which can have the usual types mentioned before AND zero or more Group types.

DaveUpton_0-1637112200057.png

 

This makes the rendering of Questionnaire's json object a little bit tricky.

RenderFragments

It's a concept of Templated Controls. Using RenderFragments you can componentize specific portion of HTML. A RenderFragment represents a chunk of Razor markup that can then be rendered by the component

Recursion to save the day

What we've come up with combines RenderFragments and recursion to traverse the nested, tree-structure until it finds an item type that is not of type Group. Then we'll use the recursive function to append to our RenderFragment along the way.


On the front end:

 

 

 

 

HTML/Blazor Front end
<EditForm Model=@QResponse OnSubmit=@SubmitQuestionnaireAsync>
   @DynamicFragment
   .
   .
</EditForm>

// Frontend @code block
private RenderFragment DynamicFragment;
private void RenderComponent(List<ItemComponent> items)
{
    DynamicFragment += GetChildItems(items);       
}

private RenderFragment GetChildItems(List<ItemComponent> items) => __builder =>
{
    @foreach (var item in items)
    {
        @if (item.Item.Count > 0)
        {
            @GetTitle(item.Text);
            @GetChildItems(item.Item);
        }
        else
        {
            @GetAnswerDisplay(item);
        }
        <br/>
    }
};

 

 

 

 

 

We have a DynamicFragment inside the EditForm element. That is a RenderFragment that we'll be using to build the form.

 

We have two key functions: RenderComponent and GetChildItems. First function updates the RenderFragments with html elements spewed by the second funtion.

Second function is the recursive function that goes through all the Questionnaire items and determines whether it should render an HTML element. If it finds the Item contains another Item with a count greater than 0 we know the type is of 'Group'. The recursion starts again.

Blazor is ultimately going to express that Razor code as a RenderTreeBuilder.

How do you kick off the render?

On the code behind:

 

 

 

 

protected override async Task OnInitializedAsync()
{
  try
  {
    RenderComponent(QResponse.Item);
    .
    .
}

 

 

 

 


During initialization you can simply call the function RenderComponent with all the Questionnaire items!

And here is how it renders.

DaveUpton_1-1637112732647.png

 

 

We hope this helps you as you try to tackle the complexity of Questionnaire component of FHIR.

%3CLINGO-SUB%20id%3D%22lingo-sub-2971861%22%20slang%3D%22en-US%22%3EFHIR%20%2B%20Blazor%20%2B%20Recursion%20%3D%20Rendering%20a%20Questionnaire%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-2971861%22%20slang%3D%22en-US%22%3E%3CP%3E%3CSPAN%20class%3D%22lia-inline-image-display-wrapper%20lia-image-align-center%22%20image-alt%3D%22FHIRBlazor.png%22%20style%3D%22width%3A%20322px%3B%22%3E%3CIMG%20src%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fimage%2Fserverpage%2Fimage-id%2F327785i855334CE22545FDE%2Fimage-dimensions%2F322x182%3Fv%3Dv2%22%20width%3D%22322%22%20height%3D%22182%22%20role%3D%22button%22%20title%3D%22FHIRBlazor.png%22%20alt%3D%22FHIRBlazor.png%22%20%2F%3E%3C%2FSPAN%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%3CSPAN%20style%3D%22font-family%3A%20inherit%3B%22%3ERendering%20a%20%3C%2FSPAN%3E%3CA%20style%3D%22font-family%3A%20inherit%3B%20background-color%3A%20%23ffffff%3B%22%20href%3D%22https%3A%2F%2Fwww.hl7.org%2Ffhir%2Fquestionnaire.html%22%20target%3D%22_blank%22%20rel%3D%22noopener%20nofollow%20noreferrer%22%3EHL7%20FHIR%20Questionnaire%3C%2FA%3E%3CSPAN%20style%3D%22font-family%3A%20inherit%3B%22%3E%26nbsp%3Bcomponent%20isn't%20as%20simple%20as%20creating%20a%20normal%20HTML%20form%20as%20each%26nbsp%3B%3C%2FSPAN%3E%3CA%20style%3D%22font-family%3A%20inherit%3B%20background-color%3A%20%23ffffff%3B%22%20href%3D%22https%3A%2F%2Fwww.hl7.org%2Ffhir%2Fquestionnaire-definitions.html%23Questionnaire.item%22%20target%3D%22_blank%22%20rel%3D%22noopener%20nofollow%20noreferrer%22%3EItem%3C%2FA%3E%3CSPAN%20style%3D%22font-family%3A%20inherit%3B%22%3E%26nbsp%3Bcomponent%20can%20both%20be%20a%20parent%20element%20or%20a%20child%20element.%3C%2FSPAN%3E%3C%2FP%3E%0A%3CUL%20class%3D%22lia-list-style-type-square%22%3E%0A%3CLI%3ESee%20it%20in%20action%20in%20our%26nbsp%3B%3CA%20href%3D%22https%3A%2F%2Fgithub.com%2Fmicrosoft%2Ffhirblaze%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3EFhirBlaze%3C%2FA%3E%26nbsp%3Brepo%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3CP%3EItem%20can%20be%20of%20type%3A%20String%2C%20Textarea%2C%20Choice%2C%20Boolean%2C%20Date...%20etc....%20These%20are%20easy%20enough.%20But%20an%20Item%20can%20also%20be%20a%20type%20of%20Group%2C%20which%20can%20have%20the%20usual%20types%20mentioned%20before%20AND%20zero%20or%20more%20Group%20types.%3CBR%20%2F%3E%3CBR%20%2F%3E%3C%2FP%3E%0A%3CP%3E%3CSPAN%20class%3D%22lia-inline-image-display-wrapper%20lia-image-align-inline%22%20image-alt%3D%22DaveUpton_0-1637112200057.png%22%20style%3D%22width%3A%20400px%3B%22%3E%3CIMG%20src%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fimage%2Fserverpage%2Fimage-id%2F327464i0D568459AFF18B50%2Fimage-size%2Fmedium%3Fv%3Dv2%26amp%3Bpx%3D400%22%20role%3D%22button%22%20title%3D%22DaveUpton_0-1637112200057.png%22%20alt%3D%22DaveUpton_0-1637112200057.png%22%20%2F%3E%3C%2FSPAN%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EThis%20makes%20the%20rendering%20of%20Questionnaire's%20json%20object%20a%20little%20bit%20tricky.%3CBR%20%2F%3E%3CBR%20%2F%3E%3C%2FP%3E%0A%3CH2%20id%3D%22toc-hId--293427182%22%20id%3D%22toc-hId--293374481%22%3E%3CSTRONG%3E%3CA%20href%3D%22https%3A%2F%2Fwww.syncfusion.com%2Ffaq%2Fblazor%2Fcomponents%2Fhow-do-you-create-elements-dynamically-in-blazor%22%20target%3D%22_blank%22%20rel%3D%22noopener%20nofollow%20noreferrer%22%3ERenderFragments%3C%2FA%3E%3C%2FSTRONG%3E%3C%2FH2%3E%0A%3CP%3EIt's%20a%20concept%20of%20Templated%20Controls.%20Using%20RenderFragments%20you%20can%20componentize%20specific%20portion%20of%20HTML.%20A%26nbsp%3BRenderFragment%26nbsp%3Brepresents%20a%20chunk%20of%20Razor%20markup%20that%20can%20then%20be%20rendered%20by%20the%20component%3CBR%20%2F%3E%3CBR%20%2F%3E%3C%2FP%3E%0A%3CH3%20id%3D%22toc-hId-397134292%22%20id%3D%22toc-hId-397186993%22%3E%3CSTRONG%3ERecursion%20to%20save%20the%20day%3C%2FSTRONG%3E%3C%2FH3%3E%0A%3CP%3EWhat%20we've%20come%20up%20with%20combines%20RenderFragments%20and%20recursion%20to%20traverse%20the%20nested%2C%20tree-structure%20until%20it%20finds%20an%20item%20type%20that%20is%20not%20of%20type%20Group.%20Then%20we'll%20use%20the%20recursive%20function%20to%20append%20to%20our%20RenderFragment%20along%20the%20way.%3C%2FP%3E%0A%3CP%3E%3CBR%20%2F%3EOn%20the%20front%20end%3A%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CPRE%20class%3D%22lia-code-sample%20language-csharp%22%3E%3CCODE%3EHTML%2FBlazor%20Front%20end%0A%3CEDITFORM%20model%3D%22%40QResponse%22%20onsubmit%3D%22%40SubmitQuestionnaireAsync%22%3E%0A%20%20%20%40DynamicFragment%0A%20%20%20.%0A%20%20%20.%0A%3C%2FEDITFORM%3E%0A%0A%2F%2F%20Frontend%20%40code%20block%0Aprivate%20RenderFragment%20DynamicFragment%3B%0Aprivate%20void%20RenderComponent(List%3CITEMCOMPONENT%3E%20items)%0A%7B%0A%20%20%20%20DynamicFragment%20%2B%3D%20GetChildItems(items)%3B%20%20%20%20%20%20%20%0A%7D%0A%0Aprivate%20RenderFragment%20GetChildItems(List%3CITEMCOMPONENT%3E%20items)%20%3D%26gt%3B%20__builder%20%3D%26gt%3B%0A%7B%0A%20%20%20%20%40foreach%20(var%20item%20in%20items)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%40if%20(item.Item.Count%20%26gt%3B%200)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%40GetTitle(item.Text)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%40GetChildItems(item.Item)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20else%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%40GetAnswerDisplay(item)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%3CBR%20%2F%3E%0A%20%20%20%20%7D%0A%7D%3B%0A%3C%2FITEMCOMPONENT%3E%3C%2FITEMCOMPONENT%3E%3C%2FCODE%3E%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EWe%20have%20a%26nbsp%3BDynamicFragment%26nbsp%3Binside%20the%26nbsp%3BEditForm%26nbsp%3Belement.%20That%20is%20a%20RenderFragment%20that%20we'll%20be%20using%20to%20build%20the%20form.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EWe%20have%20two%20key%20functions%3A%26nbsp%3BRenderComponent%26nbsp%3Band%26nbsp%3BGetChildItems.%20First%20function%20updates%20the%26nbsp%3BRenderFragments%26nbsp%3Bwith%20html%20elements%20spewed%20by%20the%20second%20funtion.%3C%2FP%3E%0A%3CP%3ESecond%20function%20is%20the%20recursive%20function%20that%20goes%20through%20all%20the%20Questionnaire%20items%20and%20determines%20whether%20it%20should%20render%20an%20HTML%20element.%20If%20it%20finds%20the%20Item%20contains%20another%20Item%20with%20a%20count%20greater%20than%200%20we%20know%20the%20type%20is%20of%20'Group'.%20The%20recursion%20starts%20again.%3CBR%20%2F%3E%3CBR%20%2F%3EBlazor%20is%20ultimately%20going%20to%20express%20that%20Razor%20code%20as%20a%20%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Faspnet%2Fcore%2Fblazor%2Fadvanced-scenarios%3Fview%3Daspnetcore-6.0%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3ERenderTreeBuilder%3C%2FA%3E.%3CBR%20%2F%3E%3CBR%20%2F%3E%3C%2FP%3E%0A%3CP%3E%3CSTRONG%3EHow%20do%20you%20kick%20off%20the%20render%3F%3C%2FSTRONG%3E%3C%2FP%3E%0A%3CP%3EOn%20the%20code%20behind%3A%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CPRE%20class%3D%22lia-code-sample%20language-csharp%22%3E%3CCODE%3Eprotected%20override%20async%20Task%20OnInitializedAsync()%0A%7B%0A%20%20try%0A%20%20%7B%0A%20%20%20%20RenderComponent(QResponse.Item)%3B%0A%20%20%20%20.%0A%20%20%20%20.%0A%7D%0A%3C%2FCODE%3E%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%3CBR%20%2F%3EDuring%20initialization%20you%20can%20simply%20call%20the%20function%26nbsp%3BRenderComponent%26nbsp%3Bwith%20all%20the%20Questionnaire%20items!%3CBR%20%2F%3E%3CBR%20%2F%3EAnd%20here%20is%20how%20it%20renders.%3C%2FP%3E%0A%3CP%3E%3CSPAN%20class%3D%22lia-inline-image-display-wrapper%20lia-image-align-inline%22%20image-alt%3D%22DaveUpton_1-1637112732647.png%22%20style%3D%22width%3A%20400px%3B%22%3E%3CIMG%20src%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fimage%2Fserverpage%2Fimage-id%2F327465i21A37CC5018B2A67%2Fimage-size%2Fmedium%3Fv%3Dv2%26amp%3Bpx%3D400%22%20role%3D%22button%22%20title%3D%22DaveUpton_1-1637112732647.png%22%20alt%3D%22DaveUpton_1-1637112732647.png%22%20%2F%3E%3C%2FSPAN%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%3CEM%3EWe%20hope%20this%20helps%20you%20as%20you%20try%20to%20tackle%20the%20complexity%20of%20Questionnaire%20component%20of%20FHIR.%3C%2FEM%3E%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-TEASER%20id%3D%22lingo-teaser-2971861%22%20slang%3D%22en-US%22%3E%3CP%3E%3CSPAN%20class%3D%22lia-inline-image-display-wrapper%20lia-image-align-center%22%20image-alt%3D%22FHIRBlazor.png%22%20style%3D%22width%3A%20999px%3B%22%3E%3CIMG%20src%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fimage%2Fserverpage%2Fimage-id%2F327320iADF04006C70E49BB%2Fimage-size%2Flarge%3Fv%3Dv2%26amp%3Bpx%3D999%22%20role%3D%22button%22%20title%3D%22FHIRBlazor.png%22%20alt%3D%22FHIRBlazor.png%22%20%2F%3E%3C%2FSPAN%3E%3C%2FP%3E%0A%3CP%3ERendering%20a%20%3CA%20href%3D%22https%3A%2F%2Fwww.hl7.org%2Ffhir%2Fquestionnaire.html%22%20rel%3D%22nofollow%20noopener%20noreferrer%22%20target%3D%22_blank%22%3EHL7%20FHIR%20Questionnaire%3C%2FA%3E%26nbsp%3Bcomponent%20isn't%20as%20simple%20as%20creating%20a%20normal%20HTML%20form.%3C%2FP%3E%3C%2FLINGO-TEASER%3E%3CLINGO-LABS%20id%3D%22lingo-labs-2971861%22%20slang%3D%22en-US%22%3E%3CLINGO-LABEL%3EBlazor%3C%2FLINGO-LABEL%3E%3CLINGO-LABEL%3EFHIR%3C%2FLINGO-LABEL%3E%3CLINGO-LABEL%3EHealthcare%3C%2FLINGO-LABEL%3E%3CLINGO-LABEL%3EHLS_Hack%3C%2FLINGO-LABEL%3E%3C%2FLINGO-LABS%3E
Version history
Last update:
‎Nov 17 2021 02:06 PM
Updated by: