Recherche d'un framework JS pour la génération d'interface utilisateur


D'une certaine manière, je voulais essayer d'implémenter une petite application client-serveur. L'implémentation dans l'idée était la suivante: côté client, nous dessinons des fenêtres en utilisant une sorte de framework JS, côté serveur, nous traitons les appels du client en utilisant une application compilée écrite par exemple en Go.


J'ai commencé à prendre un framework qui fournit une interface déclarative. À mon avis, il devrait recevoir la description du contenu du formulaire aussi simple que possible (par exemple, au format YAML, JSON, BEMJSON , XML ou même INF), et à la sortie, dessiner un beau formulaire dans le navigateur avec des contrôles de travail.


Professionnellement, je ne suis pas impliqué dans le développement web et ne m'attendais donc pas à être bloqué longtemps à ce stade.


Et je n'allais pas écrire un article, mais j'ai pensé qu'une nouvelle vision extérieure des technologies Web modernes, même pour un débutant, pourrait être intéressante pour la communauté. Une occasion de recevoir à nouveau des retours ...


Modèles HTML et CSS


Tout ce que j'ai trouvé n'a pas fonctionné comme je le voulais. Je ne veux rien savoir de la mise en page et des styles. Je ne veux pas non plus remplir le HTML, car cela se fait lors de l'application de cadres tels que Bootstrap , Skeleton , MDL , etc.


Exemple de code utilisant Bootstrap:


<div class="input-group mb-3"> <div class="input-group-prepend"> <div class="input-group-text"> <input type="checkbox" aria-label="Checkbox for following text input"> </div> </div> <input type="text" class="form-control" aria-label="Text input with checkbox"> </div> <button type="button" class="btn btn-primary btn-lg btn-block">Block level button</button> 

