Základy programovania

v JavaScripte

Milan Herda, 03/2020

Lekcia 7

Čo nás čaká

  • opakovanie (objekt)
  • objekt v praxi: ovládanie Karla pomocou tlačidiel

Typy premenných

Primitívne

  • boolean
  • číslo
  • reťazec
  • null
  • undefined

Zložené

  • objekt
  • pole
  • funkcia

Objekt

Typ zloženej premennej.

Môže obsahovať skupinu primitívnych

a/alebo zložených hodnôt.

Každej položke objektu hovoríme

vlastnosť

Ak je vlastnosť funkciou,

hovoríme jej

metóda

const prazdnyObjekt = {};

const fero = {
    vek: 45,
    vyska: 183,
    adresa: {
        ulica: 'Farská 42',
        obec: 'Nitra',
    },
    zmenVek: function (novyVek) {
        this.vek = vek;
    },
};

Úloha:

Chceme na stránku pridať tlačidlá, pomocou ktorých budeme vedieť:

Chceme mať na stránke sekciu, v ktorej budeme zobrazovať, na ktorú svetovú stranu je Karel práve otočený.

Keď sa Karel otočí, info na stránke sa zmení.

  • Karla otočiť doľava
  • otočiť ho doprava
  • pohnúť o jeden krok dopredu

Želaný stav

Algoritmizácia

Rozložíme problém na časti

  • urobíme HTML markup pre tlačidlá
  • urobíme HTML markup pre info o otočení
  • spojazdníme tlačidlá
  • rozhodneme sa, ako budeme uchovávať smer
  • pri otočení musíme upraviť smer
  • po každom otočení upravíme zobrazenú info o smere

Riešenie: HTML markup pre tlačidlá

<section class="section">
    <div class="buttons has-addons">
        <button class="button is-warning" id="btn-left">
            Doľava
        </button>
        <button class="button is-success" id="btn-step">
            Krok dopredu
        </button>
        <button class="button is-warning" id="btn-right">
            Doprava
        </button>
    </div>
</section>

Riešenie: HTML markup pre info o otočení

<section class="section">
    <!-- tlačidlá -->
    <div class="level">
        <div class="level-left">
            <div class="level-item has-text-centered">
                <div>
                    <p class="heading">Otočený na</p>
                    <p class="title" id="direction">sever</p>
                </div>
            </div>
        </div>
    </div>
</section>

Riešenie: spojazdníme tlačidlá

<script>
    const btnStep = document.querySelector('#btn-step');
    btnStep.addEventListener('click', function (event) {
        if (!jePredTebouStena()) {
            krokDopredu();  
        }
    });
    
    const btnLeft = document.querySelector('#btn-left');
    btnLeft.addEventListener('click', function (event) {
        otocDolava();
    });
  
    const btnRight = document.querySelector('#btn-right');
    btnRight.addEventListener('click', function (event) {
        otocDoprava();
     });
</script>

Nechýba nám event.preventDefault()?

Tieto tlačidlá nie sú súčasťou formuláru a tak nemajú inú defaultnú akciu, ktorú by sme potrebovali zrušiť.

Nie.

"Rozhodneme sa, ako budeme uchovávať smer"

Smer, kam je Karel otočený, je vlastnosťou Karla.

Karel môže mať potenciálne viac vlastností (riadok, stĺpec...).

Karel bude objekt a smer bude jednou z jeho vlastností.

Riešenie: uchovávanie smeru

const SEVER  = 'sever';
const JUH    = 'juh';
const VYCHOD = 'východ';
const ZAPAD  = 'západ';

const karel = {
    smer: SEVER,
};

Ale veď to vyzerá, ako objekt karel z minulej lekcie.

Áno, nie je to náhoda :) Použijeme presne tento hotový objekt karel, aj s jeho metódami

Riešenie: po otočení upravíme smer

const btnLeft = document.querySelector('#btn-left');
btnLeft.addEventListener('click', function (event) {
    otocDolava();
    karel.smer = akyJeDalsiSmerVlavo(karel.smer);
});
  
