Walkthrough
Here is a quick overview of what happens when the application loads and you click first South West then Details.
You should look at the mentioned code snippets as you follow along and make sure you understand what they do.
The first screen
- When you open
localhost:8000
in your browser, a HTTP request for/
goes to the Java server. If there were a controller configured for this URL pattern, it would now fire, but there isn't so spring falls back to servingsrc/main/resources/static/index.html
if it exists as this is by convention the default file name to show. This file does exist, so it gets sent to the browser. - This file came from
client/build/index.html
which is the compiled version ofclient/public/index.html
. There is not much to see here except a<div id="root">
which is where the React application will be loaded (the necessary script tags get added when React compiles the file). If you watch the network tab when you openlocalhost:8000
you will see requests for several css and js files, followed by a request of typefetch
forE92000001
which is the React app loading the country data to show. - When the React scripts on the page load, it runs the compiled version of the code in
client/src/App.js
. TheApp.render()
method creates the navigation bar at the top (this is a standard Bootstrap component) and two custom elements calledOverView
andUnitView
(in their own files), passing along some state. The initial state of the app is set in its constructor. - Once the component is loaded, the
OverView.componentDidMount()
function fires. This just delegates to_fetchData()
. Here we fetch the country data:App.state.overview
starts out atcountry
andApp.state.overviewCode
starts out atE92000001
. These are passed as props to theOverView
, with names oftype
andcode
, which are referenced in thefetch()
call incomponentDidMount
. The effect is to send off a request forhttp://localhost:8000/api/country/E92000001
. - You can (and should) paste the above URL in a new browser tab, and you will see some JSON as a result. On the server side, the request is handled by the
CountryController.getCountryById
function (insrc/main/java/uk/ac/bristol/cs/application/controller
) as the path is declared in its@GetMapping
annotation. - The
CountryController
first callsgetWithChildren
on therepository.CountryRepository
. This is just an interface - the implementation is provided by Hibernate, automatically triggered by Spring - but the important part here is the@Query
annotation that explains the extent of the data to fetch. This is HQL, Hibernate's query language, and we tell it to fetch not only the country but also all its regions. - This returns an instance of
model.Country
which is more or less a simple Java class with propertiesname
,code
andRegions
. Hibernate is in charge of creating the instances and the JPA annotations such as@OneToMany
explain how it should process JOINs to fetch data from other classes (such as the regions). - Back in the controller, the next thing we do is render the country as JSON. We will discuss later why we are doing it this particular way, but the code is in
model.ModelClass.renderJSON
so it can be reused for different classes. - The JSON object now gets sent to the client. If you queried the URL yourself in a new tab, you just see the JSON at this point. If React queried it with
fetch()
, it can now check - back inOverView._fetchData()
- whether the call was successful, and if so runthis.setState({loaded: "yes", item: result})
to store the object in its state (the previous call tor.json()
decoded the JSON to a JS object, which is now inresult
). - The
setState
causes react to callrender()
which, now thatthis.state.loaded
isyes
, renders a the country information including a list of regions, pulled fromthis.state.item
which is where the decoded JSON object is stored.
Navigating to a Region
Click on one of the region entries. The following happens:
- The link (actually a button styled as a link, for accessibility reasons) has a click handler
onClick={() => this.navigate(i.code, false)}
(created inOverView.children()
, wherei
is the item to navigate to, e.g. the region). OverView.navigate
callsApp.navigate
throughthis.props
(theApp
passes a reference to its ownnavigate
method when it creates theOverView
).App.navigate
contains a state machine that decides, based on the current state (are we viewing a country, region, county or ward) and theisParent
flag (are we navigating to a child or returning to the parent) what type of object we are navigating to. The only option when we are coming from the country view is a region, so this is set up correctly withsetState
and the code of the new item is set in the app state.- This state change causes
App.render
to run, which changes the props on the overview component. - The props change causes
OverView.componentDidUpdate
to be called with the old props for comparison (the new ones are already inthis.props
). If there has been a code change, then we (a.) immediately set the state back to "not loaded", which triggers arender()
call and (b.) call_fetchData()
, which asynchronously loads the new data and eventually callssetState
again, triggering anotherrender()
this time with the new data.
Exercise: Return to the countries view using the Back to parent link, then shut down the Java server (so that futher API calls will fail). Then click on a region. You should immediately see a "Loading ..." message, followed around a second or so later by "Network error". Explain, with the help of the OverView
code, how this happens.
Details View
Exercise: based on what you have learnt above, sketch the steps that happen when you click on Details to display the occupation data for the selected geographic unit. Look at both the client and the server code. The server-side methods start in StatisticsController
.