TODO LO QUE NO SABES (O SÍ) sobre los EMBER ADDONS

quién TE VA A DAR LA CHAPA LOS PRÓXIMOS 40 MINUTOS

Juan Antonio Gómez

shokmaster@gmail.com

Primero un poco de teoría...

?

?

?

¿qué es UN ADDON?

¿qué ES UN ADDON?

Se basa en el concepto de plug-in

Es una mini app, que en tiempo de construcción se mezcla con la app en la que se instala

Contiene código reutilizable

Primero un poco de teoría...

?

?

?

¿cómo es UN ADDON por dentro?

¿qué ES?

Primero un poco de teoría...

¿CÓMO ES UN ADDON por dentro?

Tiene (casi) la misma estructura de ficheros que una aplicación

Funcionan (casi) los mismos comandos de Ember CLI que en una aplicación

Primero un poco de teoría...

¿CÓMO ES UN ADDON por dentro?

addon/
app/
blueprints/
config/
   environment.js
tests/
   dummy/
   helpers/
   integration/
   unit/
vendor/
index.js
package.json

Punto de entrada de Node. Aquí aprovechamos los hooks de Broccoli

postBuild, includedCommands, treeForPublic...

Namespace del addon

Se mezcla con el namespace de la aplicación

Todos los blueprints del addon

Primero un poco de teoría...

Aplicación de apoyo para tests

ADDON

Configuración por defecto

 $ ember g metrics-adapter myAdapter

¿CÓMO ES UN ADDON por dentro?

addon/
app/
blueprints/
config/
   environment.js
tests/
   dummy/
   helpers/
   integration/
   unit/
vendor/
index.js
package.json

Primero un poco de teoría...

app/
config/
   environment.js
public/
tests/
   helpers/
   integration/
   unit/
vendor/
package.json

ADDON

APP

?

?

?

¿dónde buscar
addons?

¿CÓMO ES por dentro?

¿qué ES?

Primero un poco de teoría...

¿dónde buscar ADDOns?

// package.json

{
  "name": "ember-simple-auth-loopback-3",
  "version": "2.0.9",
  "description": "Loopback 3 support for ember-simple-auth",
  "keywords": [
    "ember-addon",
    "ember-simple-auth",
    "Loopback",
    "authentication",
    "authorization",
    "auth"
  ],
  ...
}

Primero un poco de teoría...

Paquetes NPM con la keyword "ember-addon"

?

?

?

y ahora...

¿CÓMO ES por dentro?

¿DÓNDE buscar?

¿qué ES?

Primero un poco de teoría...

¿Qué puedo hacer con un addon?

Ya lo dijo Mike North en 2015...

¿Qué puedo hacer con un addon?

Al ser una mini app, puedo proveer a la app host de elementos conocidos

Componentes
Helpers
Engines
Broccoli
Mixins
index.js

código común

1. ENCAPSULAR CÓDIGO

3. AÑADIR COMANDOS

2. MODIFICAR BUILD

const buildLiterals = require('./bin/build-literals');

module.exports = {
    name: 'build-literals',

    isDevelopingAddon() {
        return true;
    },

    postBuild(result) {
        return buildLiterals(OUTPUT_FOLDER);
    },

    includedCommands() {
        return {
            name: 'literals',

            description: 'Builds the literals files for the app and all lazy-loaded Engines.',

            availableOptions: [
                { name: 'output-path', type: 'Path',  default: 'translations/' }
            ],

            run({ outputPath }) {
                return buildLiterals(outputPath);
            }
        }
    }
};

¿Qué código sacar a un addon?

TODO EL QUE LE PUEDA SERVIR A ALGUIEN (O A MÍ) EN OTRA APLICACIÓN

AHORA COSillAS ÚTILES

HELPERS

HELPERS

{{!-- await --}}

