• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Jak organizowac kod?

Object Storage Arubacloud
+4 głosów
475 wizyt
pytanie zadane 24 września 2017 w JavaScript przez Alex.Ironside Stary wyjadacz (14,900 p.)

Witam.

Problem polega na tym ze moge pisac w js, znam skladnie i mniej wiecej wiem jak go uzywac, ale tragicznie sie robi gdy przychodzi do organizacji kodu. Wydaje mi sie ze to przez to ze w js jest duzo eventow i asynchronicznego kodu. Robie teraz projekt do szkoly i mam taki kod.

console.log('works');
let games = [
  {
    'publisher': 'Namco',
    'avatar': 'https://archive.org/services/img/msdos_Pac-Man_1983',
    'title': 'Pac-Man',
    'desc': 'Pac-Man stars a little, yellow dot-muncher who works his way around to clear a maze of the dots.',
    'publishedDate': '1983',
    'link': 'https://archive.org/embed/msdos_Pac-Man_1983'
  },
  {
    'publisher': 'Broderbund',
    'avatar': 'https://archive.org/services/img/msdos_Where_in_the_World_is_Carmen_Sandiego_1985',
    'title': 'Where in the World is Carmen Sandiego',
    'desc': 'Capture the thief that stole the artifact using clues dealing with your knowledge of geography.',
    'publishedDate': '1985',
    'link': 'https://archive.org/embed/msdos_Where_in_the_World_is_Carmen_Sandiego_1985'
  },
  {
    'publisher': 'Ingenuity',
    'avatar': 'https://archive.org/services/img/msdos_Crosscountry_Canada_1991',
    'title': 'Crosscountry Canada',
    'desc': 'Drive an 18-wheel truck picking up and delivering a variety of commodities with typed-in commands.',
    'publishedDate': '1991',
    'link': 'https://archive.org/embed/msdos_Crosscountry_Canada_1991'
  }
]

function renderLeftElement(game) {
  let snippet = `
  <div class="email-item pure-g">
    <div class="pure-u">
      <img alt="${game.title == '' || game.title == undefined || game.title == null ? '' : game.title + "'s avatar"}"
        class="email-avatar" src="${game.avatar == '' || game.avatar == undefined || game.avatar == null ? '' : game.avatar}"
        width="64" height="64">
    </div>
    <div class="pure-u-3-4">
      <h5 class="email-name">
        ${game.publisher == '' || game.publisher == undefined || game.publisher == null ? '' : game.publisher} 
        ${game.publishedDate == '' || game.publishedDate == undefined || game.publishedDate == null ? '' : game.publishedDate}
      </h5>
      <h4 class="email-subject">${game.title == '' || game.title == undefined || game.title == null ? '' : game.title}</h4>
      <p class="email-desc">${game.desc == '' || game.desc == undefined || game.desc == null ? '' : game.desc}</p>
    </div>
  </div>`
  return snippet;
}

function renderContentSnippet(game) {
  let snippet = `
  <div class="email-content">
    <div class="email-content-header pure-g">
      <div class="pure-u-1-2">
        <h1 class="email-content-title">${game.title == '' || game.title == undefined || game.title == null ? '' : game.title}</h1>
        <p class="email-content-subtitle">
            ${game.publisher == '' || game.publisher == undefined || game.publisher == null ? '' : 'Published by: ' + game.publisher} 
            ${game.publishedDate == '' || game.publishedDate == undefined || game.publishedDate == null ? '' : game.publishedDate}
        </p>
      </div>

      <div class="email-content-controls pure-u-1-2">
        <button class="secondary-button pure-button">Delete</button>
        <button class="secondary-button pure-button">Archive</button>
        <button class="secondary-button pure-button">Unread</button>
      </div>
    </div>

    <div style="height: 100%;" class="email-content-body">
      <iframe src="${game.link == '' || game.link == undefined || game.link == null ? '' : game.link}"
        width="720px" height="450px"
        frameborder="0"
        webkitallowfullscreen="true"
        mozallowfullscreen="true"
        allowfullscreen>
      </iframe>
    </div>
  </div>`
  return snippet;
}

