[Challenge Tiny Framework] Super front-end and back-end multi-terminal adaptive template, rapid and agile development!

Posted by mikawhat on Mon, 15 Jul 2019 23:49:43 +0200

I can't see it any more. Today I'm going to kill the Tiny Framework, which is called the front-end framework of componentization.

Write 3 days of code, want to tell Tiny what is modularization, if you say success, I will open source the core code of this framework! Another contribution to Tiny's use of page template is the latest v1.5, pure static! The latest share, the latest download, the latest experience! I hope you can support me a lot!

Census this template engine:

1. Universality
Ability to generate various texts: HTML,XML,RTF Source code and so on, enough Lightweight.
Plug-in template loader: Templates can be loaded from any source, such as local files, databases, etc.
Text can be generated as needed: saved to a local file; as Email Send it; send it back from a Web application to a Web browser.

2. Template Language
All commonly used instructions: include,if/elseif/else,Cyclic structure.
In the template Establish And change variables.
Complex expressions can be used to specify values almost anywhere.
Named macros can have location parameters and nested content.
Namespaces help build and maintain reusable macrolibraries, or divide a large project into modules without worrying about name conflicts.
Output Conversion Block: Convert HTML escape, compression, and grammar when nested template fragments generate output Highlight Wait a minute; you can define your own transformation.

3. General Data Model
Through plug-in object encapsulation, it can be displayed in the template in the form of variables.
Objects can be represented in an abstract (interface) way and told template developers to use them. Method, so that it is not disturbed by technical details.

4. Preparing for the Web
It can be integrated into Model2 Web application framework as an alternative to View.
Design for MVC patterns: Separate visual design from application logic; Separate page designers from programmers.

5. Intelligent internationalization and localization
Character Set Intelligence (Internal Use) UNICODE).
Digital format localization is sensitive.
Date and time formats are localized sensitive.
Non-US character set It can be used as an identifier (such as variable names).
Same template for many different languages.

6. Wait a minute.

First of all, this is Tiny's componentized macro template (PS: Don't want to see this code pulled directly, see the real componentization!).



#@btTable("tableid")
    #include("/page/tables/search.page")
    #@btToolBar()
    <a href="#link("/page/form/example.pagelet")" data-toggle="modal" data-target="#tModal"  class="btn btn-default" role="button">#faIcon("plus")</a>
    <button type="button" class="btn btn-default mul-remove">#faIcon("trash")</button>

    <div class="btn-group">
        #@dropDownTitle()<span>purpose</span>#end
        #@dropDownBody()
            #@dropDownItem()<a data-key="" data-search-type="slbUse">purpose</a>#end
            #for(var:[1..4])
                #@dropDownItem()<a data-key="${var}" data-search-type="slbUse">purpose ${var}</a>#end
            #end
        #end
    </div>
    <div class="btn-group">
        #@dropDownTitle()<span>type</span>#end
        #@dropDownBody()
            #@dropDownItem()<a data-key="" data-search-type="slbTypeDict">type</a>#end
            #for(var:[1..4])
                #@dropDownItem()<a data-key="${var}" data-search-type="slbTypeDict">type ${var}</a>#end
            #end
        #end
    </div>

    #end
    #@btTableContent("/data/bt_table_data.pagelet",{"data-page-list":"[15, 20, 50, 100, 200,ALL]","data-page-size":"15","data-id-field":"id","data-search-on-enter-key":"true","has-opera":false})
        #*<th data-field="state" data-checkbox="true"></th>*#
        <th data-field="state" data-radio="true"></th>
        #*<th data-field="id" data-sortable="true">ID</th>*#
        <th data-field="name" data-sortable="true" data-formatter="${tableId}Link">Item Name</th>
        <th data-field="price" data-class="text-nowrap" data-sortable="true">Item Price</th>

        <th data-field="time" data-sortable="true" data-formatter="timeFormatter">time</th>
    #end
    #@btTableOperate()
    <a title="edit" href="#link("/page/form/example.page?id={id}")" pjax-target="#tinypagecontent">#faIcon("edit")</a>
    <a data-opera-type="remove" href="#link("/data/bt_table_data?id={id}")">#faIcon("remove")</a>
    #end
