HFE: Validating forms

Hey @mseaton,

(Continuing here our discussions about validating HFE forms, as here on Slack for one of them.)

I am not sure where the form validation should happen exactly, maybe optionally just before here in HtmlFormEntryServiceImpl#saveHtmlForm.

Anyway my idea was to do something like that:

void validateForm(HtmlFormEntryService service, HtmlForm form) throws Exception {
    Document doc = HtmlFormEntryUtil.stringToDocument(form.getXmlData());
    
    Map<Node, TagValidator> validators = new HashMap<>();
    addTagValidators(service, HtmlFormEntryUtil.findChild(doc, "htmlform"), validators);
    
    validators.keySet().stream().forEach(node -> {
        validators.get(node).validate(node);
    });
}

Where basically I crawl through the nodes to get their TagValidator, and then validate each node.

As for the recursive addTagValidators it would look like this:

void addTagValidators(HtmlFormEntryService service, Node node, final Map<Node, TagValidator> validators) {
    
    TagHandler handler = service.getHandlerByTagName(node.getNodeName());
    
    if (handler != null) {
        if (handler instanceof TagValidator) {
            validators.put(node, (TagValidator) handler);
        }
        else {
            // unfortunately a handler leading to no validator
        }
    }
    
    NodeList list = node.getChildNodes();
    for (int i = 0; i < list.getLength(); ++i) {
        addTagValidators(service, list.item(i), validators);
    }
}

Does that make sense to you?

FYI this is syntactically correct code but I haven’t even tried it, I just pulled bits here and there taking a lot of inspiration from applyTagsHelper. I just wanted to kick start the topic and leave a trace for picking this up again next year :wink:

3 Likes

Hi @AMPATH team / @jdick, bouncing back on the same topic regarding AMPATH forms. Is there any mechanism - be it front or backend - for the forms to expose which metadata they require to function?

1 Like

@mksd am probably giving the wrong answer :grinning: . But we made an attempt to ensure the renderer just worked without any special configurations in openmrs. If you take a look at ngx-openmrs-formentry/adult-1.4.json at master · AMPATH/ngx-openmrs-formentry · GitHub the schema carries pretty much everything the renderer need to render the form and generate an encounter payload. This means it will render anything as long as it is a valid schema. Whether you are able to save the form correctly though depends on whether the metadata you used in the form actually exists in openmrs. The renderer require the implementer to inject a data service which provides data that may be needed to do validations and skip logic.

1 Like

Thanks @achachiez. I was expecting something along those lines and I think it makes sense, from a UX standpoint, to facilitate rendering (even if some referenced metadata is invalid/missing… etc.)

Could you point me to the place in the AMPATH forms project where the form is saved, along with its version… etc? So when the Form object is persisted.

Starting here we provider services for fetching remote data ngx-openmrs-formentry/app.component.ts at 737603144c49eb8194d7326b19bc23433c076d6d · AMPATH/ngx-openmrs-formentry · GitHub In this case we are mocking the services but in the main project they actually fetch data from openmrs. Starting here we ngx-openmrs-formentry/app.component.ts at 737603144c49eb8194d7326b19bc23433c076d6d · AMPATH/ngx-openmrs-formentry · GitHub We create the form

Here we populate orders ngx-openmrs-formentry/app.component.ts at 737603144c49eb8194d7326b19bc23433c076d6d · AMPATH/ngx-openmrs-formentry · GitHub

Here we populate an encounter as returned from the encounter resource.ngx-openmrs-formentry/app.component.ts at 737603144c49eb8194d7326b19bc23433c076d6d · AMPATH/ngx-openmrs-formentry · GitHub

This ngx-openmrs-formentry/app.component.ts at 737603144c49eb8194d7326b19bc23433c076d6d · AMPATH/ngx-openmrs-formentry · GitHub is called on submit and generates a payload, at this point you just call the service to save the encounter. Here is the component of Ampath POC that consumes it ng2-amrs/formentry.component.ts at master · AMPATH/ng2-amrs · GitHub