Explicación de cómo usar v-model en nuestros componentes de Vue.js

Después de mucho buscar por fin he encontrado una explicación decente de cómo usar v-model en nuestros componentes de Vue.js, especialmente cuando son componentes complejos que tienen como modelo un objeto:

Aquí hay otra buena explicación, que además aclara algo que me tuvo rascándome la cabeza un buen rato: los datos de un componente se actualizaban solos, sin necesidad de emitir yo el evento input desde el código. Como indican en el artículo:

Note: In certain situations you might notice that parent data is updated without any input events being emitted from the child. In these cases, the value prop is a deep object, and the changes are automatically reflected in the parent component. When passing a primitive value, however, we would need to emit the input event from the child to update data on the parent.

Muy recomendables los dos artículos, quizá en el segundo lo explican incluso mejor.

Desklet de Cinnamon para movimientos de empresa del Banco Sabadell

Como ya conté en la entrada API del Banco Sabadell quería ponerme en el escritorio un desklet que me mostrara los últimos movimientos de la cuenta de la empresa, pero se conoce que el Banco Sabadell piensa que no tiene por qué permitirme acceder a mis propios datos.

Así que al no poder usar la API hay que hacerlo a lo bruto, simulando un acceso web normal. Antes esto era un poco más latoso, pero desde que existe puppeteer está chupado. Adjunto el código del script que obtiene los datos y los dos ficheros del desklet.

El script de Node para obtener los datos:

const puppeteer = require("puppeteer");
const fs = require("fs");
const path = require('path');

(async () =>
{
	const browser = await puppeteer.launch({/*headless: false, devtools: true, slowMo:500*/});
	const page = await browser.newPage();

	// Log in	
	await page.goto("https://www.bancsabadell.com/txempbs/default.bs");
	await page.goto("https://www.bancsabadell.com/txempbs/default.bs");
	await page.waitFor("input[name='userNIF']");
	await page.$eval("input[name='userNIF']", el => el.value = 'DNI_DE_ACCESO');
	await page.$eval("input[name='pinNIF']", el => el.value = 'PIN_DE_ACCESO');
	await page.$eval("button[name='s1']", f => f.click());

	await page.waitFor("a.enlaceIzquierda2Nv");

	// Save cookies
	const cookiesObject = await page.cookies();
	
	for (let cookie of cookiesObject)
            await page.setCookie(cookie);

  await page.goto("https://www.bancsabadell.com/txempbs/CUExtractOperationsQueryNew.init.bs");
  await page.waitFor("div.cuenta-item");
	await page.$eval("div.cuenta-item", f => f.click());

	await page.waitFor("#sm_modules_container2");

	const data = await page.evaluate(() => {
    const tds = Array.from(document.querySelectorAll('table.sorted tr'))
    return tds.map(td => td.innerText);
  });

  const filename = path.join(__dirname, 'output.csv');
  fs.writeFileSync(filename, data.join("\n"));

	await page.waitFor(1000);
	await browser.close();
})();

El fichero desklet.js:


const Desklet = imports.ui.desklet;
const St = imports.gi.St;
const Cinnamon = imports.gi.Cinnamon;
const GLib = imports.gi.GLib;

function HelloDesklet(metadata, desklet_id) {
    this._init(metadata, desklet_id);
}

HelloDesklet.prototype = {
    __proto__: Desklet.Desklet.prototype,

    _init: function(metadata, desklet_id) {
        Desklet.Desklet.prototype._init.call(this, metadata, desklet_id);
        GLib.spawn_command_line_sync("node /home/koas/bin/bankScraper/sabadell.js");
        this.setupUI();
    },

    setupUI: function() {
        // main container for the desklet
        this.window = new St.Bin();
        this.text = new St.Label();
        this.text.set_text(Cinnamon.get_file_contents_utf8_sync("/home/koas/bin/bankScraper/output.csv"));
        
        this.window.add_actor(this.text);
        this.setContent(this.window);
    }

}

function main(metadata, desklet_id) {
    return new HelloDesklet(metadata, desklet_id);
}

El fichero metadata.json:


{
    "dangerous": false, 
    "description": "Muestra los \u00faltimos movimientos de una empresa del Banco Sabadell", 
    "prevent-decorations": false, 
    "uuid": "bancoSabadell@koas.dev", 
    "name": "\u00daltimos movimientos Banco Sabadell Empresas"
}