ECMAScript6 i jQuery - jak to zgrabnie połączyć?

0

Cześć,

Pytania, które chcę Wam za moment zadać, pochodząc od osoby (tj. ode mnie :), która z samym JavaScriptem miała do tej pory niewiele do czynienia, a teraz chciałaby poznać js od jej 'najnowszej' strony.

Tak więc, postanowiłem, że przygotuję sobie na początek tabelę na stronie WWW, która będzie interaktywna, tj.dodawanie/usuwanie wierszy, itd.. Z racji tego, że przyzwyczajony jestem do programowania obiektowego, to chciałem wykorzystać najnowszy standard EC6 i za jego pomocą tworzyć klasy i metody w js. Dodatkowo chciałem wykorzystywać bibliotekę jQuery do operowania na elementach DOM.

Nie zwlekając za długo zacząłem kodzić i mimo wszystko, że udało mi się coś działającego napisać, to mam pewien niedosyt i obawy w stosunku do napisanego kodu..

Po pierwsze - gdzieś przeczytałem, że EC6 nie do końca jest wspierane przez przeglądarki i należy to 'przetłumaczyć' do EC5. W tym celu użyłem strony *http:*babeljs.io/repl// (są też programy instalowane przez npm, ale jeszcze nie używałem). W międzyczasie przeczytałem też o TypeScript i prawdopodobnie na to bym się przerzucił.

Po drugie - dość szybko pojawiła się kwestia - jak 'zgrabnie' połączyć jQuery i pisane przeze mnie metody/klasy? Dla osób znających js zapewne było to oczywiste, że pojawi się problem z przesłanianiem operatora this. Ja byłem na początku trochę zmieszany, ale jak będzie widać w poniższym kodzie, rozwiązałem ten problem przy pomocy jednej z polecanych metod na StackOverflow, ale czy tak to powinno wyglądać? Nie mam pewności.

Ostatecznie kod, który napisałem jest dla mnie trochę 'potworkowaty' - ta konwersja EC6 -> EC5 + wyglądające 'dziwnie' łączenie kodu jQuery z metodami klas javascriptu i dodatkowo ta kombinacja that = this rodzą we mnie mieszane uczucia..

Cały wątek jest zapytaniem do osób mających większe doświadczenie z js i zmagających się z podobnym problemem, do tego przedstawionego powyżej.

pzdr,
Adam

// poniżej umieściłem fragmenty całego kodu
class MyTable {
  constructor(table_cls_name){
    this.table_cls_name = table_cls_name;
    // używane do numerowania wierszy
    this.counter = 0;
  }

  myself(){
    console.log(`table_cls_name: ${this.table_cls_name}!`);
  }

  addRow_EventListener(button_cls_name){
    // initialization
    var that = this;

    // addRow
    // przycisk, którym dodaje się nowe wiersze
    $(button_cls_name).on('click',function(){
      if (that.counter == 0){
        that.counter += 1;
        $(that.table_cls_name).show();
      }

      let data=`<tr class='row-id-${that.counter}'>\
<td><input type='checkbox' class='case'/></td>\
<td><span id='snum'>${that.counter}.</span></td>`;
      data +=`<td><div class='input-group date'>\
...</td></tr>`;

      $('table').append(data);
      that.counter += 1;

      $('#validfrom, #validto').datetimepicker({
        format: 'YYYY-MM-DD HH:mm:ss',
      });
    });
  }
  // zaznaczenie wszystkich wierszy
  select_all_EventListener(checkbox_cls_name){
    $(checkbox_cls_name).on('click',function(){
      $('input[class=case]:checkbox').each(function(){
        if($('input[class=check_all]:checkbox:checked').length == 0){
            $(this).prop("checked", false);
        } else {
            $(this).prop("checked", true);
        }
      });
    });
  }
  // usuwanie pojedynczego wiersza
  deleteRow_EventListner(delete_button_cls_name){
    var that = this;
    $(delete_button_cls_name).on('click', function() {
        $('.case:checkbox:checked').parents("tr").remove();
        $('.check_all').prop("checked", false);
        that.reindex();
    });
  }

