OnMind y sus Componentes Web

© 2015-2019 by César Arcila
This is still subject to change

OnMind tiene una serie de componentes web para apoyar el desarrollo en proyectos de modo ágil, toda vez que se logre su reutilización. Tales componentes deben ser implementados (invocados y codificados), motivo para requerir esta guía. Si bien se tiene una aplicación interna, podría pensarse en una herramienta mucho más sofisticada que posibilite un diseño visual con una interfaz de arrastrar y soltar dirigida a casi cualquier usuario y sin tener que escribir en un editor de texto, no obstante su uso es sencillo y dirigido a un equipo con nociones técnicas de modo que por lo pronto el tiempo invertido puede ser semejante o cualquier diferencia no sería lo suficientemente significativa, de hecho innecesaria para un programador avanzado, sobretodo si se aplica el método OnMind.

Ejemplo esencial

<new-component style={ state.use.setStyle(state.styles) }>
    <at-nav id="navBar" super={ this } stage={ state.stage } use={ state.use } if={ (!state.stage.isInserting) } />

    <at-new id="newBar" ref="tabularNew" super={ this } stage={ state.stage } use={ state.use } onsend={ onCreate } oncancel={ onCancel } okclass={ okClass } if={ (state.stage.isInserting) }>
        <care-subject-new ref="data" stage={ state.stage } use={ state.use } sidebar="newBar" />
    </at-new>

    <at-row-if id="rowBar" ref="tabularRow" super={ this } stage={ state.stage } use={ state.use } editif="true" removeif="true" linkif="true" linkicon="sitemap" if={ (state.stage.isShowing) }>
        <care-subject-row ref="data" stage={ state.stage } use={ state.use } />
    </at-row-if>

    <at-browse id="pane" super={ this } stage={ state.stage } use={ state.use }>
        <care-subject-list stage={ state.stage } use={ state.use } onrow={ onRow } onlinked={ onLinked } />
    </at-browse>

    <script>
        const AbstractApp = window.nub.AbstractApp  // Web Application Class
        const kit = window.kit.load()  // Library with OnMind Components
        const use = new AbstractApp()  // Library for OnMind Routines & Utilities
        const it = use.init(use)  // Private variable for Objet Data

        // => ... here comes others assignments with "it"
        it.stage = {}

        const to = {  // Target Object for Javascript Module
            components: kit,  // or { kit.Component }
            onBeforeMount() { it.use.onBeforeMount(it, this) },  // Riot event covered by OnMind
            onMounted() { it.use.onMounted(it, this) },  // Riot event covered by OnMind
            onUpdated() { it.use.onUpdated(it) },  // Riot event covered by OnMind
            started(res) {
                it.use.started(it, res, 'ask', 'box', 'SHEET', true)  // OnMind definition to start
            }
        }

        // *!!* First Thing To Do When Started
        to.first = () => {}

        export default to  // Export Javascript Module
    </script>
</new-component>

En sentido general, estas líneas crean un nuevo componente de modo funcional (con las diversas operaciones sobre datos), es decir, que se podría operar en tanto se prepare y despliegue en el entorno respectivo.

Componentes genéricos de bloques

Entendiendo que como contexto nos referimos a “apps” para gestionar datos, OnMind dispone una dinámica en pantallas comunes con modos internos sobre el bloque de información a ser usado, dando lugar a: consulta en bloque, filtro en lista, selección, modo de inserción, modo de edición y borrado.

Se comprende como componente de bloque o agrupador aquel que tiene un nivel de mayor incidencia y que puede contener elementos internos o sub-componentes. Por lo general su nombre inicia con el prefijo at, si bien en su versión genérica podrían citarse basicamente 7 (sin sus variaciones), en tanto se identifiquen se comprenden los rasgos generales y el motivo por el cual se posibilita una implementación ágil.