<script>
    function ${tableId}Link(value,row){
        return '<a href="http://www.baidu.com/?id='+row.id+'" target="_blank">'+value+'</a>';
    }
    $(function () {
        var _table = $("#${tableId}");
        var _bttable = $("#${tableId}-table");
        var otherInfoData = {};
        _table.off("click.remove").on("click.remove", "[data-opera-type=remove]", function () {
            var url=$(this).attr("href");
            layer.confirm("Are you sure you want to delete it?", {title: "Delete information"}, function () {
                $.ajax({
                    url: url,
                    type: "delete",
                    dataType: "json",
                    success: function (data) {
                        var info = "System error";
                        if (typeof(data.info) != 'undefined') {
                            info = data.info;
                        }
                        if (data && data.status && data.status == "y") {
                            layer.msg(info);
                        } else {
                            layer.msg(info, {icon: 2})
                        }
                        $("#${tableId}-table").bootstrapTable('refresh');
                    },
                    error: function (e) {
                        layer.msg("error", {icon: 2})
                    }
                })
            });
            return false;
        }).off("click.mulremove").on("click.mulremove", ".mul-remove", function () {
            var sels = $("#${tableId}-table").bootstrapTable('getSelections');
            if(sels.length==0){
                layer.msg("Please select the record first.",{icon:2})
                return false;
            }
            layer.confirm("Are you sure you want to delete it?", {title: "Delete information"}, function () {
                var ids = [];
                for (var i = 0, l = sels.length; i < l; i++) {
                    ids.push(sels[i].id);
                }
                $.ajax({
                    url: "#link("/data/bt_table_data.pagelet")",
                    type: "post",
                    data: {idItem: ids, ids: ids.join(",")},
                    dataType: "json",
                    success: function (data) {
                        var info = "System error";
                        if (typeof(data.info) != 'undefined') {
                            info = data.info;
                        }
                        if (data && data.status && data.status == "y") {
                            layer.msg(info);
                        } else {
                            layer.msg(info, {icon: 2})
                        }
                        $("#${tableId}-table").bootstrapTable('refresh');
                    },
                    error: function (e) {
                        layer.msg("error", {icon: 2})
                    }
                })
                return false;
            })
        }).off("click.searchtype").on("click.searchtype", "[data-search-type]", function () {
            $(this).closest(".btn-group").find("span:first").html($(this).html());
            var thisType = $(this).attr("data-search-type");
            otherInfoData[thisType] = $(this).data("key");
            _bttable.bootstrapTable('refreshOptions', $.extend(_bttable.bootstrapTable('getOptions'), {otherParam: otherInfoData}, {pageNo: 1}));
            _bttable.bootstrapTable('refresh');
        })
    })
</script>
#end

After the advantages, there is time to sort them out. First, talk about the shortcomings:

1. The package is not flexible enough.

2. Expansion is inconvenient or has fewer extension points.

3. The template carelessly writes out the problem, mainly the #end.

4. Look at the macro only, 116 lines of code, the generated Html code is as high as 7 times, 748 lines.

5. The so-called componentization only encapsulates several macros for easy use, and then comes a macro nesting.

 

I've been trying to write a hybrid front-end + back-end solution for a long time, referring to many frameworks, and finally some good features form the core of this framework, which is tentatively named ejEngine(ej = easy java).

The first step, Component Initial Demo

Code:

<#- Small Components - >
<@component.widget name="Slaughter TinyFramework Front end">
		
</@component.widget>

 

Operation effect:

 

The second step, high energy!!! Increase btTable component, macro nesting mechanism, plug-in pile mechanism, component configuration inheritance variable sharing mechanism, parameter json dynamic configuration mechanism