  reindex(){
    //...
  }
}

var t = new PlaylistTable('.mytable');
t.addRow_EventListener('.mybutton');
0

Luźna sugestia: zamiast jquery złap się aurelii albo angulara (2)

0
  1. To co zastosowałeś z that ma swoją nazwę i jest to "self pattern" i stosuję się według konwencji nazwy self
  2. Równie dobrze możesz to napisać za pomocą klas i metod w ES5, ES6 na końcu i tak zostanie zinterpretowany jako obiekt i metody prototypowe
  3. Nie rozumiem po co tworzysz widok (tabelę) i zamiast zapamiętać referencję do obiektów następnie za pomocą jQuery wykonujesz przeszukanie na całym dokumencie
  4. Do tworzenia elementów/podpinania listenerów nie potrzebujesz jQuery, więc po co taka zależność?
1
  • ES6 a nie EC6 (jeśli byłoby to raz to rozumiem, że to literówka, ale jak za każdym razem tak piszesz, to coś jest nie halo).
adamError napisał(a)

Z racji tego, że przyzwyczajony jestem do programowania obiektowego, to chciałem wykorzystać najnowszy standard EC6 i za jego pomocą tworzyć klasy i metody w js.

należy to 'przetłumaczyć' do EC5. W tym celu użyłem strony http:babeljs.io/repl// (są też programy instalowane przez npm, ale jeszcze nie używałem). W międzyczasie przeczytałem też o TypeScript i prawdopodobnie na to bym się przerzucił.

spartanPAGE napisał(a)

Luźna sugestia: zamiast jquery złap się aurelii albo angulara (2)

No tak. Bo jeśli ktoś "z samym JavaScriptem miał do tej pory niewiele do czynienia,", to niech się łapie od razu za obiektówkę ES6, Babela, TypeScripta oraz Aurelię i Angulara naraz to jest naprawdę dobry pomysł. No znakomity ;)

Chyba nie bardzo.

Lepiej iść powoli, bo inaczej wyjdzie nie strawna zupa.

  • jak 'zgrabnie' połączyć jQuery i pisane przeze mnie metody/klasy

Poza tym - po co ci klasa/programowanie obiektowe jeśli łamiesz zasadę enkapsulacji:
$('table').append(data);