Componente Descripción
at-browse Componente principal o bloque central para la hoja de datos o similar. Como variación se tiene at-browse-if, at-browse-to, at-browse-gap. Se usa at-browse-to para listar el detalle (items) de un documento pre-seleccionado con estilo encabezado-detalle, comprendiendo que por defecto el encabezado es simplemente informativo, no editable, o bien, se edita en la función previa)
at-nav Componente del bloque para la barra de navegación. Como variación se tiene at-nav-if, at-nav-to, at-nav-gap
at-new Componente del bloque para registro nuevo. Como variación se tiene at-new-gap, at-new-you
at-row Componente del bloque para edición y detalle de registros. Como variación se tiene at-row-if, at-row-to, at-row-gap, at-row-sum
at-raw Componente de bloque puro que consiste en una barra abierta para incrustar contenido o formularios personalizados. Se puede ver como alternativa a at-new y at-row
at-sidebar-sum Componente para menu lateral con acciones para documentos comerciales
at-upload Componente de bloque para carga de archivos. Se puede ver como una alternativa a at-raw orientada para subir archivos

Los elementos internos o sub-componentes corresponderían a una implementación personalizada de un nuevo componente o un componente del sistema ya utilizado internamente. En cualquier caso, la dinámica con datos tiende a ser reutilizable salvo por personalizaciones dispendiosas.

at-browse

<at-browse id="pane" stage={ stage } use={ use }>
    ...
</at-browse>
<at-browse-if id="pane" stage={ stage } use={ use } mode0if={ (condition1) } mode1if={ (condition2) } mode0icon="share alternate" mode1icon="table">
    ...
</at-browse-if>
<at-browse-to id="pane" stage={ stage } use={ use } detailuse="0">
    ...
</at-browse-to>
<at-browse-gap id="pane" stage={ stage } use={ use }>
    ...
</at-browse-gap>

at-nav

<at-nav id="navBar" stage={ stage } use={ use } if={ (!stage.isInserting) } />

at-new

<at-new id="newBar" ref="tabularNew" stage={ stage } use={ use } onsend={ onCreate } oncancel={ onCancel } okclass={ okClass } if={ (stage.isInserting) }>
    ...
</at-new>

at-row

<at-row id="rowBar" ref="tabularRow" stage={ stage } use={ use } if={ (stage.isShowing) }>
    ...
</at-row>

at-raw

<at-raw id="rawBar" ref="tabularRaw" stage={ stage } use={ use } if={ (condition) }>
    ...
</at-raw>

at-sidebar-sum

<at-sidebar-sum id="menuBar" stage={ stage } use={ use } confirmif={ (condition) } if={ stage.isShowingMenu } />

at-upload

<at-upload id="rawBar" ref="tabularRaw" stage={ stage } use={ use } if={ (condition) }>
    ...
</at-upload>

Eventos o métodos personalizables

El listado métodos a continuación corresponden a aquellos personalizables, es decir, que su comportamiento se puede alterar y en algunas casos es preciso que sea necesario implementar su propia lógica (por ejemplo: first, defaults y references). Se muestra el código por defecto (en Javascript) que se ejecuta si no se ha modificado el método. Para implementarlos lo que se hace es evaluar cuales se requieren modificar, reemplazando entonces con código específico dentro de los corchetes {}.

Por lo general, estos métodos pueden tener parámetros como ref, row, value, bit, e o ninguno (()). ref corresponde al nombre interno del formulario (ej. tabularRow, tabularNew), row es el objeto de datos de una fila, value se refiere a una expresion o valor, bit se refiere a una expresión corta, y e como parametro de eventos (por ejemplo, al dar clic en un botón)

