So we start another mini project
in the journey of self-learning to code so that we can build essential software and also learn our own thinking patterns and practice our ability to organize ourselves. logical arrangement.
Regarding the delay in posting new articles for a long time when the Sub-Series has not ended, I am really sorry if you are the one who is following my series of articles at this Viblo blog network. The reason is because I want to spend time finishing this mini project
first and define a few limitations in terms of code implementation before sharing in the articles here.
The story is that if you have accompanied the self-study journey starting from the first Web Programming Self-Taught Series, this is already the third mini project
; And I’m sure we are all familiar with using Google Search and Translate to find answers to related problems when reading reference code examples.
Therefore, from this Sub-Series onwards, I will not post detailed code in each article, but instead, the entire completed source code
will be attached in the opening article of each mini project
. After that, the articles will aim to share the overall design perspective and some notable actions or patterns in the process of writing implementation code. That way you’ll be able to easily skim and refer to the points you might find necessary to take notes and not have to spend time Google Searching and adapting your own code architecture.
And here’s a link to a personal blog at GitHub written in Elm, a simple SPA single page application that uses GitHub Pages as a back-end
that handles pure query requests.
- GitHub Page: https://thinhtranhnvn.github.io/
- Source code: Simplicity SPA Elm
Data Service
Our build target is a single page SPA application and so obviously the main operating logic of the website will be included in the JavaScript
code on the front end of the website. This code will be operated by the web browser on the user’s computer to control the entire navigation logic handled after each user interaction event operation.
For example, when a user chooses to open any link in a web page, this code needs to temporarily block the default behavior of the web browser to prevent reloading of the entire web page. Immediately after that, the SPA needs to perform a path analysis of the new link that the user has just selected to make a navigation decision to display the appropriate single page layout. If it is a request to view an article, the SPA will need to identify the data locator elements of that article in the library hosted on GitHub, including:
topic-id
– what topic is the post on?series-id
– WhichSeries
in the above thread?post-slug
– the description ofPost
‘s identifier name in the directory of the aboveSeries
?
And that’s how I created a simple database for my personal blog at GitHub. The directory tree diagram below is the structure of the /data
directory containing the data of the blog that I am using with the content of the articles saved in markdown
code in .txt
files, and the metadata
data is stored in the code. put in .json
files.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | /data ├── topic-list.json | ├── origin [topic] │ ├── overview.txt │ ├── series-list.json | | │ ├── hrdaya-sutra [series] │ | ├── post-list.json │ | ├── 00-the-first-preface.txt │ | ├── 01-the-second-preface.txt │ | ├── ... │ | └── 12-no-five-skandhas-no-object-until-we-come-to-no-realm.txt │ └── yoga-sutra [series] │ ├── post-list.json │ └── ... | ├── linux [topic] │ ├── overview.txt │ ├── series-list.json | | │ ├── gnome [series] │ | ├── post-list.json │ | └── ... │ └── libreoffice [series] │ ├── post-list.json │ └── ... | └── image |
Thus, the type of link that can be used for the navigation logic of the SPA will have the simplest form:
1 2 | https://username.github.io/ topic-id / series-id / post-slug |
And so, after extracting the above article data file locator elements, the SPA will be able to send a request to GitHub to query the original raw
file content with a link of the following form:
1 2 | https://raw.githubusercontent.com/username/username.github.io/main/data/ topic-id / series-id / post-slug.txt |
For example:
Here, when the user chooses to stop at the Topic
overview menu page, the SPA will display the content of an overview.txt
article of that Topic
and so the layout will be the same as the article page. If the user chooses to stop at a single page overview of a Series
, the SPA will display a slightly different layout with the main body replaced by a heading link block that includes a list of links pointing to the post within that Topic
.
Launch files
The entire source code
is located in the /src
directory on the same first level as the /data
directory and starts with the src/App.elm
file. When we run the elm make src/App.elm
compilation command, we will get a /index.html
file located outside the /src
directory and will be immediately recognized by GitHub Page
to display when the user opens the link to it. blog home page for the first time.
In addition, this file is also duplicated right in the same directory as the /404.html
file and so when the user opens a shared link somewhere and points to any article page, GitHub Page
will return a fallback file containing code with the same operating logic.
For example:
https://thinhtranhnvn.github.io/linux/gnome/00-gioi-thieu
When the user opens the above shared link from some source, the browser will receive the /404.html
file instead of the /index.html
file, and now we still have the same processing logic code with the original file. to analyze the current path and send data queries to adjust the interface accordingly. In the event that the right data is found to display, the content will obviously not be a single page layout with an error message, but will still be a single page containing the content.
Code architecture
I started coding naturally from the stage of creating the /mockup
directory to compose static HTML
single pages that simulate the design before starting to code the logic. Here, we have the following components: the main navigation bar Navigator
, the side navigation bar Overview
, the display of the Reader
article content, Indexer
heading link block. All are grouped into a folder /mockup/Element
, actually people often use the word Component
, but probably not too important.
These components are then used for single page layouts: the HomePage.html
home page, the TopicPage.html
topic dashboard, SeriesPage.html
series dashboard, and the PostPage.html
article page. All of these are grouped into a folder /mockup/Layout
. Because only SeriesPage.html
layout is different from the rest in the main content display, I only composed two files in that folder.
In this paragraph, there is a little note that I use a small implicit
convention
that is the homepage is considered the overview page of a defaultTopic
namedOrigin
.
So when it comes to the stage of writing and deploying logic code, I have figured out that I need an overview module
for the entire SPA
, which is module App
. App
will then use the components provided by the module Element
: Element.Navigator
, Element.Overview
, Element.Reader
, Element.Indexer
.
At this point, this is where I wonder the most to choose the packaging design level and share in the article here. Specifically, the most expected design is that the Element
are designed to be independent of the context
of the design and can be used for other websites if desired.
To implement the design at this level, the code requirement is that each Element
will be an isolated program and need to define its own internal data type, not knowing the elements related to navigation logic. application. Then the code used outside of any project
, for example, we have module App
here will have to import
the data type that that Element
uses internally. Then, when querying the data of a Post
, it will have to convert the Post
record into the type of data record that the other Element
requires to work.
However, I chose to stop at the level of module
design incompletely packed with Element
that understand the application’s global navigation logic, and even auto-generate requests to GitHub Page
for queries. data required for display. Thus, module App
is only responsible for adjusting the overall layout of the single page to be displayed based on the routing elements and does not need to deal with the detailed operation logic of the Element
.
The reason for this choice is because this architecture is simpler to deploy if there is no need to reuse Element
for other project
. In addition, from the perspective of anyone else who is new to coding and Elm
, it will be easy to follow the logic of the code’s operation through the articles that I share here. If you still haven’t finished coding any mini project
with Elm
and are very confident in your ability, you can refactor and refactor
source code
that you have posted according to the above architecture. It is definitely a very serious practice and the results will certainly be well worth it.
By the way, the half-baked architecture that I use is somewhat not optimal in terms of performance because the module Element
themselves send query requests for data to display, not using shared data. So there will be cases when a user requests to view a single page where the metadata.json
files are queried multiple times from different Element
.
With a brief overview of this mini project
, I hope that you will be able to test the Declarative + Elm
learning process with a set of source code
that you personally feel satisfied with. And at the present time, after using Elm
to build a simple blog, I have added data to make the decision that there will not be an project elm-fullstack
.
Yes, Elm
is great, very simple to approach when you have a basic JavaScript
background and Imperative
thinking is not too heavy. However, I feel it is necessary to have another Sub-Series dedicated to the serious Functional Programming
story with the Haskell
language. This will be a sure intention after we have prioritized handling the previously located Sub-Series Object-Oriented + Java
.