tym wywołaniem funkcji jQuery odwołujesz się do wszystkich tabel na stronie. To nie jest wcale obiektowe podejście, bo jeden obiekt może zmienić stan całej aplikacji (jeśli już godzić obiektówkę z jQuery to lepiej stosować coś jak:
$(this.container).find('table').append(data)
gdzie this.container byłby to element na stronie/ służący za pojemnik dla danego obiektu. Wtedy obiekt zmieniałby tylko swój pojemnik...

W ogóle patrzę na te kod, patrzę i nie potrafię załapać o co dokładnie tam chodzi. Twój kod jest nieczytelny. Opakowany w klasę, ale w środku chaos.

Z racji tego, że przyzwyczajony jestem do programowania obiektowego, to chciałem wykorzystać najnowszy standard EC6 i za jego pomocą tworzyć klasy i metody w js.

Żeby programować obiektowo w JS nie musisz wcale korzystać z klas. W zasadzie nie powinieneś na początku etapu. JavaScript od dawna jest językiem obiektowym, tyle, że ma prototypy zamiast klas. W ES6 owszem, pojawiły się klasy i jak najbardziej warto z nich korzystać... jak się zna już JavaScript.

A to dlatego, że ludzie którzy programowali wcześniej w innych językach (Java, C#, C++ czy innych "klasowych") mają tendencję do robienia klas totalnie wszędzie i robienia kilumetrowych piramidek dziedziczenia (bo tak się robi w Javie). Problem w tym, że w JavaScript da się uniknąć klas:

  • literały obiektowe czasem są lepsze od klas
  • czasem warto użyć dziedziczenia prototypowego (jeśli już faktycznie potrzebujemy dziedziczenia)
  • czasem programowanie na tablicach jest lepsze od obiektówki
  • czasem warto programować funkcyjnie
  • czasem warto użyć wzorca fabryki
  • czasem warto nie robić żadnych abstrakcji tylko używać wprost danych bibliotek (np. używać wprost jQuery, często to lepsze niż tworzenie abstrakcji zaciemniających kod).

JS jest językiem, w którym zarówno programowanie obiektowe, jak i programowanie obiektowe oparte na klasach często wcale nie są najskuteczniejszą metodą programowania. Owszem, są pewne sytuacje w których robienie klas jest jak najbardziej okej. W końcu jest to jeden z elementów języka i ja się bardzo cieszę, że nareszcie pojawiły się klasy w ES6 (bo przedtem i tak ludzie programowali na klasach, tyle, że je sobie symulowali w różny sposób, co nie było dobre).

Jednak wcale nie jest tak, że siadamy do JavaScriptu i programujemy obiektowo, hurra klasy, wow. Klasy jak klasy. Mają swoje zastosowanie, ale jednak trzeba wiedzieć, kiedy ich użyć, a kiedy niekoniecznie.

BTW czemu mam wrażenie, że próbujesz zrobić swoją wersję Backbone? ;) Jeśli robisz to dla czystej nauki to okej, ale jak w celach praktycznych to polecałbym odejść od jQuery na rzecz Reacta czy Angulara (tak, wyśmiałem niby naukę Angularów, ale jakby nie patrzeć praca na React, Angular czy innych tego typu rzeczach jest na dłuższą metę bardziej praktyczna niż odkrywanie koła na nowo). Co do Reacta to można szybko zacząć pracę z nim instalując ten generator od Facebooka: https://facebook.github.io/react/blog/2016/07/22/create-apps-with-no-configuration.html
w React też są klasy (chociaż i tak zalecane jest robienie komponentów na czystych funkcjach).

1

Poza tym co napisał @LukeJL to takie połączenie klas i jQuery nie ma większego sensu, opakowujesz we własny obiekt obiekt DOM, który jest opakowany w obiekt jQuery - bardziej "obiektowym" to już chyba nie można być;)

Chciałem to Ci skorygować po ludzku, ale sam koncept jest chybiony wiec nie ma co. Jak projekt jest na tyle duży, że wymaga własnych abstrakcji obiektowych to jQuery należy zastąpić wyspecjalizowaną biblioteką / frameworkiem, jeśli natomiast jest niewielki, to nie ma co zaciemniać w ten sposób kodu - na jQuery operuje się bezpośrednio.

PS
Zamiast that = this należy stosować "arrow functions" - różnią się od zwykłych funkcji anonimowych tym, że nie tworzą nowego kontekstu dla this. Możesz też użyć .bind(this) - wszystko znajdziesz w linku z komentarza pod Twoim postem.

0

Dzięki wszystkim za odpowiedzi. Przyjąłem wszystkie uwagi i będę starał się mieć je na uwadze :)

@LukeJL - Chciałbym odnieść się do Twoich słów, bo wydają się mieć sens .)

Żeby programować obiektowo w JS nie musisz wcale korzystać z klas. W zasadzie nie powinieneś na początku etapu. JavaScript od dawna jest językiem obiektowym, tyle, że ma prototypy zamiast klas. W ES6 owszem, pojawiły się klasy i jak najbardziej warto z nich korzystać... jak się zna już JavaScript.

Króto podsumowując - sugerujesz mi (jako osobie praktycznie nie znającej js), nie korzystać z tych wszystkich nowości, które powstały w ostatnich latach wokół js (klasy, TypeScript) i mimo wszystko programować w 'starym' stylu, tj. używanie prototype, bo. chcąc nie chcąc, od tego nie ucieknę, tak?