Tout ce que je veux appliquer à l'entrée devrait être quelque chose comme ceci:


 { "main": { "prop":{}, "elements": [ { "type":"checkbox", "name":" -", "style":"classic", "checked": true }, { "type":"button", "name":"", "style":"outline", "onclick":"btnsave_onclick" } } } 

Eh bien, pour qu'il soit possible de combiner des éléments en groupes et d'indiquer comment les éléments doivent être situés à l'intérieur du groupe: verticalement ou horizontalement. Quelle sera la conception là-bas, ce n'est pas particulièrement important pour moi. Je m'attendais à trouver même une sorte d'éditeur de fenêtre visuelle, mais non.


Assemblage technologique


Ensuite, j'ai essayé de chercher des assemblages technologiques - quelqu'un devait avoir parcouru un long chemin depuis longtemps et plus d'une fois.


Et j'ai trouvé quelque chose de similaire à ce dont j'ai besoin: un site à partir de zéro sur une pile complète de technologies BEM .


Exemple BEMJSON:


 ({ block: 'page', title: 'Hello, World!', styles: [ { elem: 'css', url: 'index.min.css' } ], scripts: [ { elem: 'js', url: 'index.min.js' } ], content: [ 'Hello, World!' ] }) 

Lorsque j'ai fait défiler le 20e écran du texte et que le curseur était toujours en haut, j'ai pensé que ce court chemin était en quelque sorte long. Ça ne marchera pas.


Soit dit en passant, j'ai lu sur ce BEM - j'ai aimé l'essence, et il est bien décrit. Il existe différentes feuilles de triche, par exemple, j'ai aimé celle-ci . J'ai également cherché des informations et constaté que tout le monde n'aime pas la technologie (par exemple, ici et ici ). Et ici, à mon avis, l'essence des points controversés est énoncée.


Ce qui est intéressant, c'est qu'en utilisant la recherche, en dehors de BEM, pendant longtemps je n'ai pas pu trouver d'autres technologies alternatives. Cependant, ils sont: OOCS (un peu comme le même BEM, juste plus simple), SMACSS , AtomicCSS , CSSinJS (trouvé ici ).


Node.js


J'ai alors pensé, ok, vous pouvez abandonner l'idée originale et implémenter la partie serveur dans un langage de script, c'est-à-dire simplement utiliser nodejs. Ce n'est pas seulement à la mode, élégant, jeune, c'est aussi l'occasion d'écrire sur toutes les couches dans une langue. Encore beaucoup d'articles (j'ai aimé celui-ci sur le démarrage rapide). Si je comprends bien, d'innombrables packages NPM sont écrits pour cela, pour presque toutes les tâches. Il y a toujours une chose aussi sérieuse qu'Electron dans cette affaire.


Exemple de code de page utilisant Electron:


 <head> <meta charset="utf-8"> <link rel="stylesheet" href="assets/css/variables.css"> <link rel="import" href="sections/about.html"> </head> <body> <nav class="nav js-nav"> <header class="nav-header"> <h1 class="nav-title">Electron <strong>API Demos</strong></h1> </header> <div class="nav-item u-category-windows"> </div> <footer class="nav-footer"> <button type="button" id="button-about" data-modal="about" class="nav-footer-button">About</button> </footer> </nav> <main class="content js-content"></main> <script> require('./assets/normalize-shortcuts') </script> </body> </html> 

Le seul noeud moins qu'il est possible d'écrire des choses plus productives, en utilisant Go.
Supposons que je veuille utiliser nodejs. Il existe un catalogue de packages NPM afin que vous puissiez choisir vous-même quelque chose qui vous convient.


Il existe un package appelé formulaires . Voici un exemple de code:


 var forms = require('forms'); var fields = forms.fields; var validators = forms.validators; var reg_form = forms.create({ username: fields.string({ required: true }), password: fields.password({ required: validators.required('You definitely want a password') }), confirm: fields.password({ required: validators.required('don\'t you know your own password?'), validators: [validators.matchField('password')] }), email: fields.email() }); 

Ceci est assemblé dans le formulaire par la commande reg_form.toHTML(); . Une option intéressante, mais pas ça.


Il existe également Element , Riot , TotalJS et iView . Cela peut être ajouté à la liste à partir de Bootstrap, MDL, etc.


Exemple de code de page utilisant Element:


 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <!-- import CSS --> <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"> </head> <body> <div id="app"> <el-button @click="visible = true">Button</el-button> <el-dialog :visible.sync="visible" title="Hello world"> <p>Try Element</p> </el-dialog> </div> </body> <!-- import Vue before Element --> <script src="https://unpkg.com/vue/dist/vue.js"></script> <!-- import JavaScript --> <script src="https://unpkg.com/element-ui/lib/index.js"></script> <script> new Vue({ el: '#app', data: function() { return { visible: false } } }) </script> </html> 

Il existe également des Blueprintsjs . C'est peut-être le plus similaire à ce que je cherchais. Il est possible de configurer les commandes séparément les unes des autres.


Le code ressemble à ceci:


Beaucoup de code utilisant des plans
 /* * Copyright 2018 Palantir Technologies, Inc. All rights reserved. * * Licensed under the terms of the LICENSE file distributed with this project. */ import * as React from "react"; import { FormGroup, H5, InputGroup, Intent, Switch } from "@blueprintjs/core"; import { Example, handleBooleanChange, handleStringChange, IExampleProps } from "@blueprintjs/docs-theme"; import { IntentSelect } from "./common/intentSelect"; export interface IFormGroupExampleState { disabled: boolean; helperText: boolean; inline: boolean; intent: Intent; label: boolean; requiredLabel: boolean; } export class FormGroupExample extends React.PureComponent<IExampleProps, IFormGroupExampleState> { public state: IFormGroupExampleState = { disabled: false, helperText: false, inline: false, intent: Intent.NONE, label: true, requiredLabel: true, }; private handleDisabledChange = handleBooleanChange(disabled => this.setState({ disabled })); private handleHelperTextChange = handleBooleanChange(helperText => this.setState({ helperText })); private handleInlineChange = handleBooleanChange(inline => this.setState({ inline })); private handleLabelChange = handleBooleanChange(label => this.setState({ label })); private handleRequiredLabelChange = handleBooleanChange(requiredLabel => this.setState({ requiredLabel })); private handleIntentChange = handleStringChange((intent: Intent) => this.setState({ intent })); public render() { const { disabled, helperText, inline, intent, label, requiredLabel } = this.state; const options = ( <> <H5>Props</H5> <Switch label="Disabled" checked={disabled} onChange={this.handleDisabledChange} /> <Switch label="Inline" checked={inline} onChange={this.handleInlineChange} /> <Switch label="Show helper text" checked={helperText} onChange= {this.handleHelperTextChange} /> <Switch label="Show label" checked={label} onChange={this.handleLabelChange} /> <Switch label="Show label info" checked={requiredLabel} onChange= {this.handleRequiredLabelChange} /> <IntentSelect intent={intent} onChange={this.handleIntentChange} /> </> ); return ( <Example options={options} {...this.props}> <FormGroup disabled={disabled} helperText={helperText && "Helper text with details..."} inline={inline} intent={intent} label={label && "Label"} labelFor="text-input" labelInfo={requiredLabel && "(required)"} > <InputGroup id="text-input" placeholder="Placeholder text" disabled={disabled} intent={intent} /> </FormGroup> <FormGroup disabled={disabled} helperText={helperText && "Helper text with details..."} inline={inline} intent={intent} label={label && "Label"} labelFor="text-input" labelInfo={requiredLabel && "(required)"} > <Switch id="text-input" label="Engage the hyperdrive" disabled={disabled} /> <Switch id="text-input" label="Initiate thrusters" disabled={disabled} /> </FormGroup> </Example> ); } } 

Le résultat ressemble à ceci:


Même si nous ne prenons en compte que la dernière partie: tout de même, en quelque sorte ce n'est pas très concis.


metadata.js


J'ai également trouvé une chose intéressante sur le Web: les métadonnées . Un démarrage rapide est ici . Cette bibliothèque a des fonctionnalités redondantes, et dans l'article, à mon avis, la chose la plus importante n'est pas - la description de la bibliothèque elle-même. C'est ici .


On nous propose de décrire le formulaire comme suit:


Description du formulaire utilisant metadata.js
 { "enm": {}, "cat": { "": { "form": { "selection": { "fields": [ "is_folder", "id", "", "_t_.name as presentation", "`cat_`.name as ``", "`cat_`.name as ``" ], "cols": [ {"id": "id", "width": "120", "type": "ro", "align": "left", "sort": "server", "caption": ""}, {"id": "", "width": "150", "type": "ro", "align": "left", "sort": "server", "caption": ""}, {"id": "presentation", "width": "*", "type": "ro", "align": "left", "sort": "server", "caption": ""}, {"id": "", "width": "70", "type": "ro", "align": "left", "sort": "server", "caption": ""}, {"id": "", "width": "170", "type": "ro", "align": "left", "sort": "server", "caption": " "} ] } } }, "": { "form": { "selection": { "fields": [ "is_folder", "id", "_t_.name as presentation", "`enm_`.synonym as ``", "`cat_`.name as ``" ], "cols": [ {"id": "presentation", "width": "*", "type": "ro", "align": "left", "sort": "server", "caption": ""}, {"id": "", "width": "150", "type": "ro", "align": "left", "sort": "server", "caption": " "}, {"id": "", "width": "150", "type": "ro", "align": "left", "sort": "server", "caption": ""} ] } } } }, "doc": { "": { "form": { "selection": { "fields": [ "date", "number_doc", "", "", "posted", "", "" ], "cols": [ {"id": "date", "width": "120", "type": "ro", "align": "left", "sort": "server", "caption": ""}, {"id": "number_doc", "width": "120", "type": "ro", "align": "left", "sort": "server", "caption": ""}, {"id": "", "width": "170", "type": "ro", "align": "left", "sort": "server", "caption": ""}, {"id": "", "width": "120", "type": "ron", "align": "right", "sort": "server", "caption": ""}, {"id": "", "width": "100", "type": "ro", "align": "left", "sort": "server", "caption": ""}, {"id": "", "width": "*", "type": "ro", "align": "left", "sort": "server", "caption": ""} ] }, "obj": { "head": { " ": ["number_doc", "date", "", "", "", ""], "": ["", "", ""], "": ["", "", "", "", "", "", "", {"id": "", "path": "o.", "synonym": " ", "type": "ro"} ] }, "tabular_sections": { "": { "fields": ["row","","","","","","","","","","","","",""], "headers": "№,,,.,,,% ,,% ,,,.,,", "widths": "40,*,*,70,50,70,70,70,70,70,70,70,70,80", "min_widths": "40,200,140,70,50,70,70,70,70,70,70,70,70,80", "aligns": "", "sortings": "na,na,na,na,na,na,na,na,na,na,na,na,na,na", "types": "cntr,ref,ref,calck,refc,calck,calck,ron,refc,ron,ron,dhxCalendar,ref,txt" } } } } } }, "ireg": {}, "areg": {}, "dp": {}, "rep": {}, "cch": {}, "cacc": {} } 

C'est très similaire à ce dont vous avez besoin. Cependant, des questions se posent: le jeu de boutons semble-t-il être défini par défaut? Puis-je le changer? Où est la description de la syntaxe (qu'est-ce que "enm", "cat", etc.)?
On peut voir que beaucoup d'efforts ont été mis dans le projet, mais la bibliothèque est encore humide.


Conclusion


Spéculons. Les formulaires peuvent être pré-dessinés sur le serveur, compilés et soumis au code client html / css / js prêt. Dans ce cas, vous devez rechercher quelque chose de complètement différent. Mais si nous voulons que l'utilisateur travaille avec nous dans une seule fenêtre de navigateur, et dans cette fenêtre voir les fenêtres enfants de l'application Web, il ne serait pas logique de mettre en cache le code css / js sur le client, qui recevra de courtes commandes du serveur avec une description du formulaire suivant et les tirera au détriment des ressources clients?


Chers collègues, où cachez-vous le cadre d'interface déclarative? )


Il doit l'être! Pourquoi je ne peux pas le trouver?

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


All Articles