- Java Spring Boot & Vanilla Javascript solution
- Java spring boot & vanilla Javascript
- Spring Boot backend
- Start coding the app
- Dataset
- Write your controller
- Code to parse the data
- The front-end
- Page and Javascript
- Solution explanation
- Conclusion
- Разработка приложений с Spring Boot + JS
- Что вы создадите
- Что вам потребуется
- Как проходить этот урок
- Установка Spring Boot CLI
- Создание Spring приложения
- Добавление JavaScript библиотеки
- Написание веб-интерфейса
- Запуск приложения
- Итог
Java Spring Boot & Vanilla Javascript solution
Back in the day when I started working with Java, things were tough, quite tough actually. For my first job out of university, I was working on a large real-time inventory management app built using Java Struts framework with a MySql db backend. I remember how tough it was and how much time it took to just setup things and get it going. Now with Spring Initializr, it’s much easier to setup and get started. I have been working with Spring Boot a lot lately and thought it would be a good idea to build something with in my spare time. This ended being my weekend activity and in this post, I will be talking about my weekend hack which was a Java Spring Boot & Vanilla Javascript solution. The entire solution of this post is online and fully open-source on Github. You can find a link to it at the end of this post.
Java spring boot & vanilla Javascript
A simple Java spring boot app that’s fully open source and is available on Github. I will be honest, my primary goal of this exercise was to brush up on Java myself. For the last few years, I have done a lot of iOS/Swift and NodeJS as opposed to Java. While I remember the Java syntax like the back of my hand, I thought I would practice a bit on some of the new things, like Lombok or CompletableFuture etc.
Spring Boot backend
This was the easy part. It was not that hard at all. I remember starting Java development back in 2007 with the Struts framework and how hard it was. Now with Spring initializr, it’s so easy to get started with building a Spring boot app.
Start coding the app
Generate a Spring boot project from here at Spring Initializr. On that url click on Add Dependencies and add Spring Boot Dev Tools, Lombok and Spring Web and click generate. This will generate a project that will be downloaded on your machine.
Unzip the contents of the downloaded file and import the project in your IDE. One example is using IntelliJ, with IntelliJ, click open project in IntelliJ and import the folder from the zip file generated. The folder structure should look like,
Dataset
I got the dataset for Covid case numbers from the NSW government website. It is the Covid cases data set my likely source of infection.
Write your controller
Controllers are an entry point into your application. Let’s create a REST controller and call it ProcessDataController which will have 3 endpoints.
@RestController @RequestMapping("data") public class ProcessDataController < @Autowired private CSVParser csvParser; @GetMapping("/raw") public Dataset parseToJson() < return csvParser.parseDataset("cnfrm_case_table4_location_likely_source.csv"); >@GetMapping("/byPostcode/") public Dataset casesByPostcode(@PathVariable("postcode") Integer postcode) < return csvParser.byPostcode(postcode); >@GetMapping("/byPostcode//aggregate") public List aggregateCasesByPcode(@PathVariable("postcode") Integer postcode) < return csvParser.caseAggregateByInf(postcode); >>
Notice the AutoWired CSVParser class? we will look at that next.
Code to parse the data
In summary, the Java code parses the contents of the csv files and stores them in a data structure. Let’s look at the Here’s what the Java code looks like,
First up is the one to one mapping of a row in the csv file to a Java POJO.
@NoArgsConstructor @Getter @Setter public class CovidCase < private Date notification_date; private Integer postcode; private String likely_source_of_infection; private String lhd_2010_code; private String lhd_2010_name; private Integer lga_code19; private String lga_name19; public CovidCase(String notification_date, Integer postcode, String likely_source_of_infection, String lhd_2010_code, String lhd_2010_name, Integer lga_code19, String lga_name19) < this.postcode = postcode; this.likely_source_of_infection = likely_source_of_infection; this.lhd_2010_code = lhd_2010_code; this.lhd_2010_name = lhd_2010_name; this.lga_code19 = lga_code19; this.lga_name19 = lga_name19; try < DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); this.notification_date = dateFormat.parse(notification_date); >catch (Exception e) < e.printStackTrace(); >> >
@Autowired ResourceLoader resourceLoader; public CSVParserImpl() < super(); this.parseDataset(defaultFilename); >@Override public Dataset parseDataset(String filePath) < Listcases = new ArrayList<>(); try < InputStream is = getClass().getClassLoader().getResourceAsStream(filePath); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); //skip the first line String headerStr = reader.readLine(); String line = null; while((line = reader.readLine()) != null) < cases.add(new CovidCase(line.split(","))); >Collections.sort(cases, new Comparator() < @Override public int compare(CovidCase o1, CovidCase o2) < if(o1.getNotification_date() == null || o2.getNotification_date() == null)< return 0; >return o1.getNotification_date().compareTo(o2.getNotification_date()); > >); dataCache.setCovidCases(cases); > catch (IOException ioe) < ioe.printStackTrace(); >return dataCache; > @Override public List caseAggregateByInf(Integer postcode) < DateFormat df = new SimpleDateFormat("dd/MM/yyyy"); MapcaseMap = new HashMap<>(); for(CovidCase c: dataCache.getCovidCases()) < if((c.getPostcode() != null) && c.getPostcode().equals(postcode)) < String pattern = c.getLikely_source_of_infection() .trim() .toLowerCase(Locale.ROOT) + ":" + df.format(c.getNotification_date()); CasesByDate caseByDate = caseMap.get(pattern); if (caseByDate == null) < caseByDate = new CasesByDate(); caseByDate.setDate(c.getNotification_date()); caseByDate.setLhdName(c.getLhd_2010_name()); caseByDate.setSource(c.getLikely_source_of_infection()); >caseByDate.setCount(caseByDate.getCount() + 1); caseMap.put(pattern, caseByDate); > > List cases = caseMap.values().stream().collect(Collectors.toList()); cases.sort(new Comparator() < @Override public int compare(CasesByDate o1, CasesByDate o2) < return o2.getDate().compareTo(o1.getDate()); >>); return cases; >
In the method above, the first step is to parse the data from the csv file and storing it in the Java class. Next ups is the method caseAggregateByInf which gets all the data based on the postcode.
The front-end
The front-end has been kept relatively simple since, the required functionality is not a lot, we can solve it all using vanilla Javascript.
Page and Javascript
First, A simple HTML page with a few divs, an input field anda button.
Cases near you
Toggle Details -->
Above we have a very simple HTML page which has a few divs and a button that calls some Javascript. The Javascript is in the index.js file and let us take a look at the index.js file.
async function getAggregate() < const postcode = document.getElementById("inPostcode").value; const url = `/data/byPostcode/$/aggregate`; let cases = await fetch(url) .then(response => response.json()) .then(data => data); renderAggregate(cases); > function renderAggregate(data) < const reducer = (a, b) =>a + b.count; let casesCount = data.reduce(reducer, 0); let header = document.createElement("h3"); header.innerHTML = `Total cases: $`; const tableHeader = document.getElementById("tableHeading"); tableHeader.innerHTML = ""; tableHeader.appendChild(header); const dateMap = new Map(); data.forEach(dt => < let casesObj = dateMap.get(dt.date); if(casesObj === null || casesObj === undefined) < casesObj = <>; casesObj.dataPoints = []; casesObj.date = dt.date; casesObj.total = 0; > casesObj.total += dt.count; casesObj.dataPoints.push(dt); dateMap.set(dt.date, casesObj); >); const parent = document.getElementById("summaryCards"); parent.innerHTML = ""; //we should have a map of case numbers by date for (const Java spring boot javascript of dateMap) < const card = document.createElement("div"); card.className = "resultCard"; const summaryDiv = document.createElement("div"); summaryDiv.style.textAlign = "center"; summaryDiv.innerHTML = `$ case(s) on $
`; card.appendChild(summaryDiv); const dataPointsDiv = document.createElement("div"); value.dataPoints.forEach(dp => < const detailDiv = document.createElement("div"); detailDiv.innerHTML = `$ cases are $
`; card.appendChild(detailDiv); >); parent.appendChild(card); > >
Solution explanation
Once the user clicks the button, it calls the getAggregate method in the index.js file.
const postcode = document.getElementById(«inPostcode»).value;
const url = `/data/byPostcode/$/aggregate`;
let cases = await fetch(url)
.then(response => response.json())
.then(data => data);
renderAggregate(cases);
Remember the get aggregate function is async, hence we can wait for the fetch operation to complete and get the data. If you remember, earlier we defined a ProcessDataController, annotated it with @RequestMapping(‘data’) and added a method
@GetMapping(«/byPostcode//aggregate»)
public List aggregateCasesByPcode(@PathVariable(«postcode») Integer postcode) return csvParser.caseAggregateByInf(postcode);
>
The Javascript function will get the cases data by postcode by invoking the code above. Once the data is retrieved, the renderAggregate method is called which will create a div element and set the innerHTML to render the covid cases data.
const card = document.createElement(«div»);
card.className = «resultCard»; const summaryDiv = document.createElement(«div»);
summaryDiv.style.textAlign = «center»;
summaryDiv.innerHTML = `
$ case(s) on
$
`;
card.appendChild(summaryDiv);
What do you think of the above? It is not that hard is it? Maybe it has a steeper learning curve then when using a framework, but once you get the initial understanding, it’s not that hard. It is just a lot more work i.e. more code to write. Hence I agree had a framework such as Angular or Reactjs be used here, there will be a lot less code.
To read more about the Java code and the vanilla Javascript used, please have a look at the README or look at the source code in the repository. Now let’s focus on to the main aspects of this post, the AWS side of things.
Conclusion
You can find the complete source code as well as instructions on how to run it on this Github.
At this stage this only shows data for the Covid cases in New South Wales, Australia. Feel free to modify it and add the logic to show for another Australian state or for that matter any other part of the world.
If you find any of my posts useful and want to support me, you can buy me a coffee :) https://www.buymeacoffee.com/bhumansoni Or you can buying or even try one of my apps on the App Store. https://mydaytodo.com/apps/ In addition the above, have a look at a few of the other posts, How to create radio buttons using vanilla Javascript https://mydaytodo.com/vanilla-javascript-create-radio-buttons/ How to build a Javascript frontend for Java Spring Boot backend https://mydaytodo.com/java-spring-boot-vanilla-javascript-solution/ Or have a look at some useful Javascript tutorials below https://mydaytodo.com/category/javascript/
Разработка приложений с Spring Boot + JS
Этот урок показывает как использовать интерфейс командной строки Spring Boot для создания приложения с поддержкой Spring MVC на стороне сервера и web-интерфейсом с использованием шаблонизатора Thymeleaf и jQuery.
Что вы создадите
Вы создадите web-приложение с использованием Groovy и Spring, а также Thymeleaf и jQuery.
Что вам потребуется
- Примерно 15 минут свободного времени
- Любимый текстовый редактор или IDE
- JDK 6 и выше
- Gradle 1.11+ или Maven 3.0+
- Вы также можете импортировать код этого урока, а также просматривать web-страницы прямо из Spring Tool Suite (STS), собственно как и работать дальше из него.
Как проходить этот урок
Как и большинство уроков по Spring, вы можете начать с нуля и выполнять каждый шаг, либо пропустить базовые шаги, которые вам уже знакомы. В любом случае, вы в конечном итоге получите рабочий код.
Чтобы начать с нуля, перейдите в Настройка проекта.
- Загрузите и распакуйте архив с кодом этого урока, либо кнонируйте из репозитория с помощью Git: git clone https://github.com/spring-guides/gs-spring-boot-cli-and-js.git
- Перейдите в каталог gs-spring-boot-cli-and-js/initial
- Забегая вперед, создайте Spring приложение
Когда вы закончите, можете сравнить получившийся результат с образцом в gs-spring-boot-cli-and-js/complete .
Установка Spring Boot CLI
Spring Boot включает в себя интерфейс командной строки. Существует несколько способов для его установки.
Вы можете установить его через gvm:
curl -s get.gvmtool.net | bash
После установки gvm, установите CLI:
gvm install springboot spring --version
Если вы используете Mac, то вы также можете установить CLI через Homebrew:
brew tap pivotal/tap brew install springboot spring --version
Воспользовавшись любым из вариантов, вы теперь имеете возможность создать Spring приложение с минимальными усилиями.
Создание Spring приложения
Теперь создайте простейшее приложение:
@Grab("thymeleaf-spring4") @Controller class Application < @RequestMapping("/greeting") public String greeting(@RequestParam(value="name", required=false, defaultValue="World") String name, Model model) < model.addAttribute("name", name) return "greeting" >>
Этот Spring MVC контроллер определяет REST точку выхода /greeting . Позже мы напишем шаблон для него.
Добавление JavaScript библиотеки
Продемонстрируем это с помощью jQuery анимации. Для начала нам необходимо получить копию jQuery. Простейшим вариантом является добавление Groovy аннотации @Grab в начале вашего приложения, которая автоматически получает библиотеку:
@Grab("org.webjars:jquery:2.0.3-1") // это позволит автоматически получить jquery
Написание веб-интерфейса
Давайте создадим Thymeleaf шаблон для поддержки нашей Spring MVC точки выхода:
Теперь добавьте следующий шаблон:
+ '!'" />
Запуск приложения
Теперь запустите приложение:
Чтобы увидеть анимацию, перейдите по адресу http://localhost:8080/greeting.
Это почти пустое приложение включает большое количество возможностей благодаря Spring Boot.
- @Controller говорит о том, что это Spring MVC приложение и Spring Boot будет запускать встроенный контейнеер сервлетов Tomcat
- Несколько вариантов для автозагрузки ресурсов(более подробную информацию смотрите на страницах webjars и static web content блога). Поэтому мы разместили наш web шаблон в каталоге /templates
На данном этапе вы можете добавлять RESTful точки выхода, работать с web-интерфейсами и расширять приложение по вашей необходимости.
Итог
Spring Boot CLI предоставляет быстрай способ создания серверного приложения. Он также поддерживает подключение ваших любимых JavaScript ресурсов и HTML шаблонов. Вы также можете добавлять CSS ресурсы. В итоге: вы не создаете файл проекта, вместо этого вы быстро погружаетесь в разработку вашего приложения.
С оригинальным текстом урока вы можете ознакомиться на spring.io.