{{#if (await model.author)}}
    {{get (await model.author) 'name'}}
{{else}}
    No author!
{{/if}}
{{!-- is-pending --}}

{{#if (is-pending promise)}}
    <img src="loading.gif" />
{{else}}
    Loaded!
{{/if}}
{{!-- is-rejected --}}

{{#unless (is-pending promise)}}
    {{#if (is-rejected promise)}}
        rejected! :(
    {{/if}}
{{/unless}}
{{!-- is-fulfilled --}}

{{#unless (is-pending promise)}}
    {{#if (is-fulfilled promise)}}
        Yay it worked!
    {{else}}
        Oh :(
    {{/if}}
{{/unless}}
{{!-- promise-all --}}

{{#if (is-pending (promise-all promise1 promise2))}}
    <img src="loading.gif"/>
{{else}}
    Loaded!
{{/if}}

HELPERS

{{!-- queue --}}

<button {{action (queue
    (action "backupData")
    (action "unsafeOperation")
    (action "restoreBackup")
)}} />
{{!-- take --}}

<h3>Top 3:</h3>
{{#each (take 3 contestants) as |contestant|}}
    {{contestant.rank}}. {{contestant.name}}
{{/each}}
{{!-- pipe --}}

<button {{action (pipe (action 'addToCart') (action 'purchase') (action 'goHome')) item}}>
  1-Click Buy
</button>
pipe
compute
toggle
optional
queue
array
map
map-by
sort-by
filter
reverse
range
join
compact
contains
append
chunk
without
shuffle
flatten
filter-by
reject-by
find-by
intersect
invoke
union
take
drop
reduce
repeat
object-at
slice
next
has-next
previous
has-previous
group-by
inc
dec

INTERNACIONALIZACIÓN

photos:
  title: "Your photos"
  banner: "You have {numPhotos, plural, =0 {no photos.} =1 {one photo.} other {# photos.}}"
<h1>{{t 'photos.title'}}</h1>

<p>{{t 'photos.banner' numPhotos=model.photos.length}}</p>
Números:

  {{format-number myNumber format='EUR'}}
  {{format-number myNumber style='currency' currency='USD'}}

Fechas:

  {{format-date myDate weekday='long' timeZone='UTC'}}

Horas:

  {{format-time myTime format='hhmmss'}}

Tiempo relativo:

  {{format-relative timestamp}} -> 3 days ago

calidad de código

addon/routes/dummy.js
  4:2  warning  Expected space or tab after '//' in comment  spaced-comment

✖ 1 problem (0 errors, 1 warning)
  0 errors and 1 warning potentially fixable with the `--fix` option.


addon/templates/dummy.hbs
  1:10  error  Non-translated string used  no-bare-strings

===== 1 Template Linting Error

TESTING

 $ ember exam -s --split=4 --parallel 

"A client-side server to help you build, test and demo your Ember app"

"Run your tests with randomization, splitting, and parallelization"

TESTING

// tests/factories/user.js
import FactoryGuy from 'ember-data-factory-guy';

FactoryGuy.define("project", {
    default: {
        title: (f) => `Project ${f.id}`
    },
    traits: {
        medium: (f) => {
            f.title = `Medium Project ${f.id}`
        },
        withUser: (f) => {
            f.user = FactoryGuy.make('user')
        }
    }
});
let project =  make('project');
project.get('title'); //=> 'Project 1'

let project2 =  make('project', 'medium');
project2.get('title'); //=> 'Medium Project 2'

let project3 =  build('project', 'withUser');
project3.get('user.name'); //=> 'User 1'

Charla que no te puedes perder:

TESTING

import a11yAudit from 'ember-a11y-testing/test-support/audit';

...

test('Some test case', async(assert) => {
    visit('/');

    await a11yAudit();

    assert.ok(true, 'no a11y errors found!');
});

RETOS frecuentes

¿Cómo acceder a la configuración de la app?

¿Cómo importar código de paquetes npm?

¿Cómo minificar el HTML que genera mi aplicación?

¿Cómo pasar parámetros de configuración a mi aplicación?

otros addons destacables

ember-decorators - Decoradores

ember-cli-fastboot - Renderizado en servidor

emberfire - Adapter con Firebase

ember-cli-deploy - Pipeline de despliegue

ember-cli-workbox - Service Workers

ember-concurrency - Gestión de tareas asíncronas

ember-simple-auth - Autenticación

liquid-fire - Animaciones

¿DUDAS?

¡GRACIAS!

Juan Antonio Gómez

shokmaster@gmail.com

Made with Slides.com