function remove(id) {
  let elem = document.querySelector(id);
  return elem.parentNode.removeChild(elem);
}

function renderRightContent(snippet) {
  document.querySelector('.rightSidebar').insertAdjacentHTML('beforeend', snippet);
}

let elementSelected = { id: 0 };

function removeClass(leftElems, prevElem) {
  leftElems[prevElem].classList.remove('email-item-selected');
}

function setClass(leftElems, currElem) {
  leftElems[currElem].classList.add('email-item-selected');
  return currElem;
}

function resetEventListeners(leftElements,rightContent){
  for (let i = 0; i < leftElements.length; i++) {
    leftElements[i].addEventListener('click', () => {
      remove('.email-content');
      renderRightContent(rightContent[i]);
      removeClass(leftElements, elementSelected.id);
      elementSelected.id = setClass(leftElements, i);
      // Can't believe this actually works... Recurrsion is beautiful
    })
  }
}

function renderLeftContent() {
  let leftItems = games.map(game => (renderLeftElement(game))).join('');
  document.querySelector('.leftSidebar').insertAdjacentHTML('beforeend', leftItems);
}

let form = `
  <div class="email-content" style="width:90%;margin: auto; margin-top: 20px;">
    <form class="form-horizontal">
      <div class="form-group row">
        <label class="col-md-4" for='publisher'>Publisher</label>
        <input class="col-md-8" type="text" id="publisher">
      </div>

      <div class="form-group row">
        <label class="col-md-4" for='avatar'>Avatar link</label>
        <input class="col-md-8" type="text" id="avatar">
      </div>

      <div class="form-group row">
        <label class="col-md-4" for="title">Title</label>
        <input class="col-md-8" type="text" id="title">
      </div>

      <div class="form-group row">
        <label class="col-md-4" for='desc'>Description</label>
        <input class="col-md-8" type="text" id="desc">
      </div>

      <div class="form-group row">
        <label class="col-md-4" for='publishedDate'>Published date:</label>
        <input class="col-md-8" type="date" id="publishedDate">
      </div>

      <div class="form-group row">
        <label class="col-md-4" for='link'>Link</label>
        <input class="col-md-8" type="text" id="link">
      </div>
      <div class="row">
        <button type="submit" id="submit-button" class="col-md-offset-3 btn btn-default">Add to library</button>
      </div>
    </form>
  </div>
`;


document.addEventListener('DOMContentLoaded', () => {
  renderLeftContent();
  let rightContent = Array.from(games.map(game => renderContentSnippet(game)));
  renderRightContent(rightContent[0]);
  // We land on the first game in the array
  let leftElements = Array.from(document.querySelectorAll('.email-item'));
  // console.log(leftElements);
  elementSelected.id = setClass(leftElements, 0);
  resetEventListeners(leftElements,rightContent);
});

let addNewItemButton = document.querySelector('#addNewItem');
addNewItemButton.addEventListener('click', () => {
  remove('.email-content');
  document.querySelector('.rightSidebar').insertAdjacentHTML('beforeend', form);
  let submitButton = document.querySelector('#submit-button');
  submitButton.addEventListener('click', (e) => {
    e.preventDefault();
    let input = Array.from(document.querySelectorAll('input'));
    games.unshift(
      {
        publisher: input[0],
        avatar: [1],
        title: input[2],
        desc: input[3],
        publishedDate: input[4],
        link: input[5]
      });
    let leftElements = Array.from(document.querySelectorAll('.email-item'));
    for (let i = 0; i < leftElements.length; i++) {
      remove('.email-item');
    }
    renderLeftContent();
    resetEventListeners(leftElements);
  })
})


