След като прекарах 6+ месеца дълбоко в света на Ruby, бях много развълнуван да започна да работя по първото си JavaScript приложение. JavaScript със сигурност е различен свят и свикването с различния синтаксис определено беше предизвикателство. Въпреки това JavaScript е вълнуващ. Това е инструментът за манипулиране на DOM. Ето как са възможни динамичните приложения с една страница. Да видите, че съдържанието се появява, изчезва или променя в уеб браузъра, без страницата да се презарежда, е удоволствие. По-долу ще разгледам как подходих към съставянето на моя първи JavaScript frontend проект, използвайки Ruby on Rails като API за бекенда. Ще разгледам накратко внедряването на бекенда в Heroku и предния край на Netlify, за да стартирам и стартирам първото си приложение в интернет!

За моето приложение реших да създам Coffee Logger. Това ще ми позволи да следя различните кафета, които купувам, и във всяко кафе мога да записвам регистрационни файлове с различна информация, включително използваната рецепта, техники за приготвяне, оценки и т.н. Като човек, който е работил в индустрията за специално кафе през последните 6 години и като голям любител на кафето, този вид подробен подход към регистриране на данни за кафе ме вълнува.

Според мен има два начина да се подходи към кодирането на приложение с пълен стек. Можете или да изградите първо своя бекенд, или да изградите своя интерфейс, като използвате статични данни, и след това да ги замените с динамично съдържание, идващо от бекенда по-късно. Реших да използвам втория подход. Започнах, като създадох файл index.html, заедно с файл style.css и файл main.js. След като премахнах някакъв основен стил, започнах да форматирам таблица, за да съхранявам основна информация за различни кафета (името им, кой ги е изпекъл, разнообразие от зърна и т.н.). Бързо ми стана ясно, че html таблиците не са много отзивчиви, така че се отказах от идеята за таблицата в полза на използването на css grid. Исках информацията на страницата да може да се променя на по-малки размери на екрана и мрежата е (според мен) най-добрият начин за справяне с проблема. След като имах някои фиктивни данни в индексния си файл, беше време да започна да кодирам малко JavaScript, за да направя съдържанието на страницата си динамично.

Първото нещо, което исках да направя в моя JS файл, беше да уловя всеки HTML елемент, до който исках да имам достъп, в променлива. Това ще ми позволи да добавя слушатели на събития към някоя от тези променливи или да редактирам техния стил или вътрешен HTML!

const coffeeModal = document.querySelector(".coffee-modal");
const logModal = document.querySelector(".log-modal");
const coffeeModalClose = document.querySelector(".coffee-modal-close");
const logModalClose = document.querySelector(".log-modal-close");
const newCoffeeForm = document.querySelector(".new-coffee-form");
const newLogForm = document.querySelector(".new-log-form");
const coffeeName = document.getElementById("coffee-name");
.... etc

Знаех, че ще ми трябва формуляр за добавяне на нови кафета към моя списък и реших да се справя с този проблем, като използвам модал. Модалът е блок от съдържание, който изскача, когато се щракне върху нещо, което е скрито по подразбиране, често за улавяне на данни от формуляра. Добавих бутон и използвах слушател на събития към JavaScript, за да потърся щракване върху бутон. След като бутонът е щракнат, мога да използвам JS, за да променя дисплея от „none“ на „flex“. И точно така, бум, динамично уеб съдържание! Javascript е вълнуващо нещо.

button.addEventListener("click", () => {
  coffeeModal.style.display = "flex";
});

Исках да направя отделен js файл, за да се съсредоточа върху JavaScript обект, който исках да създам за всяко кафе. Това ще ми позволи да настроя конструктор, така че всеки създаден кафе обект да има свойства, които мога да извикам в различни методи. Настроих файл, наречен coffee.js, и настроих класа си за кафе.

class Coffee {
  constructor({id, name, roaster, variety, process}) {
    this.id = id;
    this.name = name;
    this.roaster = roaster;
    this.variety = variety;
    this.process = process;
  }
}

Забележете, че елементите в конструктора са във къдрави скоби. Това е така, за да мога да използвам модерна js техника, наречена деструктуриране на обекти. По същество по този начин мога да предам Javascript обект като аргумент на нов обект или функция и мога да извадя конкретни стойности от този обект, стига имената да съвпадат с имената на ключовете в обекта! Много яки неща. По този начин, когато разполагам с моите JSON данни, мога да предам всички тези данни като аргумент и моят нов клас за кафе може да извлече точно това, от което се нуждае, и автоматично да създаде променливи със същите имена, които мога да използвам! Това е страхотно нещо.

Сега беше време да спра да използвам фалшиви данни за контейнери в моя интерфейс и да се обърна към изграждането на API, използвайки моя стар скъп приятел, Ruby On Rails. Честно казано, завръщането в Руби беше изключително приятно. Намирам, че начинът, по който Rails подхожда към файловите структури и разделянето на проблемите, е красив. Бързо пътуване до терминала и написах:

rails new backend --api

И с това пакетът започна да изтегля всички файлове, които ще ми трябват, за да настроя бързо API с помощта на Rails. API тагът в края уведомява групача, че няма да имаме нужда от файлове за преглед, тъй като вместо това ще извеждаме JSON данни, които да бъдат интерпретирани от фронтенд частта на нашето приложение. Бързо създадох модел на кафе, контролер и маршрути, използвайки генератора на ресурси на Rails.

