-
-
Notifications
You must be signed in to change notification settings - Fork 59
View Formats
nin-jin edited this page Feb 14, 2021
·
6 revisions
Comparison of component description formats. We use view.tree
because it is compact and powerful.
- Компактность. Чем больше кода, тем больше времени тратится на его поддержку, больше точек отказа, сложнее ориентироваться.
- Наглядность. Синтаксис должен недвусмысленно говорить о том, что происходит. По возможности в мозгу должны вырабатываться мнемоники.
- Простота. Чем меньше идиом и исключений, тем проще и быстрее освоить язык.
- Сложные свойства. Любое свойство может содержать любые JSON данные вперемешку со вложенными компонентами.
- Связывания свойств. Вложенные компоненты через свойства связываются с корневым. Связи бывают: левосторонними, правосторонними, двусторонними. Свойства бывают мутабельные и немутабельные. А так же они бывают синглтонами и мультитонами. Кроме того они могут быть локализуемыми и не локализуемыми.
- Объявления свойств. Свойства объявляются как отдельно, так и инлайново. При объявлении, у свойства тут обязательно же задаётся значение по умолчанию.
- Декларативность. Необходима для стат анализа кода компонент. Например, необходимо для экстракции локализованных строк, автоматического формирования конфигуратора и тд.
- Типизация. Тайпскрипт должен получать максимум информации о типах, чтобы проверить корректность использования компонент.
- HTML многословный. Много визуального шума мешает понимать суть.
- Даже со всеми наворотами IDE HTML не очень удобно редактировать.
- Во view.tree значением любого свойства может быть что угодно и даже целые поддеревья компонент. В $mol это активно используется. В HTML же значениями именованных свойств могут быть только строки, а дерево может быть только одно. JSX в принципе позволяет и деревья в атрибуты запихивать, но это уже совсем далеко от HTML получается.
- В HTML основной акцент делается на тип узла, а не его уникальное имя (которого обычно вообще нет).
view.tree
наоборот, ставит акцент на уникальном имени (и у каждого узла оно обязано быть), а тип - второстепенная вариативная информация. - view.tree наглядно показывает направление движения данных (стрелочками). В HTML приходится использовать малонаглядные костыли типа "бананы в ящике".
$mol_app_users $mol_page
head /
<= Filter $mol_string
hint <= filter_hint @ \Search users on GitHub
value?val <=> filter_query?val \
body /
<= List $mol_list
rows <= user_rows /
Foot $mol_row
sub /
<= Reload $mol_button_minor
title <= reload_title @ \Reload
event_click?val <=> event_reload?val null
<= Add $mol_button_minor
enabled <= loaded false
title <= add_title @ \Add
event_click?val <=> event_add?val null
<= Save $mol_button_major
enabled <= changed false
title <= save_title @ \Save
event_click?val <=> event_save?val null
<= Message $mol_status
status <= users_master /
$mol_app_users_item $mol_row
sub /
<= Title $mol_string
value?val <=> title?val \
<= Drop $mol_button_minor
title <= drop_title @ \Drop
event_click?val <=> event_drop?val null
namespace $ { export class $mol_app_users extends $mol_page {
head() {
return [].concat( this.Filter() )
}
@ $mol_mem
Filter() {
const obj = new this.$.$mol_string
obj.hint = () => this.filter_hint()
obj.value = ( val? : any ) => this.query( val )
return obj
}
filter_hint() {
return this.$.$mol_locale.text( "$mol_app_users_filter_hint" )
}
@ $mol_mem
query( val? : any , force? : $mol_atom_force ) {
return ( val !== void 0 ) ? val : ""
}
body() {
return [].concat( this.List() )
}
@ $mol_mem
List() {
const obj = new this.$.$mol_list
obj.rows = () => this.user_rows()
return obj
}
user_rows() {
return [] as any[]
}
@ $mol_mem
Foot() {
const obj = new this.$.$mol_row
obj.sub = () => [].concat( this.Reload() , this.Add() , this.Save() , this.Message() )
return obj
}
@ $mol_mem
Reload() {
const obj = new this.$.$mol_button_minor
obj.title = () => this.reload_title()
obj.event_click = ( val? : any ) => this.event_reload( val )
return obj
}
reload_title() {
return this.$.$mol_locale.text( "$mol_app_users_reload_title" )
}
@ $mol_mem
event_reload( val? : any , force? : $mol_atom_force ) {
return ( val !== void 0 ) ? val : null as any
}
@ $mol_mem
Add() {
const obj = new this.$.$mol_button_minor
obj.enabled = () => this.loaded()
obj.title = () => this.add_title()
obj.event_click = ( val? : any ) => this.event_add( val )
return obj
}
loaded() {
return false
}
add_title() {
return this.$.$mol_locale.text( "$mol_app_users_add_title" )
}
@ $mol_mem
event_add( val? : any , force? : $mol_atom_force ) {
return ( val !== void 0 ) ? val : null as any
}
@ $mol_mem
Save() {
const obj = new this.$.$mol_button_major
obj.enabled = () => this.changed()
obj.title = () => this.save_title()
obj.event_click = ( val? : any ) => this.event_save( val )
return obj
}
changed() {
return false
}
save_title() {
return this.$.$mol_locale.text( "$mol_app_users_save_title" )
}
@ $mol_mem
event_save( val? : any , force? : $mol_atom_force ) {
return ( val !== void 0 ) ? val : null as any
}
@ $mol_mem
Message() {
const obj = new this.$.$mol_status
obj.status = () => this.users_master()
return obj
}
users_master() {
return [] as any[]
}
} }
namespace $ { export class $mol_app_users_item extends $mol_row {
sub() {
return [].concat( this.Title() , this.Drop() )
}
@ $mol_mem
Title() {
const obj = new this.$.$mol_string
obj.value = ( val? : any ) => this.title( val )
return obj
}
@ $mol_mem
title( val? : any , force? : $mol_atom_force ) {
return ( val !== void 0 ) ? val : ""
}
@ $mol_mem
Drop() {
const obj = new this.$.$mol_button_minor
obj.title = () => this.drop_title()
obj.event_click = ( val? : any ) => this.event_drop( val )
return obj
}
drop_title() {
return this.$.$mol_locale.text( "$mol_app_users_item_drop_title" )
}
@ $mol_mem
event_drop( val? : any , force? : $mol_atom_force ) {
return ( val !== void 0 ) ? val : null as any
}
} }
export class $mol_app_users extends $mol_page {
this.head() = ()=> [
this.Filter() = ()=> $mol_string({
hint : ()=> this.filter_hint() = ()=> $mol_locale.text( this , 'Search users on GitHub' ),
value : (next) => this.filter_query( next ) = ()=> '',
})
]
this.body() = ()=> [
this.List() = ()=> $mol_list({
rows : ()=> this.user_rows() = ()=> []
})
]
this.Foot() = ()=> $mol_row({
sub = ()=> [
this.Reload() = ()=> $mol_button_minor({
title : ()=> this.reload_title() = ()=> $mol_locale.text( this , 'Reload' ),
event_click : (val)=> this.event_reload(val) = (val)=> null,
}),
this.Add() = ()=> $mol_button_minor({
enabled : ()=> this.loaded() = ()=> false,
title : ()=> this.add_title() = ()=> $mol_locale.text( this , 'Add' ),
event_click : (val)=> this.event_add(val) = (val)=> null,
}),
this.Save() = ()=> $mol_button_major({
enabled : ()=> this.changed() = ()=> false
title : ()=> this.save_title() = ()=> $mol_locale.text( this , 'Save' )
event_click : (val)=> this.event_save(val) = (val)=> null
}),
this.Message() = ()=> $mol_status({
status : ()=> this.users_master() = ()=> []
}),
]
})
}
export class $mol_app_users_item extends $mol_row {
this.sub() = ()=> [
this.Title() = ()=> $mol_string({
value : (val)=> this.title(val) = ()=> ''
}),
this.Drop() = ()=> $mol_button_minor({
title : ()=> this.drop_title() = ()=> $mol_locale.text( this , 'Drop' ),
event_click : (val)=> this.event_drop(val) = (val)=> null,
}),
]
}
<mol_page bind=mol_app_users>
<head>
<mol_string bind=Filter>
<hint bind=filter_hint locale="Search users on GitHub" />
<value bidi=filter_query value="" />
</mol_string>
</head>
<body>
<mol_list bind=List>
<rows bind=user_rows></rows>
</mol_list>
</body>
<mol_row bind=Foot>
<sub>
<mol_button_minor bind=Reload>
<title bind=reload_title locale="Reload" />
<event_click bidi=event_reload value=null />
</mol_button_minor>
<mol_button_minor bind=Add>
<title bind=add_title locale="Add" />
<enabled bind=loaded value=false />
<event_click bidi=event_add value=null />
</mol_button_minor>
<mol_button_major bind=Save>
<title bind=save_title locale="Save" />
<enabled bind=changed value=false />
<event_click bidi=event_save value=null />
</mol_button_major>
<mol_status bind=Message>
<status bind=users_master></status>
</mol_status>
</sub>
</mol_row>
</mol_page>
<mol_row bind=mol_app_users_item>
<sub>
<mol_string bind=Title>
<value bidi=title value="" />
</mol_string>
<mol_button_minor bind=Drop>
<sub bind=drop_title locale="Drop" />
<event_click bidi=event_drop value=null />
</mol_button_minor>
</sub>
</mol_row>
<defs:defs xmlns:defs="mol_defs" xmlns:def="mol_def" xmlns:prop="mol_prop" xmlns="mol_arg">
<def:mol_app_users extends="mol_page">
<prop:head>
<prop:Filter extends="mol_string">
<hint>
<prop:filter_hint locale="Search users on GitHub" />
</hint>
<value way="two">
<prop:filter_query string="" />
</value>
</prop:Filter>
</prop:head>
<prop:body>
<prop:List extends="mol_list">
<rows>
<prop:user_rows />
</rows>
</prop:List>
</prop:body>
<prop:Foot extends="mol_row">
<sub>
<prop:Reload extends="mol_button_minor">
<title>
<prop:reload_title locale="Reload" />
</title>
<event_click bidi="event_reload" value="null" />
</prop:Reload>
<prop:Add extends="mol_button_minor">
<title>
<prop:add_title locale="Add" />
</title>
<enabled>
<prop:loaded value="false" />
</enabled>
<event_click bidi="event_add" value="null" />
</prop:Add>
<prop:Save extends="mol_button_major">
<title>
<prop:save_title locale="Save" />
</title>
<enabled>
<prop:changed value="false" />
</enabled>
<event_click bidi="event_save" value="null" />
</prop:Save>
<prop:Message extends="mol_status">
<status>
<prop:users_master value="null" />
</status>
</prop:Message>
</sub>
</prop:Foot>
</def:mol_app_users>
<def:mol_app_users_item extends="mol_row">
<sub>
<prop:Title extends="mol_string">
<value bidi="title" string="" />
</prop:Title>
<prop:Drop extends="mol_button_minor">
<title>
<prop:drop_title locale="Drop" />
</title>
<event_click bidi="event_drop" value="null" />
</prop:Drop>
</sub>
</def:mol_app_users_item>
</defs:defs>
{
"mol_app_users": {
"_extends": "mol_view",
"head": [
{
"_prop": "Filter",
"_extends": "mol_string",
"hint": {
"_prop": "filter_hint",
"_locale": "Search users on GitHub"
},
"value": {
"_bidi": "filter_query",
"_value": ""
}
},
],
"body": [
{
"_prop": "List",
"_extends": "mol_list",
"rows": {
"_prop": "user_rows",
"_value": []
}
}
],
"Foot": {
"_extends": "mol_row",
"sub": [
{
"_prop": "Reload",
"_extends": "mol_button_minor",
"title": {
"_prop": "reload_title",
"_locale": "Reload"
},
"event_click": {
"_bidi": "event_reload",
"_value": null
}
},
{
"_prop": "Add",
"_extends": "mol_button_minor",
"title": {
"_prop": "add_title",
"_locale": "Add"
},
"enabled": {
"_prop": "loaded",
"_value": false
},
"event_click": {
"_bidi": "event_add",
"_value": null,
}
},
{
"_prop": "Save",
"_extends": "mol_button_major",
"title": {
"_prop": "save_title",
"_locale": "Save"
},
"enabled": {
"_prop": "changed",
"_value": false
},
"event_click": {
"_bidi": "event_save",
"_value": null
}
},
{
"_prop": "Message",
"_extends": "mol_status",
"status": [
{
"_prop": "users_master",
"_value": null
}
]
}
]
}
},
"mol_app_users_item": {
"_extends": "mol_row",
"sub": [
{
"_prop": "Title",
"_extends": "mol_string",
"value": {
"_bidi": "title",
"_value": "",
}
},
{
"_prop": "Drop",
"_extends": "mol_button_minor",
"title": {
"_prop": "drop_title",
"_locale": "Drop"
},
"event_click": {
"_bidi": "event_drop",
"_value": null,
}
}
]
}
}