Composing an HTML view with conditional logic

674 views Asked by At

In my multitenant app, I have many instances of the following:

<!-- ko if: tenantA() -->
<div>Tenant One snippet</div>
<!-- /ko -->
<!-- ko if: tenantB() -->
<div>Tenant Two snippet</div>
<!-- /ko -->
<!-- ko if: userTypeA() -->
<div>User type A snippet</div>
<!-- /ko -->
<!-- ko if: userTypeB() -->
<div>User type B snippet</div>
<!-- /ko -->

This is an oversimplified example but you get the picture - many components of a larger view are composed based on business logic from various data points.

The benefit of this is that I have one view and the solution footprint is more manageable, but it is a bit messy. What alternative patterns are there?

I was considering splitting anything and everything that is displayed on condition into an html template and rendering html based on logic in the js viewmodel, but this introduces more complexity into my viewmodels and muddles up my solution footprint (this is important to me for quick navigation during dev).

I am working with durandal and knockout but tagged Aurelia because I will be migrating sometime and think this is more of a pattern question than a particular technology question.

2

There are 2 answers

1
prazor9 On

HTML templates always tend to become messy and difficult to maintain once your app starts to grow big.

I would suggest you to break your HTML into multiple components there by It will be easier to maintain and most important you will end up reusing all these components.

one of the greatest example would be JSX which works best with React.js where in you can write HTML inside JS. so you can divide your app into maintainable components. give JSX a try it really makes your life lot easier as font-end developer.

5
LStarky On

Logical Pattern, Software Design Concepts

You definitely want to be thinking in terms of components, regardless of what language or framework you choose to use for implementation.

  1. Divide up your application into different views (perhaps one for each of the navigation menu elements).
  2. Subdivide each of these views further (as needed) based on what types of functions they serve. I like the way that Bootstrap's panels help you visualize this.
  3. Develop each of these components and give them names, like ContactList, ContactEdit, ContactView, etc. Develop HTML views and JavaScript, TypeScript or other language viewmodels for each of these components. Be sure to use MVC logic to separate your views from the data, using placeholders in the view to indicate where you will fill in the data. The views are like templates.
  4. Use your specific language or framework to link it all together.

Here is a great tutorial on Component-Based Software Architecture and Design.

Aurelia Component Logic

I'm currently using Aurelia, and since you mentioned it, we'll use the Aurelia framework as an example to illustrate the above principles. Best practice would be to create the components (for modularity and reuse) and then use the if.bind property to include them or not in the DOM.

Example:

<tenant-one if.bind="tenantA"></tenant-one>
<tenant-two if.bind="tenantB"></tenant-two>

Or to only show/hide each component from the view (but include all in the DOM), you would use show.bind instead of if.bind, but that seems to make less sense for your use case.

Aurelia Data Binding

Since I don't know what you actually hope to display here, the above code is based on your code snippet in the question. However, for similar views where only the data will change (like a template), you would bind the data from the parent to the child so that it can be displayed correctly. In Aurelia, this would look like this.

Example:

<tenant-view data.bind="tenantData"></tenant-view>

More Complex Example:

<tenant-view fname.bind="firstName" lname.bind="lastName" data.bind="tenantData"></tenant-view>

In each of these examples, you would need to develop 4 files. You would need the main (parent) container that has a view and a viewmodel. The viewmodel would be in charge of retrieving or accessing the tenant information and then passing it on to each of the child components. The child component would have its own view and viewmodel.

For example, the TenantView view (highly simplified) might be like this:

<template>
  <p><strong>Tenant Name: </strong>${fname} ${lname}</p>
  <p><strong>Additional Data: </strong>${data}</p>
  <p if.bind="data.rentDue">Tenant's rent is due!</p>
</template>

And then the TenantView viewmodel (again, highly simplified) might be like this:

import { bindable, bindingMode } from 'aurelia-framework';

export class TenantView {
  @bindable fname;
  @bindable lname;
  @bindable data;
}

Once you have this component created, you would then insert it (optionally) in the parent view, like this:

<template>
  <h1>Contact View</h1>
  <h2>${firstName} ${lastName}</h2>

  <tenant-view if.bind="cat == 'tenant'" fname.bind="firstName" lname.bind="lastName" data.bind="contactData"></tenant-view>

  <phone-numbers data.bind="contactData"></phone-numbers> <!-- another component -->

  <page-footer></page-footer> <!-- another component -->

</template>