const btnRight = document.querySelector('#btn-right');
btnRight.addEventListener('click', function (event) {
    otocDoprava();
    karel.smer = akyJeDalsiSmerVpravo(karel.smer);
});
  • najskôr otočíme Karla
  • potom upravíme Karlov smer
const btnLeft = document.querySelector('#btn-left');
btnLeft.addEventListener('click', function (event) {
    otocDolava();
    karel.vlavoVbok();
});
  
const btnRight = document.querySelector('#btn-right');
btnRight.addEventListener('click', function (event) {
    otocDoprava();
    karel.vpravoVbok();
});

Riešenie: po otočení upravíme smer

otocDolava();
karel.vlavoVbok();
  • najskôr otočíme Karla
  • potom upravíme Karlov smer
karel.vlavoVbok();

Riešenie: po otočení upravíme smer

const SEVER  = 'sever';
const JUH    = 'juh';
const VYCHOD = 'východ';
const ZAPAD  = 'západ';

const karel = {
    smer: SEVER,
  
    vlavoVbok: function () {
        otocDolava();
        this.smer = akyJeDalsiSmerVlavo(this.smer);
    },
  
    vpravoVbok: function () {
        otocDoprava();
        this.smer = akyJeDalsiSmerVpravo(this.smer);
    },
};

Riešenie: po otočení upravíme smer

const btnLeft = document.querySelector('#btn-left');
btnLeft.addEventListener('click', function () {
    karel.vlavoVbok();
});
  
const btnRight = document.querySelector('#btn-right');
btnRight.addEventListener('click', function () {
    karel.vpravoVbok();
});

Riešenie: po každom otočení upravíme zobrazenú info o smere

const infoDirection = document.querySelecter('#direction');

const btnLeft = document.querySelector('#btn-left');
btnLeft.addEventListener('click', function () {
    karel.dolava();
    infoDirection.innerHTML = karel.smer;
});
  
const btnRight = document.querySelector('#btn-right');
btnRight.addEventListener('click', function () {
    karel.doprava();
    infoDirection.innerHTML = karel.smer;
});

Má riešenie nedostatky?

  1. Kráčanie dopredu hýbe Karlom, takže by tiež malo byť súčasťou objektu karel.

Má riešenie nedostatky?

  1. Kráčanie dopredu hýbe Karlom, takže by tiež malo byť súčasťou objektu karel.
const btnStep = document.querySelector('#btn-step');
btnStep.addEventListener('click', function (event) {
    karel.krok();
});
const karel = {
    // ... zvyšný doterajsí kód ...
    
    krok: function () {
        if (!jePredTebouStena()) {
            krokDopredu();
        }
    }
};

Má riešenie nedostatky?

  1. Kráčanie dopredu hýbe Karlom, takže by tiež malo byť súčasťou objektu karel.
  2. Vždy po zavolaní karel.vlavoVbok() musíme hneď písať aj príkaz pre úpravu informácie.

 

Ak zavoláme karel.vlavoVbok() a zabudneme druhý príkaz, tak sa nám rozíde zobrazená informácia s reálnym stavom

Riešenie: Karlovi povieme, že vždy po otočení chceme updatnúť zobrazené info

const karel = {
    // ...
    vlavoVbok: function () {
        otocDolava();
        this.smer = akyJeDalsiSmerVlavo(this.smer);
        
        document.querySelecter('#direction').innerHTML = this.smer;
    },
};

Takto je ale Karel pevne previazaný so stránkou, kde sa používa a vyžaduje, aby na tej stránke bol element #direction.

To nie je dobré, lebo nám to neumožňuje použiť Karla na inej stránke, kde taký element nie je.

Pokus o riešenie: parametrizácia

Hlavný problém je pevné previazanie Karla na element so selektorom "#direction"

Ak tento selektor nebude v Karlovi pevne uvedený, ale príde nejako zvonka, tak je problém vyriešený.

Pokus o riešenie: parametrizácia

const karel = {
    // ...
    vlavoVbok: function (selektor) {
        otocDolava();
        this.smer = akyJeDalsiSmerVlavo(this.smer);
        
        document.querySelecter(selektor).innerHTML = this.smer;
    },
};