Método & Código JS por defecto Descripción
to.first = () => {} Bajo este método se implementa la primera cosa por hacer cuando se ha inicializado el artefacto o componente mayor. Es posible lanzar la consulta inicial incluyendo to.onQuery(). Código que se ejecuta por defecto: to.first = () => { it.use.onChoice(it) }.
to.defaults = () => {} Devuelve los valores por defecto de un registro nuevo en un objeto {}. Código que se ejecuta por defecto: to.defaults = () => { return {} }
to.references = (ref, row) => {} Útil para establecer referencias a vínculos de datos o resolucionadores de éstas. Para traer datos que se encuentran en otra API o en otra ruta de la misma, se puede usar fetch o el metodo it.use.json() (ver anotación*). Código que se ejecuta por defecto: to.references = (ref, row) => { return true }
to.find = (value) => {} Para personalizar la búsqueda en bloque, estableciendo el critero con su respectivo valor. Recibe el valor para busqueda. Código que se ejecuta por defecto: to.find = (value) => { return true }
to.match = (bit, row) => {} Para personalizar el filtro de lista de resultados, evaluando la expresión de coincidencia. Recibe la expresión de filtro y el registro iterable. Código que se ejecuta por defecto: to.match = (bit, row) => { return true }
to.validateNew = () => {} Para validar un nuevo registro. Debe retornar verdadero si continúa la insercción, o falso para evitar que se inserte el registro. Aquí se personalizan las validaciones de formulario antes de ser enviadas al servidor. Código que se ejecuta por defecto: to.validateNew = () => { return true }
to.validateRow = () => {} Para validar un registro que se edita. Debe retornar verdadero si se acepta la edición, o falso para evitar que se envíe el cambio. Aquí se personalizan las validaciones de formulario antes de ser enviadas al servidor. Código que se ejecuta por defecto: to.validateRow = () => { return true }
to.afterLoad = () => {} Ocurre después de cargar la primera consulta o peticion de datos (query). Puede ser que en first se ejecute una consulta en tanto se inicia, pero no siempre es así. En todo caso el evento tendría lugar cuando se han cargado datos. Código que se ejecuta por defecto: to.afterLoad = () => { return null }
to.afterCreate = () => {} Ocurre después de crear o insertar un registro. Código que se ejecuta por defecto: to.afterCreate = () => { return null }
to.afterChange = () => {} Ocurre después de cambiar o editar un registro. Código que se ejecuta por defecto: to.afterChange = () => { return null }
to.touchRow = (row) => {} Cuando se registran directorios o círculos de contactos permite resolver el envío de datos del contacto principal si no existe. Esto es así en tanto que en el sistema todo contacto se registra en un único artefacto o tabla y estos pueden estar vinculados en diversos directorios o círculos, pero si al registrarlo en un directorio no existe no sería posible agregarlo. Es aquí dónde se resuelve esto. Código que se ejecuta por defecto: to.touchRow = (row) => { return null }
to.subtotal = () => {} Útil para establecer el mensaje de conteo de registro de modo personalizado. Por ejemplo, agregando alguna suma de otro dato. Código que se ejecuta por defecto: to.subtotal = () => { return null }
to.onPost = (ref) => {} Evento usado cuando se envía un registro nuevo en tanto se confirma. Recibe la referencia del formulario de datos. No suele alterarse salvo que se requiera código personalizado. Código que se ejecuta por defecto: to.onPost = (ref) => { it.use.onPost(it, null, ref) }
to.onPut = (ref) => {} Evento usado cuando se envía un registro editado en tanto se confirma. Recibe la referencia del formulario de datos. No suele alterarse salvo que se requiera código personalizado. Código que se ejecuta por defecto: to.onPut = (ref) => { it.use.onPut(it, null, ref) }
to.onDelete = () => {} Evento usado cuando se confirma la eliminación de un registro. No suele alterarse salvo que se requiera código personalizado. Código que se ejecuta por defecto: to.onDelete = () => { it.use.onDelete(it) }
to.onQuery = (e) => {} Evento asociado a botón cuando se hace petición para consulta de registros. No suele alterarse salvo que se requiera código personalizado. Código que se ejecuta por defecto: to.onQuery = (e) => { if (e) e.preventDefault(); it.use.onQuery(it) }
to.onNew = (e) => {} Evento asociado a botón cuando se confirma un registro nuevo. No suele alterarse salvo que se requiera código personalizado. Este evento invoca a validateNew por lo que las validaciones ya están contempladas. Código que se ejecuta por defecto: to.onNew = (e) => { if (e) e.preventDefault(); it.use.onNew(it) }
to.addAndNext = (e) => {} Ocurre en un botón de insercción con siguiente paso, siendo posible personalziar su lógica en este método. Código que se ejecuta por defecto: to.addAndNext = (e) => { e.preventDefault(); it.use.onAddAndNext(it) }