rails g resource Coffee name roaster variety process

След това в моя контролер създадох контролерно действие за моя индексен маршрут.

def index
  coffees = Coffee.all
  render json: coffees
end

Създадох някои начални данни, стартирах своя rails сървър, посетих localhost:3000 и намерих всички данни за кафе, съхранени в моята база данни, красиво представени във формат JSON! Обичам колко е просто! Сега да се върна към интерфейса, за да видя дали мога да взема цялото това JSON съдържание от задния край и да го включа в DOM.

За щастие модерният JavaScript улеснява взаимодействието с API с помощта на Fetch, което връща обещания на JavaScript и ви позволява да управлявате асинхронно какво да правите с тези данни (или липса на данни). За да направите заявка за извличане, просто трябва да:

// replace url with whatever api endpoint you are hitting
fetch (url)
.then(res => res.json)
.then(data => whateverYouWantToDoWithTheData)
.catch(err => whateverYouWantToDoWithErrors)

Толкова е лесно! Създадох JavaScript клас, наречен ApiService, за да се справя с всички мои функции за извикване на api. Първият метод на ApiService, който направих, се нарича getCoffees().

class ApiService {
  static getCoffees() {
    return fetch(baseURL)
    .then(res => res.json()
    .catch(err => console.log(err)
  }
}

Реших, че ще се справя с това какво да правя с тези данни в отделна функция, просто исках методите на ApiService да върнат JSON данните. Тогава по-късно в моята първоначална функция за зареждане на страница мога да включа следния код:

ApiService.getCoffees()
.then(data => data.forEach(coffeeData => {
  let newCoffee = new Coffee(coffeeData);
  newCoffee.renderCoffeeRow();
}));

Ето къде всичко се събира. Мога да предам всички тези JSON данни като аргумент на моите кафе обекти и използвайки деструктурирането, което споменах по-рано, моите кафе обекти могат да извлекат всичко необходимо. След това използвам метод, който създадох, наречен renderCoffeeRow(), за да дам на всяко кафе, върнато от api, ред на моята маса.

Това е мощно нещо и с още няколко заявки за извличане, говорещи напред-назад между моя преден и бекенд, имах напълно функционално приложение с пълен стек, генериращо динамично съдържание на една страница, без да се налага страницата да се презарежда. Създадох друг модел за индивидуалните регистрационни файлове на кафето, като използвах активни асоциации на записи, за да дам на кафето си has_many :logs и log belongs_to :coffee. Също така вложих регистрационните файлове под маршрутите за кафе, за да могат да бъдат достъпни само като посетите „/coffees/2/logs“, за да получите регистрационните файлове за кафе с идентификатор 2! Още няколко заявки за извличане на съществуващи регистрационни файлове, добавяне на нов регистрационен файл и изтриване на регистрационен файл и моето приложение беше завършено!

Наистина исках да пусна това приложение в интернет, тъй като нямах реален опит с каквото и да било внедряване и уеб хостинг. За щастие сайтове като Heroku и Netlify направиха този процес доста лесен. Първата стъпка беше превключването на моята база данни от стандартната, използвана в Heroku, sqlite, към Postgres. Уверете се, че сте инсталирали Postgres на вашата машина и след това стартирайте:

rails db:system:change --to=postgresql

Това е! Стартирайте пакета, за да направите промяната и сте готови да започнете да внедрявате бекенда в Heroku. Препоръчвам да изтеглите Heroku CLI, след като създадете (БЕЗПЛАТЕН) акаунт при тях. Когато сте в задната си папка, изпълнете командата.

heroku create

След като създаде хранилище, можете да стартирате

git push heroku master

Това е изтегляне на вашата информация за API на бекенда в хранилището на Heroku. След това, точно както трябва да направите локално, стартирайте вашите миграции на Heroku.

heroku rake db:migrate

Това трябва да е всичко, което трябва да направите. Вашият сайт трябва да е активен на Heroku сега!! Ще трябва да промените URL адреса, който вашата заявка за извличане прави, за да достигне до сайта на Heroku сега вместо към вашата локална база данни и сега можем да се справим с интерфейса с Netlify.

Това е ОЩЕ ПО-ЛЕСНО. Разбира се, използвах GitHub за проследяване на всички мои ангажименти и т.н., така че след като настроите акаунта си в Netlify, можете да го свържете към вашето хранилище в Github и всички ангажименти, направени за овладяване там, автоматично ще се променят на сайта на Netlify. Казах му да публикува моята предна част на директорията, тъй като моите предни и бекенд файлове съществуват в едно и също хранилище. Той ще търси файл, наречен index.html, така че докато има такъв в корена на директорията, в която търси, той просто ще работи.

Толкова съм развълнуван, че мога да имам приложение с пълен стек в мрежата, разположено на две различни места и работещо безпроблемно заедно. Този JavaScript модул на училището Flatiron ме накара да се почувствам по-уверен в разбирането си за това как работи мрежата и наистина се чувствам така, сякаш вече мога да се нарека пълен стек уеб разработчик!!