xmlet/HtmlFlow

View in inconsistent state when Dynamic block throws an exception!!!

3t-service opened this issue · 5 comments

Looking at https://htmlflow.org/features/ for partial template

static void template(DynamicHtml<Pet> view, Pet pet) { view .form().attrMethod(EnumMethodType.POST) .div().attrClass("form-group has-feedback") .dynamic(div -> view.addPartial(InputField.view, InputField.LV.of("Date", "date", LocalDate.now()))) .__() //div .__() //form``` )

DynamicHtml<T> view does not contain the method form().

The method form() is undefined for the type DynamicHtml

html div and tr exist. I'm using 3.0.2. Is there a work around?
I am trying to make a template for head() but this does not exist. The only way to access head is via: html().head()

You can use 'view.defineRoot()'
Also there are other mistakes in the documentation such as taskDetailsTemplate .dynamic needs changing to .of
or it will throw exception about nested dynamic

Thank you @3t-service for your feedback.

Indeed, for that example of partial we cannot directly invoke .form() on view. You are right, one possible workaround is to use defineRoot(). Even so, in this case I prefer to include the form inside a div. I just made that fix on documentation.

Regarding your second alert about wrong use of .dynamic on taskDetailsTemplate I cannot reproduce your observations as it seems to be working fine.

That sample was copied from the following unit test, which I can run without any problem.
https://github.com/xmlet/HtmlFlow/blob/master/src/test/java/htmlflow/test/views/HtmlForReadme.java#L104

I am sorry for any annoying about the documentation, I understand your trouble with that. But we are just a few developers maintaining this library and we are always giving priority for new features rather than improving docs.

I have found a way to replicate You are already in a dynamic block exception.

Here I have defined a dynamic block which adds p tags from a HashMap.
There is an intentional error in the first list with a null in item 'c'.
After this exception all subsequent calls of view.render fail. How to recover from this?

`HtmlView<HashMap<String, Object>> view1 = DynamicHtml.view(Main::taskDetailsTemplate);

static void taskDetailsTemplate(DynamicHtml<HashMap<String, Object>> view, HashMap<String, Object> task) {
    view
        .html()
            .head()
                .title().text("Task Details").__()
            .__() //head
            .body()
                .dynamic(body -> {
                	for (Map.Entry<String, Object> item : task.entrySet()) {
                        body.p().text(item.getValue()).__();
                    }
                })
            .__() //body
        .__(); // html
}


public static void main(String[] args) {
	HashMap<String, Object> list = new HashMap<String, Object>();
	list.put("a", "item 1");
	list.put("b", "item 2");
	list.put("c", null);

	HashMap<String, Object> list2 = new HashMap<String, Object>();
	list2.put("a", "item 1");
	list2.put("b", "item 2");
	list2.put("c", "item 3");

	try {
		view1.render(list2); // works fine 
	} catch (Exception e) { 
		e.printStackTrace(); 
	}

	try {
		view1.render(list);
	} catch (Exception e) {
		e.printStackTrace(); // null in first list
	}

	try {
		view1.render(list2);
	} catch (Exception e) { 
		e.printStackTrace(); // should not fail, second list is ok
	} 
	// Now all view1.render(object); calls result in You are already in a dynamic block! Do not use dynamic() chained inside another dynamic!

}`

Great catch @3t-service! There is a bug indeed in dynamic processing that is not dealing with the case where the dynamic block throws an exception leaving the view in inconsistent state.

Regarding your previous comment about:

Also there are other mistakes in the documentation such as taskDetailsTemplate .dynamic needs changing to .of or it will throw exception about nested dynamic

You are right too. The unit test for this documentation had render in comment and I was not running the template.
https://github.com/xmlet/HtmlFlow/blob/master/src/test/java/htmlflow/test/views/HtmlForReadme.java#L104

There is a mistake in the template and we have to replace nested dynamic with of. I will fix this in the documentation right now.

Regarding the bug in dynamic I will have to plan a fix, since it requires changes in HtmlApiFaster dependency.

    HtmlView<HashMap<String, Object>> view1 = DynamicHtml.view(Main::taskDetailsTemplate);

    static void taskDetailsTemplate(DynamicHtml<HashMap<String, Object>> view, HashMap<String, Object> task) {
        try {
          .html()
              .head()
                  .title().text("Task Details").__()
              .__() //head
              .body()
                  .dynamic(body -> {
            	      for (Map.Entry<String, Object> item : task.entrySet()) {
                          body.p().text(item.getValue()).__();
                      }
                  })
              .__() //body
          .__(); // html
        } catch (Exception e) {
            Main.view1 = DynamicHtml.view(Main::taskDetailsTemplate);
            throw e;
        }
  }

I am currently using a workaround where I wrap the view calls in a try statement and recreate the view in case of an exception.
It works but I suspect it is not performant.
Thanks for looking into this and take care.