📜 ⬆️ ⬇️

W3View - straight web UI path

My previous posting on Habré reached its goals, - a lot of people learned about the existence of W3View, some visited GitHub , someone probably even liked it.


At the same time, the following comments revealed the need for a clearer description of what the task this library solves, how it does it, and why you may need it (all of a sudden).


If, for some reason, you are not interested in finding out how you can build an advanced UI on Web technologies without experiencing pain, nausea, or dizziness, this article will seem boring to you, I invite others under cat.


Patterns, traditions and "patterns"


According to the tradition established by one small and innerHTML, the UI on the Web is built with the help of various template engines; over the past few years, template engines have come a long way and have developed to impossibility. They were supposed to simplify the creation of interfaces, but the tasks did not make this easier. To solve complex problems, the developers of the template engines introduced a lot of new concepts and tied up the concept of a variety of concepts. Some concepts even dictate the rules by which the Model should be built, which in my opinion completely contradicts common sense. We do not write applications for concepts. In this case, the template engine does not always relieve us of the need to use the DOM API, whatever one may say, but it is impossible to foresee everything. However, a direct impact on the DOM can easily break any, even the most beautiful concept ...
The developers of template engines, in order to prevent the destruction of concepts, have declared such an impact anathema and moveton - that is, in our opinion, anti-pattern .


So what is left for the developer to do if what he develops does not fit into the templates? - one thing remains - to look for an alternative .


Alternative to patterns


Once, a long time ago, HTML, Javascript and the DOM API completely allowed to do without the use of templates and black magic. HTML described the structure of the form, Javascript, using the DOM API, managed this form and processed its events, it was beautiful, simple and convenient. But interfaces are becoming more complex and dynamic, and HTML continues to be a data description language. The way out can be decomposition - splitting a large and complex shape into small, independent, reusable capsules - components.


The component declaration may look like a small HTML page with a script that describes the reaction to data change and defines event handlers. Then from such components can be collected more complex.



W3View is a factory for creating simple and complex components.


How it works and how to use it, I will try to explain with a small application as an example. Long thought about which example is better to consider, stopped at the W3View • TodoMVC . Done, works, made for the demonstration, there is something to compare, not too big.


The specification, layout and other incoming can be found here . Probably worth a look at the specification and layout before reading on.


How the W3View • TodoMVC application works


I will omit the descriptions of the Model and Controller, they are unremarkable. Let me just say that I used the classic MVC triad, where the active Model implements its internal logic and makes no assumptions about how it will be displayed, the View is completely based on W3View , and the Controller links everything together to the final application. The application implements the layout of TodoMVC without changes and does not add any of its styles.


For simplicity, the component description is placed in a hidden DIV, right on the page.


Connect, initialize and run the application


The application connects to the page with four SCRIPT tags:


<script src="node_modules/w3view/w3view.js"></script> <script src="js/models/todo.js"></script> <script src="js/controllers/todo.js"></script> <script src="js/app.js"></script> 

The first is the W3View library, the second and third are the Model and Controller, the fourth is the startup script, the main thing in it (from what is relevant to the work of W3View) is:


 var appContext = new todoController(todoModel('todos-W3View')); var w3view = new W3View(appContext); var sources = document.getElementById('components'); w3view.register(sources.children); document.body.removeChild(sources); w3view.create('application').mount(document.body, 0); //   ,     


Usually I manage with fewer lines, but here I decided for beauty to clean the DOM tree, what to do is perfectionism. When using a pre-prepared bundle, these six lines turn into two:


 var appContext = new todoController(todoModel('todos-W3View')); w3view(appContext).create('application').mount(document.body, 0); 

Understandably, a bundle is much better for production, but when developing and for clarity, I prefer to connect the sources.


Connected, initialized, launched, and what? Yes ..., it was necessary to create components!


Create components


As I said before, the component declaration is an HTML element, so it’s enough just to cut the existing layout into pieces, describe the response to changing data and event handlers, and then put it all back together.


