React

React es una biblioteca para crear aplicaciones SPA desarrollada por Facebook. Es muy usada actualmente. Por esta razón, Dogma soporta de manera nativa el desarrollo con React. Actualmente, se ha probado su uso en varios proyectos con Semantic UI React.

Directiva #!react

Una directiva #!react, al comienzo de un módulo dogmático, indica que estamos ante un módulo que define o trabaja con React. Cuando el compilador se encuentra con esta directiva, añade el siguiente código al archivo JavaScript final:

import React, {Component} from "react";

Si no declara la directiva #!react, el compilador propagará un error si intenta declarar elementos XML.

Definición de componentes

Una vez indicado que el archivo va a trabajar con React, puede definir los componentes mediante funciones y/o tipos. En el caso de los tipos, no hay más que heredar Component (importado implícitamente por la directiva #!react) u otro componente.

Ejemplo de componente funcional:

#!react

#imports
from "semantic-ui-react" use Button

#My button component.
@component
export fn MyButton() = <Button>"Click here"</Button>

Y ahora un componente de clase:

#!react

#imports
from "semantic-ui-react" use Button

#My button component.
@component
export type MyButton(props) : Component(props)

@override
fn MyButton.render() = <Button content="Click here" onClick=(.handleClick) />

#Handle the click.
@bind
fn MyButton.handleClick(e)
  #...

Uso de JSX

Dogma soporta una versión propia de JSX, para ayudar a escribir componentes React. Se parecen algo, pero si desarrolla con Dogma tendrá que hacer un pequeño cambio de chip. Mínimo pero hay que hacerlo. La parte positiva es que se ha acercado al máximo al lenguaje Dogma, lo que facilita su aprendizaje.

Definición de elementos

Por un lado, tenemos cómo definir los elementos React. Al igual que JSX, mediante:

<elemento ...>contenido</elemento>
<elemento .../>

Declaración de propiedades

Las propiedades de los elementos contienen la mayor parte de las diferencias con JSX. Por un lado, toda propiedad tiene un nombre y un valor, el cual:

También es posible indicar la sintaxis de los mapas literales que se muestra a continuación:

{propiedad} = (valor)

#similar a:
propiedad = (valor.propiedad)

Además, se puede indicar que incluya los campos de un objeto, mediante las siguientes sintaxis:

...variable
...constante
...(expresión)

Veamos unos ejemplos ilustrativos:

<Image src="/images/viewframe/image.png" size="small" wrapped=true />
<Image src=("/images" + "/viewframe" + "/image.png") size=("small") wrapped />
<div style={color="white", backgroundImage=fmt("url(%s)", imgUrl)}>

Recordemos que si una propiedad omite su valor, debe existir una variable con el mismo nombre. Así pues, en el ejemplo anterior, wrapped es lo mismo que wrapped=wrapped. Esto es muy diferente a JSX, donde la declaración de una propiedad sin valor lo que hace es asignarle el valor true.

Declaración de contenido

Cuando un elemento no presenta contenido, puede declararse como sigue:

#sin contenido
<elemento propiedades />
<elemento propiedades></elemento>

Pero si tiene contenido, las diferencias con JSX son mayúsculas. Tenga mucho cuidado. El contenido de un elemento consiste en una secuencia de expresiones dogmáticas y/u otros elementos. Donde las expresiones se delimitan por paréntesis (()). Ejemplos:

#contenido simple: una expresión
<div>(1+2+3)</div>                        #similar a <div>{1+2+3}</div> en JSX
<title>("My title")</title>               #similar a <title>{"My title"}</title> en JSX
<title>(fmt("Hello %s!", user))</title>   #similar a <title>{fmt("Hello %s!", user)}</title>

#contenido múltiple formado por varios elementos
<div>
  <one/>
  <two/>
  <three/>
</div>

#contenido múltiple formado por varias expresiones
<div>
  (one)
  (two)
  (three)
</div>

#contenido múltiple mixto
<div>
  <one />
  (two)
  <three />
</div>

Recordemos que en JSX, las expresiones se delimitan por llaves ({}), pero en Dogma, por paréntesis (()).

Uso de la anotación @bind

Cada vez que se define un método de instancia con la anotación @bind, el compilador crea en cada instancia del tipo un miembro que lo ata al método. Por ejemplo, cuando el compilador se encuentra con lo siguiente:

@bind
fn DesktopContainer.hideFixedMenu()
  .setState({fixed=false})

Lo que hace es crear automáticamente el campo hideFixedMenu, en el constructor del tipo DesktopContainer, como sigue:

this.hideFixedMenu = (function() {
  this.setState({fixed: false});
}).bind(this);

De esta manera, se puede indicar muy fácilmente los controladores de los eventos como, por ejemplo:

<Visibility onBottomPassed=(.showFixedMenu) onBottonPassedReverse=(.hideFixedMenu)>
  ...
</Visibility>

En vez de:

<Visibility onBottomPassed=(.showFixedMenu.bind(self)) onBottonPassedReverse=(.hideFixedMenu.bind(self))>
  ...
</Visibility>

Para que @bind se aplique a las definiciones de métodos, es necesario que su tipo se haya declarado como @component. En nuestro ejemplo, tendrá que ser definido como sigue:

@component
type DesktopContainer(props) : Component(...)

Por convenio y buenas prácticas, también se recomienda marcar los componentes funcionales como @component. Ejemplo:

@component
fn HomeHeading(props=>{mobile}) = <Container>
  <Header
    as = "h1"
    content = "Dogma"
    inverted = true
    style = {
      fontSize = if mobile then "2em" else "4em" end
      fontWeight = "normal"
      marginBottom = 0
      marginTop = if mobile then "1.5em" else "3em" end
    }
  />

  <Header
    as = "h2"
    content = "Lenguaje de programación de scripts"
    inverted = true
    style = {
      fontSize = if mobile then "1.5em" else "1.7em" end
      fontWeight = "normal"
      marginTop = if mobile then "0.5em" else "1.5em" end
    }
  />

  <Button primary=true size="huge">
    ("Introducción")
    <Icon name="right arrow" />
  </Button>
</Container>

HomeHeading.propTypes = {
  mobile = PropTypes.bool
}