Solucionado (ver solução)
Solucionado
(ver solução)
16
respostas

Curiosidade sobre Angular

Apenas por curiosidade, se eu quisesse imitar ainda mais o angular como:

Realizado em curso:

<button class="btn btn-primary text-center" onclick="negociacaoController.importarNegociacoes()" type="button">
    Importar Negociações
</button>

Trocar para:

<button class="btn btn-primary text-center" alura-controller="NegociacaoController" alura-click="importarNegociacoes()" type="button">
    Importar Negociações
</button>

Assim evitando a necessidade de instanciar o controller. É algo muito complexo de se fazer ?

16 respostas

Eu entendi o que você quer fazer, dá para fazer sim. Implementei aqui rapidinho uma solução prova de conceito:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Negociações</title>
    <link rel="stylesheet" href="css/bootstrap.css">
    <link rel="stylesheet" href="css/bootstrap-theme.css">
    <script src="js/app/polyfill/es6.js"></script>

</head>
<body class="container" data-controller="NegociacaoController">

    <!-- código omitido -->

   <!-- scripts anteriores omitidos -->
    <script>
        document.addEventListener('DOMContentLoaded', function() {

            function colocaPrimeiraLetraEmMinusculo(string) {
                        return string.charAt(0).toLowerCase() + string.slice(1);
            }

            let nodeList = document.querySelectorAll('[data-controller]');

            Reflect.apply(Array.prototype.forEach, nodeList, [function(node) {
                    let claz = node.dataset.controller;

                    let clazAsString = window.classes[claz].toString();

                    window[colocaPrimeiraLetraEmMinusculo(claz)] = new window.classes[claz]();
            });     
        });   
    </script>

</body>
</html>

Contudo, isso não é suficiente. Quando criar suas classes, você não pode tê-las no escopo global, caso contrário não conseguirá instanciá-las dimanicamente. Aqui, defini a convenção de adicionar as classes window.classes.

Vejamos como fica a declaração de `NegociacaoController':

(function(classes) {

    class NegociacaoController {

        // código omitido'
    }

    classes.NegociacaoController = NegociacaoController;

})(window.classes || (window.classes = {}));

Agora você pode filosofar um pouco sobre o código que foi escrito aí.Hehehe. Eu não fiz isso no treinamento, porque nos módulos futuros eu pretendo usar um sistema de módulo, sendo assim, essa solução é muito custosa.

A ideia do treinamento era ensinar ES6, o mini framework só nasceu porque se coadunava para explicar vários recursos do ES6. Mas eu entendo que você é um rapaz curioso. Com certeza, é sempre bom saber mais.

Fechou Matheus?

Alterei o código para usar Reflect.apply do ES6. Assim fica mais da hora!

Putz, posso simplificar ainda mais. O problema é que NodeList não possui forEach porque não é um Array e sim um NodeList. Daí, uso o spread operador para transformar o NodeList em um array:

    <script>
        document.addEventListener('DOMContentLoaded', function() {

            function colocaPrimeiraLetraEmMinusculo(string) {
                        return string.charAt(0).toLowerCase() + string.slice(1);
            }

            [...document.querySelectorAll('[data-controller]')].forEach(function(node) {
                    let claz = node.dataset.controller;

                    let clazAsString = window.classes[claz].toString();

                    window[colocaPrimeiraLetraEmMinusculo(claz)] = new window.classes[claz]();
            });     
        });   
    </script>

Só lembrando que essa solução instancia todos os controllers da sua página.

solução!

Por fim, você pode criar aluraframe/client/app/helpers/boot.js:

(function(doc, win) {

    function colocaPrimeiraLetraEmMinusculo(string) {
         return string.charAt(0).toLowerCase() + string.slice(1);
    }

    doc.addEventListener('DOMContentLoaded', function() {

        [...doc.querySelectorAll('[data-controller]')].forEach(function(node) {
                let claz = node.dataset.controller;

                let clazAsString = win.classes[claz].toString();

                win[colocaPrimeiraLetraEmMinusculo(claz)] = new win.classes[claz]();
        });     
    });   

})(document, window);

Com isso, basta você importar boot.js em sua página index.html, não importa que posição.

Obrigado Flávio, é meio complexo, vou estudar o código e tentar implementa-lo em TypeScript(estou trocando meus códigos javascript), ficarei ancioso pelo terceiro módulo do curso.

Flavio estudando e implementando o código, em nenhum momento vi onde você associou a função alura-click="importarNegociacoes()" com o controller.

Você tem que escrever o nome do controller na frente. Para fazer igual Angular, você tem que emular shadow dom.

Aliás, isso não é ruim, é excelente fazer isso, porque dá o mesmo efeito do controlerAs do Angular deixando claro onde você esta chamando o método.

Cara, mas o que eu escrevi não tem nada de sofisticado. É javascript puro...

Só isso aqui pode pode confundir:

[...doc.querySelectorAll('[data-controller]')].

Todo NodeList, apesar de ser acessado por índice, não é um array e não podemos fazer forEach. Eu inventei essa estratégia ai misturando spread operator para converter o NodeList em um array para eu poder fazer forEach. For não existe mais, aliás, o Jaffar Russain deu uma palestra com o nome The End of Loop. Eu poderia ter usado um for, mas não rola.

Beleza? Fechou?

Fechou, mas o angular não precisar repetir o nome do controller no click:

Angular:

<button alura-click="funcao()">

O meu tive que fazer:

<button onclick="NomeController.funcao()">

Cara, isso que você esta seguindo cegamente do Angular é uma má prática. Se você tiver 3 controllers aninhados, funcao() é de qual controller? Foi por isso que nas versões mais atuais do Angular você tem controller as.

Contudo, se você quiser repetir o erro das primeiras versões do Angular, terá que emular, como disse, shadow DOM para esconder a chamada do nome do controller.

Beleza, deu certo aqui Flávio, muito obrigado.