In TodoMVC, it was easy to isolate the dynamic parts, and the root component of the APPLICATION was:


 <section as="application" class="todoapp"> <header class="header"> <h1>todos</h1> <input ref="newTodo" class="new-todo" placeholder="What needs to be done?" autofocus> </header> <main ref="main"></main> <totals ref="totals"></totals> <script type="javascript"> //   ,   //      </script> </section> 

As you can see - just HTML, with a pair of non-standard attributes, two non-standard tags and the wrong type of tag SCRIPT.



 <section as="application" ...>...   SECTION      APPLICATION, <input ref="newTodo" ...>    INPUT,       "this.ref.newTodo" 

The component instance itself will be accessible from its own script like this , by the way, the component instance is just a DOM node.


 function (appContext, factory){ //    } 


SCRIPT tag content and component instance life cycle


So SCRIPT. We have to tell the Controller about our Presentation, do it when placing the application on the page, for this we define the onMount handler in the script:


 this.onMount = function (){ appContext.setView(this); console.log("application section created"); } 

This handler is called immediately after the component is inserted (mounted) into the DOM tree. You can define an onUnmount which is paired to it , which is called immediately before unmounting the component. In order for them to work it is necessary to make installation and disassembly using special component methods - mount (targetElement, index) and unmount () . If a component is removed from the DOM, it can be re-mounted if necessary. There are more onCreate and onDestroy , respectively, are called when creating and destroying the component (you need to destroy using the destroy method). When you call destroy on a component instance, it is first unmounted (unmount ()), and then its entire subtree is also recursively destroyed into trash. After calling the destroy item, the element can no longer be used.


The onCreate and onMount handlers should be defined if you need to place references to the component in other objects. The code that removes these links should be placed in the onDestroy and onUnmount handlers ; this should be done symmetrically.


 //     TodoMVC //     , function someFunc(e){...} //     -, //        //      this.onMount(){ window.addEventListener('mousemove', someFunc); } //       ? //            this.onUnmount(){ window.removeEventListener('mousemove', someFunc); } 

You just need to wash your hands, and then the memory will not flow away.


SCRIPT tag content, data refresh


So, with the life cycle figured out, let's understand now with the change of data:


 <section as="application" class="todoapp"> <header class="header"> <h1>todos</h1> <input ref="newTodo" class="new-todo" placeholder="What needs to be done?" autofocus> </header> <main ref="main"></main> <totals ref="totals"></totals> <script type="javascript"> this.onSetData = function (input){ this.ref.main.setData(input); this.ref.totals.setData(input.total); }; this.onMount = function (){...}; </script> </section> 

Everything is simple here - when we receive new data we distribute them to the elements in our subtree. Outside (here from the Controller) the data is set by the setData method, it is already present in all created instances of W3View components and simply calls the onSetData handler that needs to be determined. The setData method and the onSetData handler can take up to three arguments:



And in other matters, you can apply all three arguments at your own discretion, pass there whatever you see fit, it depends only on the task to be solved and the implementation of the onSetData handler .


this.ref.main and this.ref.totals are links to elements in the subtree of our component, marked with the attribute ref - ref = "main" and ref = "totals", respectively, I think it already mentioned.


SCRIPT tag content - how to handle events


There is one more element in the APPLICATION component, it is labeled ref = "newTodo" , this is an input field. According to the specification, something can be entered into it, but when you press the ENTER key, something should be included in the list of messages . Of course, for this to happen, you need to handle pressing ENTER.


Events in W3View are handled in a very standard way - by putting the standard event handlers on ordinary elements. In this case, onkeydown on the element on which we set the ref = "newTodo" attribute.


 <section as="application" class="todoapp"> <header class="header"> <h1>todos</h1> <input ref="newTodo" class="new-todo" placeholder="What needs to be done?" autofocus> </header> <main ref="main"></main> <totals ref="totals"></totals> <script type="javascript"> this.ref.newTodo.onkeydown = function (e){ var ENTER_KEY = 13; if(e.which !== ENTER_KEY) return; appContext.addNewTodo(this.value); this.value=''; }; this.onSetData = function (input){...}; this.onMount = function (){...}; </script> </section> 

Everything is quite ordinary: they took an element, hung up an event, which, if the ENTER key is pressed, calls a method on the Controller (The controller here is called appContext - remember, we passed it to new W3View - this is it).


Done, the root component of the application has ended, move on to the next. The next is MAIN, because the TOTALS component is trivial - I will not write anything about it. Just the same setting of attributes and properties for DOM elements, nothing new. But the MAIN component is worth more detail.


Built-in component ARRAY-ITERATOR


The most interesting thing about MAIN is the use of the built-in ARRAY-ITERATOR:


 ... <array-iterator ref="list" usetag="ul" class="todo-list"> <listItem></listItem> </array-iterator> ... 

ARRAY-ITERATOR is already present in every W3View instance, this component is used to output arrays. It is configured by the component that will display the elements. In setData, ARRAY-ITERATOR takes two arguments:



A component that displays an element of an array will receive three arguments:



If you specify several components to display the elements of an array, their instances will alternate, for example:


 ... <array-iterator ref="list" usetag="ul" class="todo-list"> <listItem class="even"></listItem> <listItem class="odd"></listItem> </array-iterator> ... 

In this case, all the odd elements will have the class "even", and all the even elements will have the class "odd".


ARRAY-ITERATOR is based on DIV, and here we need UL, so we modify it using the attribute usetag = "ul" . Any km3 component W3View can receive such an attribute for tag substitution, on the basis of which it was announced.


If you need to use the tags TBODY, TR or TD outside the TABLE context when declaring components, you can use the tagname attribute for any element other than the W3View components.


Well, now you all know how to create components, how to register them in a factory and how to use them. It remains only to talk about how to keep your components in your hands.


Component Libraries - Modules


TodoMVC is a very small and simple application - only four components were needed to describe its interface, so it was possible to describe all this right on the page. In real life, the components may need a lot. Only one of the buttons, inputs and other sliders dozens of happen, what can we say about small screens, sockets, tins, popup pupchchiki and other things, for which they love UI so much.


Of course, it’s not convenient to describe all this on the page, you need to select them into separate files and it is desirable that these are not just files, but modules with component libraries. W3View allows you to load modules and resolve dependencies between them on the fly. To do this, you need to use moduleLoader , you need to use it like this:


 <script src="../w3view.js"></script> <script src="moduleLoader.js"></script> <script src="httpreader.js"></script> <script> var appContext = {}; moduleLoader(appContext, '../examples/modules/window.w3v.html', reader, function(factory){ factory.create('app').mount(document.body); }); </script> 

This is an example from the w3view package loader folder.


moduleLoader takes four arguments:



Dependencies are connected using the IMPORT tag, if you need to connect another library to one library, then in this one you need to write:


 <import src="./realative/path/to/module" as="namespace" type="html"></import> <div as="my-component"> ...... 

The name specified in the as attribute can be further used as a namespace, like this:


 <import src="./realative/path/to/module" as="namespace" type="html"></import> <div as="my-component"> <namespace:another-component> </namespace:another-component> </div> 

Thus, all components from the connected library become available. No matter how many times we import one module, it will be loaded only once, and recursive dependencies are also allowed.


To reduce download time and parse HTML component descriptions, you can use builder. It is located in the w3view package builder folder, where it is written how to use it in the build.js file. The result of the assembly is one js file, including all imported libraries.


Finally finished, thanks for reading


That's all you need to know about W3View to start using. The library itself is much smaller than this description, it will not be difficult to understand its device just by looking at the source code, I tried to make it as simple and transparent as possible. The library does not impose any rules, so you can program as you see fit to solve your problems.


Good luck.


')

Source: https://habr.com/ru/post/349866/


All Articles