/*
for (let i = 0; i < leftElements.length; i++) {
  leftElements[i].addEventListener('click', () => {
    //Uncheck the last one, check the new one.
    for (let j = 0; j < leftElements.length; j++) {
      leftElements[j].classList.remove('email-item-selected');
    }
    leftElements[i].classList.add('email-item-selected');
  })
}
*/

Jest to kod dlugi, ale jak widzicie wiekszosc jest wrzucona w funkcje, ale mimo wszystko nie moge sie w tym odnalezc. Jak Wy organizujecie kod? Moglby ktos mi pokazac na tym kodzie? Nie zeby wszystko za mnie zrobil ale wyjasnil jak by to szlo. Co moge zrobic zeby przestac sie gubic w tym i kazdym innym kodzie javaScript?

komentarz 25 września 2017 przez Mikołaj Kawczynski Dyskutant (9,160 p.)
Ja bym wrzucił każdą funkcję do innego pliku, ewentualnie kilka powiązanych ze sobą do jednego a na końcu wersje produkcyjną tylko bym skompresował.

5 odpowiedzi

+2 głosów
odpowiedź 24 września 2017 przez rafal.budzis Szeryf (85,260 p.)
edycja 24 września 2017 przez HaKIM

Kod jest bardzo okej :) nie ma w nim jakiejś tragedii kilka drobiazgów można poprawić.

1. W funkcjach renderContentSnippet, renderLeftElement tworzysz zmienną let snippet tylko po to by ją zwrócić. 

Możesz zamienić funkcje aby po prostu zwracały ten tekst 