Ja na swoje 'usprawiedliwienie' mam to, że chciałem poznać i zrobić coś praktycznego przy użyciu js wykorzystując jego najlepsze?/najnowsze 'osiągnięcia' (aby poniekąd przyspieszyć sobie naukę js). Tym samym chciałem ominąć 'gorszą' stronę js (przynajmniej tak mi się wydawało..).

I mogę się domyślać, że słowa które napisał @Maciej Cąderek też podzielasz ?

--
Mały offtop związany poniekąd z tematem..

Jak to jest z tym jQuery..

Załóżmy, że mamy tabele, do której dodawane są wiersze z jakąś rozwijaną listą. I teraz dla każdej nowo dodanej listy, dodawane są ficzery przy użyciu jQuery w następujący sposób:

function fillSelects(){
      var selects = [".pl", ..]; 
      selects.forEach(function(item) {
        $(item).chosen({
          allow_single_deselect: true,
          no_results_text: 'Oops, nothing found!'
        });
      });
}

Powyższa funkcja wywoływana jest za każdym razem po dodaniu nowego wiersza. Jak widać, iteruje ona po wszystkich elementach danej klasy i uzupełnia je o nowe funkcje.

Czy jest coś złego w takim podejściu? Jestem świadomy, że pewnie niepotrzebne iteruję wszystko od początku, ale bardziej zastanawia mnie, co się dzieje w przypadku elementów, które już wcześniej miały dodane owe funkcje? Czy są one 'automatycznie' pomijane, czy może są nadpisywane te wcześniejsze funkcje?

2

Tu niestety cięzko mi powiedzieć, bo nie wiem jak działa plugin chosen (z jQuery jest o tyle problem, że ma mnóstwo pluginów, i każdy plugin ma swoje własne sposoby działania).

nie korzystać z tych wszystkich nowości, które powstały w ostatnich latach wokół js (klasy, TypeScript) i mimo wszystko programować w 'starym' stylu, tj. używanie prototype, bo. chcąc nie chcąc, od tego nie ucieknę, tak?

Nawet jak zadeklarujesz coś jako klasę, to i tak będzie to pod spodem funkcja.

wykorzystując jego najlepsze?/najnowsze 'osiągnięcia' (aby poniekąd przyspieszyć sobie naukę js).

To ma sens, tylko trzeba wiedzieć, które to osiągnięcia. Co do klas, to nie ma nic złego w ich używaniu (chociaż warto poznać normalną obiektówkę JS, prototypy etc. bo jak będziesz znał tylko klasy, to się cofniesz w rozwoju a nie rozwiniesz). Natomiast prawda jest taka, że to wcale nie klasy są największymi osiągnieciami ES6... Bardziej bym obstawiał, że byłyby to funkcje strzałkowe (arrow functions), bo ułatwiają one pisanie funkcji, bo teraz można napisać:

a => b => a + b

zamiast

function (a) {
   return function (b) {
        return a + b;
   } 
}

a że w JS większość rzeczy robi się na funkcjach, to programowanie staje się o wiele prostsze (plus wiążą this). JS staje się coraz bardziej ekspresywny.

Destructuring z ES6 również wydaje się być znacznie ciekawszym osiągnięciem niż klasy.

template strings również wydają mi się być ciekawsze.

moduły ES6 też są większym osiągnięciem niż klasy...

Wiele ludzi docenia również const/let zamiast var. Wg mnie to trochę przesadzona demonizacja składni var, ale muszę powiedzieć, że np. wczoraj użycie const mi pomogło w programowaniu, bo zgłosiło mi błąd tam gdzie potrzeba, jak niechcący przypisałem coś znowu do zmiennej zadeklarowanej jako const.

Także jest wiele innych rzeczy ciekawych w ES6 i poza (wymieniłem te, z których korzystam na codzień).

W każdym razie - nie widzę nic złego w używaniu klas (i nawet bym ich bronił w pewnych zastosowaniach), tylko moim zdaniem warto poznać najpierw tradycyjną obiektówkę JSową, bo od tego i tak się nie ucieknie. Pamiętaj, że to obiekty są podstawowymi budulcami OOP JSowego a nie klasy. JS jest językiem bardziej obiektowym niż klasowym.