El metodo it.use.json() se puede usar como alternativa a fetch para obtener datos de la API de OnMind. Devuelve siempre JSON, incorpora cabeceras y token de conexion para OnMind. Básicamente se pasan 3 parámetros, siendo el tercero opcional, por ejemplo: it.use.json(uri, next, fail)

Eventos o métodos internos

A continuación se lista el código por defecto de los demás eventos internos cuyo comportamiento es generalmente el programado y sólo se sobreescriben si verdaderamente se necesita.

to.focus = (element)        => { it.use.onFocus(element) }
to.onNew       = (e)        => { if (e) e.preventDefault(); it.use.onNew(it) }
to.onBack      = (e)        => { if (e) e.preventDefault(); it.use.onBack(it) }
to.onFinder    = (e)        => { e.preventDefault(); it.use.onFinder(it) }
to.onEdit      = (e)        => { e.preventDefault(); it.use.onEdit(it) }
to.onRemove    = (e)        => { e.preventDefault(); it.use.onRemove(it) }
to.onHideProps = (e)        => { it.use.onHideProps(it) }
to.onCancel    = (e)        => { e.preventDefault(); it.use.onCancel(it) }
to.onUndo      = (e)        => { e.preventDefault(); it.use.onUndo(it) }
to.onCreate    = (e)        => { if (e) e.preventDefault(); it.use.onCreate(it) }
to.onAlter     = (e)        => { e.preventDefault(); it.use.onAlter(it) }
to.onPrint     = (e)        => { e.preventDefault(); it.use.onPrint(it) }
to.onSidebar   = (e)        => { e.preventDefault(); it.use.onSidebar(it) }
to.onEditLock  = (e)        => { e.preventDefault(); it.use.onEditLock(it) }
to.onChoice    = (e)        => { it.use.onChoice(it) }
to.onFind      = (e)        => { e.preventDefault(); it.use.onFind(it) }
to.onRow       = (e)        => { e.preventDefault(); it.use.onRow(it, e) }
to.onCard      = (e)        => { e.preventDefault(); it.use.onCard(it, e) }
to.onLinked    = (e)        => { if (e) e.preventDefault(); it.use.onLinked(it, e, null) }
to.onSubtotal  = (e)        => { e.preventDefault(); it.use.onSubtotal(it) }
to.onNext      = (e)        => { e.preventDefault(); it.use.onNext(it) }
to.onFilter    = (e)        => { e.preventDefault(); it.use.onFilter(it, e) }
to.getMatch    = (value)    => { it.use.getMatch(it, value) }
to.setBlank    = (row)      => { it.use.setBlank(it, row) }
to.setRecord   = (ref, row) => { it.use.setRecord(it, ref, row) }
to.getRecord   = (res)      => { it.use.getRecord(it, res) }
to.getData     = (res)      => { it.use.getData(it, res) }
to.fail        = (res)      => { it.use.fail(it, res) }
to.created     = (res)      => { it.use.created(it, res) }
to.changed     = (res)      => { it.use.changed(it, res) }
to.deleted     = (res)      => { it.use.deleted(it, res) }
to.getCookie   = ()         => { it.use.getCookie(it) }
to.saveCookie  = ()         => { it.use.saveCookie(it) }
to.clearCookie = ()         => { it.use.clearCookie(it) }
to.reload      = (e)        => { it.use.toReload(it, e, (res) => { it.use.whenReload(it, res) }) }
to.onExport    = (e)        => { it.use.onExport(e) }
to.onHelp      = (e)        => { e.preventDefault(); it.use.onHelp(it) }
to.onPhone     = (e)        => { e.preventDefault(); it.use.onPhone(it) }
to.asAmount    = (m)        => { return it.use.asAmount(m, true) }
to.drawNodes   = ()         => { it.use.drawNodes(it) }
to.renderNodes = (source)   => { it.use.renderNodes(it, source) }
to.onNode      = (d)        => { return null }