function renderContentSnippet(game) {
  return `<div ....

2. Nazewnictwo funkcji też dużo daje. Np jest funkcja remove musiałem ją całą przeanalizować żeby dowiedzieć się co robi. Gdyby nazywała się removeBySelector lub przynajmniej removeElement mówiła by dużo więcej.

3. Aby poradzić sobie z funkcjami asynchronicznymi można by rozbijać je na mniejsze funkcje. Przeanalizujmy ten fragment 

submitButton.addEventListener('click', (e) => {
    e.preventDefault();
    let input = Array.from(document.querySelectorAll('input'));
    games.unshift(
      {
        publisher: input[0],
        avatar: [1],
        title: input[2],
        desc: input[3],
        publishedDate: input[4],
        link: input[5]
      });
    let leftElements = Array.from(document.querySelectorAll('.email-item'));
    for (let i = 0; i < leftElements.length; i++) {
      remove('.email-item');
    }
    renderLeftContent();
    resetEventListeners(leftElements);
  });

 

funkcja jest rozbudowana i to trzeba przyznać. Najlepszym sposobem jest podzielenie tego na mniejsze funkcje. W funkcji robisz 4 rzeczy : dodajesz gre, usuwasz email-itemy, renderujesz i resetujesz listenery. Po przeróbkach funkcja powinna wyglądać tak. 

submitButton.addEventListener('click', (e) => {
    e.preventDefault();
    addNewGamesFromInputs();
    removeEmailElements();
    renderLeftContent();
    resetEventListeners(leftElements);
});

4. Spójność ! masz kilka funkcji render które odpowiadają za trzymanie i renderowanie HTMLa. warto się trzymać jednej koncepcji i zmienną form z HTMLem wrzucił bym do funkcji renderForm aby trzymać sie jednej koncepcji. 

5. Warto poczynić jakieś kroki w strone obiektowości abyś miał lepszy podział aplikacji. Na pewno jeden osobny obiekt zrobił bym na szablony HTMLa oraz wrzucił bym je do osobnego pliku. 

6. Może tutaj jeszcze znajdziesz coś wartościowego : https://github.com/ryanmcdermott/clean-code-javascript

komentarz 24 września 2017 przez Alex.Ironside Stary wyjadacz (14,900 p.)
Dobra lekturka. Dzieki!
+1 głos
odpowiedź 24 września 2017 przez ProgramistaStepek Nałogowiec (27,020 p.)
Przy większych projektach(a nawet niektórych mniejszych) warto rozdzielać w kod na wiele plików. ES6 wprowadza moduły co jeszcze bardziej ułatwia sprawę. Zauważ, że w swoim kodzie JS masz za dużo HTML. Postaraj się generować niektóre rzeczy metodą document.createElement(); i pętlą for. Organizacja kodu dotyczy również nazw. Nazwa zmiennej renderLeftContent jest błędna, ponieważ podczas np. zmiany layoutu strony twój 'leftContent' może znaleźć się po prawej stronie a wtedy będzie jeszcze ciężej odnaleźć się w kodzie.
komentarz 24 września 2017 przez Alex.Ironside Stary wyjadacz (14,900 p.)
ok. Ma sens. Myslalem wlasnie nad rozbiciem eventow i moze stworzenie jakiegos obiektu na 'delegaty' czy cos takiego
+1 głos
odpowiedź 24 września 2017 przez Ehlert Ekspert (212,670 p.)
Jeśli chcesz to lepiej organizować, a jednocześnie trzymać się stylu, który masz wyżej to przerzuć się na ES6 i React.
komentarz 24 września 2017 przez Comandeer Guru (601,110 p.)
Po co React?
komentarz 24 września 2017 przez Ehlert Ekspert (212,670 p.)
Skoro upodobał sobie html w kodzie js i do tego separację logiki, to wydaje mi się, że React to dobry pomysł.
komentarz 24 września 2017 przez Alex.Ironside Stary wyjadacz (14,900 p.)
Nieszczegolnie ja to sobie upodobalem tylko profesor nam to tak zorganizowal. Nie wiem nawet jak by to inaczej zrobic
+1 głos
odpowiedź 24 września 2017 przez Comandeer Guru (601,110 p.)
Czemu trzymasz kod HTML w kodzie JS? Najlepiej byłoby to przerzucić na jakiś system szablonów.

Ogólnie wychodzę z założenia, że jeśli czegoś nie trzeba generować w JS, to tego w JS nie generuję. Serwer jest w tym szybszy.

Dodatkowo warto duże funkcje podzielić na mniejsze, z których każda robiłaby tylko jedno, małe zadanie.
komentarz 26 września 2017 przez Alex.Ironside Stary wyjadacz (14,900 p.)
Niestety to jest zadanie na czysto js. Jak generowac kod html przy dodatkowym jego kopiowaniu? Pasek po lewej (leftItems o ile dobrze pamietam) moze miec tyle pol ile uzytkownik tego zapragnie, w tym problem
komentarz 26 września 2017 przez Comandeer Guru (601,110 p.)

W HTML jest znacznik template, który można wykorzystać do tego celu. Wystarczy tam zapisać szablon dla takiego jednego itemu a następnie go klonować i wstawiać jako kolejny.

0 głosów
odpowiedź 24 września 2017 przez turtelian Obywatel (1,760 p.)
Podpinam się pod twoje pytanie bo też mam z tym problem. Na swoim przykladzie zauważylem że bloki z komentarzem potafią naprawdę duzo polepszyć kod pod kątem czytelności. (+ dla balaganiarzy polecam jakis plugin porzadkujacy kod np beauty dla bracketsa)
komentarz 24 września 2017 przez ProgramistaStepek Nałogowiec (27,020 p.)
Warto zauważyć, że komentarze nie zawsze są dobre. Źle wykorzystane (a tak się dzieje dużo częściej) nie poprawiają czytelności kodu, a wręcz ją niszczą. Także ostrożnie z komentarzami w kodzie :)

Podobne pytania

+3 głosów
1 odpowiedź 255 wizyt
+1 głos
1 odpowiedź 352 wizyt
0 głosów
2 odpowiedzi 341 wizyt

92,568 zapytań

141,424 odpowiedzi

319,634 komentarzy

61,956 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...