Code:

<#- Small Components - >
<@component.widget name="Slaughter TinyFramework Front end">
     <#- btTable Component - >
     <@component.btTable config="{'url':'list','orderColumn':['name']}";gridConfig,plug>
     		<#References to components or custom components in the plug-in pile can refer to parent configuration, and the location of the plug-in does not affect - >
	   		<#if plug == "btTableTemplate">
	   			
	   			<#- template component - >
	      			<@component.btTableTemplate config="{'mode':'auto','checkbox':'${gridConfig.checkbox}'}";config>
				<tr>
				  <#list columns as column>
				  <td>{{ d.rows[i].${column.columnNameFirstLower} }}</td>
				  </#list>
				</tr>
				</@component.btTableTemplate>
	   		</#if>
     </@component.btTable>
</@component.widget>

 

Operation effect:

 

Explain:

The macro nesting mechanism calls btTable in the widget and btTable calls btTable Template.

Plug-in Pile Mechanism: btTable has a plug callback, which is configured in the template. The plug-in Pile refers to components or custom components, which can refer to the parent configuration, and the location of the plug-in does not affect.

Component superior and subordinate configuration inherits variable sharing mechanism: parent can share variables openly to subordinate calls, such as: in the config parameter of btTable Template, the checkbox configuration of parent btTable is referenced.

Parameter json dynamic configuration mechanism: the parent configuration defines the config={} string, which can be invoked by parameters in the component template language, such as: config.mode of btTableTemplate provides auto mode and customs mode. If configured as {mode':'auto'}, the content of the btTableTemplate tag body is not output, and in config template language, the content of the tag body is not output. All parameters defined can be invoked openly through variable sharing mechanism.

 

Step 3: Get through components and Javascript

Code:

<#- Small Components - >
<@component.widget name="Slaughter TinyFramework Front end">
     <#- btTable Component - >
		<@component.btTable config="{'url':'','orderColumn':['name']}" attr="style:'color:green'" event="click:function(){
	    			console.log('click grid');
	   	}";gridConfig,plug>     		<#References to components or custom components in the plug-in pile can refer to parent configuration, and the location of the plug-in does not affect - >
	   		<#if plug == "btTableTemplate">
	   			
	   			<#- template component - >
	      		<@component.btTableTemplate config="{'mode':'custom','checkbox':'${gridConfig.checkbox}'}";config>
				<tr>
				<td class="bs-checkbox"><input name="btCheckAll" type="checkbox" value="{{ d.rows[i].id }}"></td>
				<td>{{ d.rows[i].id }}</td>
				<td>{{ d.rows[i].name }}</td>
				<td>Remarks</td>
				<td>{{ d.rows[i].createTime }}</td>
				<td>{{ d.rows[i].createTime }}</td>
				<td>No time</td>
				</tr>
				</@component.btTableTemplate>
	   		</#if>
     </@component.btTable>
</@component.widget>

 

Operation effect:

Explain:

The attr parameter is configured in btTable: attr="style:'color:green'", and the click event is configured as "click: function ()".{
                    console.log('click grid');
} The function of calling js by components has been preliminarily realized.

 

Fourth step, four steps to become immortal, component Javascript, complete effect

Code:

<#Hook
<@component.hook />

