Skip to content
This repository has been archived by the owner on Sep 24, 2024. It is now read-only.
nielsbaloe edited this page Aug 14, 2018 · 1 revision

Fluent HTML inside VertxUI is a descriptive replacement for Javascript + HTML. You will need a .css file for your layout too. Any pure-css framework, like bootstrap or purecss (or Jquery Mobile), is easy to handle with Fluent HTML, and you can also use it to write out more complex interaction or graphical stuff for existing web pages.

You don't need Vert.X as framework for Fluent HTML -any server environment that can serve files from /build/development or /build/production is fine- but Vert.X static file serving and file reloading (with Figwheely) it is supported out of the box with VertxUI.

You also do not need a generic javascript framework like jquery, because the javascript that is generated with VertxUI runs everywhere without any browserspecific fixes. It's not 2000 anymore, or so to say: "This is not your grandfathers javascript".

Start

You can start with Fluent.body or Fluent.header to get access to the body or header of your page. Use Fluent.document Fluent.window and Fluent.console for direct browser access.

Fluent div = Fluent.body.div(null,"Hello world");

Fluent.console.log("You just pressed F12 to see this message in your console.");

Fluent.window.alert("Hi there");

Children

Fluent is a children based language. It loves children. This is compatible with the descriptive nature: you're just describing your view by creating new children all the time. Don't worry about removing or adjusting graphical things, because that is what ViewOn and ViewOnBoth can do for you automaticly. So don't start changing the DOM yourself, you probably want to make a state out of it or a model for it.

You can add children by directly calling methods. The first parameter is practically always a css-class, the second the inner text:

 <div><ul><li class="item">Olah</li></ul></div>

 body.div().ul().li("item","Olah");

If you need more than one child, there are several ways to do so. You can create items with a capital which are not bind yet. With unbinded children you can now created children in a lot of ways, including giving a list of children to the constructor of ul(), create a seperate ul-object and add each .li individually, or add a stream.

<div>
	<ul>
		<li class="item">Olah 1</li>
		<li class="item">Olah 2</li>
		<li class="item">Olah 3</li>
	</ul>
</div>

body.div().ul(  Li("item","Olah 1"),  Li("item","Olah 2"),  Li("item","Olah 3")  );

Fluent ul=body.div().ul();
ul.li("item","Olah 1");
ul.li("item","Olah 2");
ul.li("item","Olah 3");

body.div().ul(  Stream.of("Olah 1","Olah 2","Olah 3").forEach( s->Li("Item",s) ) );

Control over your features

A normal fluent has access to these methods to set features:

  • .css(Css.name,"value") to set css
  • .att(Att.name,"value") to set attributes (and a few properties)
  • .txt("innerText") to set inner text
  • .listen(eventType,event->{...}) to add listeners

That should be enough. For your convenience, these are added too:

  • .tag() .css(Css.name) .att(Att.name) .txt() and .listen(eventType) to get values.
  • .click() .keyup() .keydown() .focus() .blur() .mouseup() .mousedown() as convenience listener methods.
  • .dom() to get the underlying dom node, and Fluent.getByElementId("id") when you start with an existing html page.
  • .id("set") and .id() for id's - although you will not be needing these at all, because you'll save the fluents.
  • .textNode() for creating a textnode - do not use this, try to avoid children and text together in a node.
  • .hide(boolean) for quick showing and hiding.

An example: these expressions below are all equal. You can see it might be confusing to do too much at one line. It might be best if there are multiple children and attributes to set, to create a variable first (the last method below).

// Should be fine, when you understand that you have created a new child with .span(... .
Li().class("YourClass").css(Css.display,"block").span("explain").txt("bladiebla");

// Not ideal
Li("YourClass", Span("explain","bladiebla")).css(Css.display,"block");

// Ideal
Fluent li = Li().class("YourClass").css(Css.display,"block");
li.span("explain","bladiebla");

ViewOn and ViewOnBoth

When things change, you probably want to use ViewOn (or ViewOnBoth for two things) instead of changing the DOM yourself (which can be very complex in real life situations and therefore less reliable and less advisable). There are proper examples in the examples package, but here is a teaser:

ViewOn<Model> view = body.add(models, m -> {
    if (m==null) {
        return Div().h1("titlerCssClass","Please move along, nothing to see here");
    }
    Fluent result=Div();
    result.span("List of things that are done");
    result.ul(m.stream().filter(i->i.isDone()).forEach(i->Li("item",i.getName()));
    return result;

}

You can now change your models list, and call view.sync() if your model has changed. This way of scripting can be seen in the TodoMvc and MvcBootstrap files: the View class has a few ViewOn... classmembers, and only a few methods called ...sync() which are called by the controller when the model has changed.

Creating view components

By writing ViewOn objects, you are already writing view components! In bigger projects, you can easily write lots of classes that extend from ViewOn. I can imagine several extends-ViewOn classes in a big project.

However, you can also do the same at HTML+CSS level, which is more or less the 'traditional' way of thinking about view components. The MVCBootstrap example has one minor example of that:

public class ComponentDatePicker extends Fluent {

public ComponentDatePicker() {
	super("INPUT", null);
	classs("form-control").att(Att.type, "text").id("datepicker");

	// The non-component version was:
	// Input("form-control").att(Att.type, "text").id("datepicker");
    }
}

Confusing stuff

A browser can get pretty confusing when attributes and properties are explained - there is a whole range of Internet Explorers that dealt wrongly with that. Attributes are things you add inside a DOM tag (like <tagname attrName=attrValue>), and properties are things that you control with a dot-notation (like inputField.value = ... or result=inputField.value ). To make things confusing, some properties are also available as attribute (like 'checked'), then the attribute represent the default value - or property default* is used for that (like .defaultValue). Are you still there?

To prevent confusion, inside Fluent a few properties can be set and removed with attr() too. There are seperate .dom*() methods to get the real life value for input fields. If you use these .dom*() methods, the virtual DOM will get the change too, so that the visual recycling of Fluent works well. Otherwise you can see some old text or checkboxes in your form fields.

Input input = Input("cssClass","checkbox").att(Att.checked,"1");
input.clicked( (fluent,event)-> {
  boolean received = fluent.domChecked();
  // Without Virtual DOM updating: boolean received = ((InputEvent)event.getTarget()).getChecked();
  ...
}
Clone this wiki locally