Toto nie je dobré riešenie, lebo nás núti do každého zavolania karel.vlavoVbok() odovzdať selektor

karel.vlavoVbok('#direction');

Pokus o riešenie: parametrizácia cez property

const karel = {
    // ...
    selektorPreSmer: null,
    
    vlavoVbok: function () {
        otocDolava();
        this.smer = akyJeDalsiSmerVlavo(this.smer);
        
        document.querySelecter(this.selektorPreSmer).innerHTML = this.smer;
    },
};
karel.selektorPreSmer = '#direction';

const btnLeft = document.querySelector('#btn-left');
btnLeft.addEventListener('click', function () {
    karel.vlavoVbok();
});

Vznikol nám však duplicitný kód

const karel = {
    // ...
    selektorPreSmer: null,
    
    vlavoVbok: function () {
        otocDolava();
        this.smer = akyJeDalsiSmerVlavo(this.smer);
       
        // pridáme kontrolu, či bol selektor nastavený
        if (this.selektorPreSmer !== null) {
            document.querySelecter(this.selektorPreSmer).innerHTML = this.smer;
        }
    },
    vpravoVbok: function () {
        otocDoprava();
        this.smer = akyJeDalsiSmerVpravo(this.smer);
       
        // tu máme duplicitu
        if (this.selektorPreSmer !== null) {
            document.querySelecter(this.selektorPreSmer).innerHTML = this.smer;
        }
    },
};

Vytiahneme duplicitu do samostatnej metódy

const karel = {
    // ...
    selektorPreSmer: null,
  
    dajVedietOZmeneSmeru: function () {
        if (this.selektorPreSmer !== null) {
            document.querySelecter(this.selektorPreSmer).innerHTML = this.smer;
        }
    },
    
    vlavoVbok: function () {
        otocDolava();
        this.smer = akyJeDalsiSmerVlavo(this.smer);
        this.dajVedietOZmeneZmeneSmeru();
    },
    vpravoVbok: function () {
        otocDoprava();
        this.smer = akyJeDalsiSmerVpravo(this.smer);
        this.dajVedietOZmeneSmeru();
    },
};

Bonus: Skúsme Karla úplne odtieniť od HTML

  • nechcem, aby bol Karel naviazaný na použitie vo webstránke
  • musím sa preto zbaviť volania document.querySelector
  • Karel musí kódu, ktorý ho používa, ponúknuť možnosť, ako sa "zavesiť" na udalosť zmeny smeru
  • jednoduchý spôsob je ponúknuť prázdnu metódu, ktorú vonkajší svet nastaví podľa svojej potreby
const karel = {
    // ...
    // Už nepotrebujeme selektor.
    // Namiesto neho ponúkneme "udalosť" na zavesenie.
    // Budeme očakávať, že to je funkcia.
    poZmeneSmeru: null,
  
    dajVedietOZmeneSmeru: function () {
        if (this.poZmeneSmeru !== null) {
            this.poZmeneSmeru(this.smer);
        }
    },
    
    vlavoVbok: function () {
        otocDolava();
        this.smer = akyJeDalsiSmerVlavo(this.smer);
        this.dajVedietOZmeneZmeneSmeru();
    },
    vpravoVbok: function () {
        otocDoprava();
        this.smer = akyJeDalsiSmerVpravo(this.smer);
        this.dajVedietOZmeneSmeru();
    },
};
<script>
    karel.poZmeneSmeru = function (novySmer) {
        const infoSmer = document.querySelector('#direction');
        infoSmer.innerHTML = novySmer;
    };

    const btnStep = document.querySelector('#btn-step');
    btnStep.addEventListener('click', function (event) {
        karel.krok();
    });
  
    const btnLeft = document.querySelector('#btn-left');
    btnLeft.addEventListener('click', function (event) {
        karel.dolava();
    });

    const btnRight = document.querySelector('#btn-right');
    btnRight.addEventListener('click', function (event) {
        karel.doprava();
    });
</script>

Ďakujem za pozornosť