No i potęga funkcji. Klasa i tak jest niczym bez swoich metod (czyli funkcji, jakby nie patrzeć). Natomiast w JS można zdziałać cuda na samych funkcjach i obiektach. Zobacz, że nawet w tym kodzie co wkleiłeś, to niby masz jakąś klasę ale w środku i tak operujesz na funkcjach - odpalasz jakieś funkcje, dajesz funkcje jako callbacki, operujesz na jakichś obiektach itp.

3
adamError napisał(a):

Króto podsumowując - sugerujesz mi (jako osobie praktycznie nie znającej js), nie korzystać z tych wszystkich nowości, które powstały w ostatnich latach wokół js (klasy, TypeScript) i mimo wszystko programować w 'starym' stylu, tj. używanie prototype, bo. chcąc nie chcąc, od tego nie ucieknę, tak?

Wg mnie powinieneś korzystać jak najbardziej, wszystkie nowe przeglądarki i Node.js 6 obsługują ES6 niemal w całości (jedyne istotne braki to obsługa natywnych modułów i TCO), aczkolwiek korzystanie z nowych abstrakcji / cukru składniowego musisz poprzedzić nauką podstawowych mechanizmów (bo nawet jak ich nie będziesz tworzył w nowym kodzie, to natkniesz się na nie wszędzie):

  1. Deklarowanie zmiennych:
  • var -> let + const
  • uwaga: naucz się jak działa var, ale w kodzie stosuj tylko const, a w razie wyraźnej potrzeby let
  1. Kolekcje:
  • obiekty i tablice -> Map + Set + WeakMap + WeakSet -> opcjonalnie: biblioteki niemutowalnych kolekcji - Immutable.js lub Mori
  1. Funkcje:
  • tradycyjny zapis -> arrow functions
  1. Literały obiektowe:
  • klasyczna konstrukcja -> zapis skrócony
  1. Dziedziczenie:
  • funkcje-konstruktory -> klasy
  • dodatkowo: Object.create(), Object.assign(), funkcje-fabryki
  1. Moduły:
  • CommonJS równie ważny jak ES6 Modules
  1. Stringi:
  • zwykła konkatenacja -> template strings
  1. Destructing:
  • tu nie ma odpowiednika w ES5 - warto się tego nauczyć szybko bo ułatwia pisanie kodu
  1. Parametry domyślne:
  • ustawianie wartości brakujących parametrów w kodzie (za pomocą if oraz ||) -> ES6 default parameters
  1. Łączenie i kopiowanie tablic:
  • Array.prototype[.slice(), .concat(), splice(), .push() itp] -> spread operator
  1. Funkcje, ze zmienną liczbą parametrów:
  • pseudotablica arguments => rest operator
  1. Iteracja:
  • for, while, for .. in -> Array.prototype.forEach(), Array.prototype.map(), Array.prototype.filter(), Array.prototype.reduce() -> iteratory i for .. of
  1. Asynchroniczność:
  • callbacki -> promises -> generatory (+ np. Co: https://github.com/tj/co ) -> async-await
  • uwaga: generatory oczywiście nie służą tylko do upraszczania asynchronicznych operacji

Powyższa kolejność jest przypadkowa.

Ogólnie moje zdanie jest takie: naucz się wszystkich konstrukcji, zaczynając od tych starszych / bazowych, ale korzystaj w swoim kodzie raczej z tych nowych (o ile nie musisz czegoś poświęcić dla wydajności).

Na czas nauki nie ma znaczenia to, że nie zadziała to na każdej przeglądarce - gdy dojdziesz do momentu pisania produkcyjnego kodu to skonfigurowanie i odpalenie Webpacka czy co tam będzie modne nie będzie dla Ciebie żadnym problemem (to dosyć łatwe).

No i polecam Style Guide po raz kolejny: https://github.com/airbnb/javascript

1 użytkowników online, w tym zalogowanych: 0, gości: 1