<#- Small Components - >
<@component.widget name="Slaughter TinyFramework Front end">
	
	<#Query Form Component
		<@component.hideQueryForm url="list" native='
			find("search").bind({
					click:function(){
						
						//Call component
						var params = ${id}.queryForm.serialize();
			          	params = decodeURIComponent(params, true);
			          	${id}.pagination.onLoad(params);
						
						<%-- 
						//ajax
						$.post("list",function(resp){
							alert(resp);
						});
						--%>
					}
				});
				
			';queryFormConfig/>
			
     <#- btTable Component - >
		<@component.btTable config="{'url':'','orderColumn':['name']}" attr="style:'color:green'" event="click:function(){
	    			console.log('click grid');
	   	}";gridConfig,plug>     		<#References to components or custom components in the plug-in pile can refer to parent configuration, and the location of the plug-in does not affect - >
	   		<#if plug == "btTableTemplate">
	   			
	   			<#- template component - >
	      		<@component.btTableTemplate config="{'mode':'custom','checkbox':'${gridConfig.checkbox}'}";config>
				<tr>
				<td class="bs-checkbox"><input name="btCheckAll" type="checkbox" value="{{ d.rows[i].id }}"></td>
				<td>{{ d.rows[i].id }}</td>
				<td>{{ d.rows[i].name }}</td>
				<td>Remarks</td>
				<td>{{ d.rows[i].createTime }}</td>
				<td>{{ d.rows[i].createTime }}</td>
				<td>No time</td>
				</tr>
				</@component.btTableTemplate>
	   		</#if>
	   		
	   		<#if plug == "gridToolBar">
	      		<@component.gridToolBar config="{'mode':'auto'}";config />
	   		</#if>
	   		
     </@component.btTable>
</@component.widget>

 

Operation effect:

 

Explain:

First, btTable introduces two plug s, one is btTable Template, and the other is gridToolBar. Look at the page specifically.

Importantly, the hook component is introduced. The significance of this component is to collect those element objects and manipulate them in an object-oriented way. The front-end library is jquery.

At the core, hideQueryForm is introduced to hide the query form component, in which the native parameter:
native='find("search").bind({
                    click:function(){
                        
// Call component
                        var params = ${id}.queryForm.serialize();
                          params = decodeURIComponent(params, true);
                          ${id}.pagination.onLoad(params);
                        
                        <%-- 
// You can also use ajax
                        $.post("list",function(resp){
                            alert(resp);
                        });
                        --%>
                    }
                });'

You can see that this parameter value directly manipulates js objects, executes js methods, and invokes components.

The find("search") method is to find elements with this Id in the component, assuming that the hideQueryForm template fragment we defined is as follows:

        <div class="form-row-cell">
          <button id="${componentId}-search" type="button" class="btn btn-primary btn-sm"><i class="fa fa-search"></i>&nbsp;search</button>
          <input type="reset" class="btn btn-default btn-sm" value="Reset" data-value="Reset">
        </div>

At this time, call find("search") method in native, you will find this control, bind click events to this control, fully support pure js writing, painless, sugar-free, easy to take, such as:

click:function(){alert('click');},mouseover:function(){alert('mouseover');} Multiple event bindings are separated by commas.

 

Sometimes it involves cross-component invocation, but it is not clear about the ID rules of other components, so hook components are introduced, such as: btTable contains pagination pagination component, which is automatically injected into hook, calling method directly through ${id}.pagination.onLoad(params), or operating pagination pagination paging object, calling in code. QueyForm and pagination are two objects.

The ${Id} is the Id number of the entire macro, such as demo110, which is generally generated randomly.

 

Fifth step, kill or not? Please leave a message, I contributed v1.5 code, preview!

How do you feel? It's still the most basic version. Many things have not been packaged properly. I hope you can make more valuable comments.

Plug-in Pile Reference: javamall

tiny interface, template engine reference: tiny

Template, Component, Control, Metadata Development Reference: eova

Front-end frameworks use lists: layer, myPagination, jquery.

Follow-up will continue to improve, is this framework useful? The template engine written by java is well known to all, haha.

 

The advantages of ejEngine are as follows:

1. Portability is strong, the front end can be replaced, template engine encapsulation, template engine to solve, switch to the bottom can be, just like the above code, switch to easyUI is very easy.

2. Non-strong coupling,. net, php,python and so on can be realized, regardless of which language, can write template.

3. With the function of metadata, edit template code online, realize rapid development dynamically, fast and accurate.

 

On csdn, smart front-end template rush address: GO

Preview:

 

Topics: JSON Java Javascript JQuery