📜 ⬆️ ⬇️

Creating a web application on Go in 2017. Part 3

Content

As a result of previous articles, we got an application for Go, which can serve a small piece of HTML. This article will tell about the client part, which, alas, consists mainly of JavaScript, and not Go.


JavaScript in 2017


This part grieved me the most. I really don’t know, nor how to classify the mess that today's JavaScript represents, how to explain it. Attempting to sort things out will lead to a great, but completely different article. So let's take it as a reality that we cannot change, and move on to how best to work with it.


Kinds of JS


The most common type of JS today is known as ES2015 (aka ES6 or ECMAScript 6th edition) and is mainly supported by more or less recent browsers. The latest released JavaScript specification is ES7 (aka ES2016), but since browsers are still catching up with ES6, it looks like ES7, as such, will never be accepted, because most likely the next ES8 will be released in 2017, which will replace ES7 in terms of waiting for browser readiness.


This is odd, but it seems that there is no easy way to create an environment that is fully consistent with a specific version of ECMAScript. You can not even return to the old fully supported version of ES5 or ES4, and therefore it is not possible to check your script for compliance. The most you can do is test it in all available browsers and hope for the best.


Due to the constantly changing and significantly different support of the language among platforms and browsers, transpilation has emerged as a general idea to solve this problem. Transpilation basically comes down to transforming code into one that matches a particular ES version or specific environment. For example, import Bar from 'foo'; can become var Bar = require('foo'); . Therefore, if a particular feature is not supported, it can be made available using the appropriate plug-in or transpiler. I suspect that the phenomenon of transpiliation prevalence has led to additional problems, such that the input expected by the transpiler assuming the existence of a no longer supported feature coincides with the conclusion. Often this can be fixed by additional plugins, and it can be very difficult to figure this out. Repeatedly, I spent a lot of time trying to make money to find out later that my whole approach was out of date due to a new and better solution, now embedded in some other tool.


Js frameworks


There is also a lot of disagreement about which JS framework is the best. This question further confuses the fact that the same framework may be radically different from version to version. It's amazing why not just change the name.


I have no idea which one is the best, but I only had enough patience for a couple. About a year ago I spent a lot of time dealing with AngularJS, and this time, for the sake of diversity, I was messing with React. React seemed more logical to me, so this example application uses it, however.


React and JSX


If you don’t know what React is, here’s my (technically incorrect) explanation: this is HTML embedded in JavaScript. We all have brainwashed about the fact that JavaScript is embedded in HTML and this is the natural order of things, so that the inversion of this relationship does not occur to anyone. Because of the fundamental simplicity of this revolutionary (!) Principle, I consider React ingenious.


"Hello World!" in React it looks like this:


 class Hello extends React.Component { render() { let who = "World"; return ( <h1> Hello {who}! </h1> ); } } 

Please note - the HTML code starts without any wrappers or delimiters. Oddly enough, the opening angle bracket (”<”) works quite reliably as a marker that marks the beginning of the HTML code. And inside HTML, the opening brace indicates that we are temporarily returning to JavaScript, and thus the values ​​of the variables are substituted inside HTML. This is almost all you need to know to “comprehend” React.


Technically, the aforementioned file format is known as JSX , while React is a library that provides classes for creating React objects, such as React.Component above. JSX is transpiled into regular JavaScript using a tool known as Babel, and, in fact, JSX is not even required - the React component can be written in pure JavaScript, and there is an approach in which React is used without JSX. Personally, I think that the "without JSX" method is somewhat more noisy, and I like the fact that Babel allows using a more modern dialect of JS (although, not dealing with transpilers is definitely a plus).


Minimally working example


First, we need three external JavaScript libraries. These are (1) React and ReactDOM, (2) the Babel browser transpiler and (3) a small library called Axios, which is useful for performing JSON HTTP requests. I'll take them from Cloudflare CDN, but you can use some other way. To connect libraries, we need to expand our indexHTML variable to this form:


 const ( cdnReact = "https://cdnjs.cloudflare.com/ajax/libs/react/15.5.4/react.min.js" cdnReactDom = "https://cdnjs.cloudflare.com/ajax/libs/react/15.5.4/react-dom.min.js" cdnBabelStandalone = "https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.24.0/babel.min.js" cdnAxios = "https://cdnjs.cloudflare.com/ajax/libs/axios/0.16.1/axios.min.js" ) const indexHTML = ` <!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>Simple Go Web App</title> </head> <body> <div id='root'></div> <script src="` + cdnReact + `"></script> <script src="` + cdnReactDom + `"></script> <script src="` + cdnBabelStandalone + `"></script> <script src="` + cdnAxios + `"></script> <script src="/js/app.jsx" type="text/babel"></script> </body> </html> ` 

At the very end, "/js/app.jsx" now loaded, which we have yet to create. In the previous section, using http.Dir() we filled in a field in the UI configuration structure called cfg.Assets . Now we need to wrap it in a handler that serves the files, and Go will easily provide us with this:


  http.Handle("/js/", http.FileServer(cfg.Assets)) 

Thus, all files in the "assets/js" directory will be accessible via the "/js/" path.


Now create the assets/js/app.jsx :


 class Hello extends React.Component { render() { let who = "World"; return ( <h1> Hello {who}! </h1> ); } } ReactDOM.render( <Hello/>, document.querySelector("#root")); 

The only difference from the previous listing in the very last line, which, in fact, causes the application to be drawn.


If we now go to the main page with a browser (JS-compatible), we will see “Hello World”.


How it works: the browser uploaded “app.jsx”, as it was told, but since “jsx” is an unfamiliar file type, the browser simply ignored it. When Babel got his chance to work, he looked at our document for script tags with the type “text / babel” and re-requested these pages (so they appear twice in the developer’s tools, but the second request must be served completely from the browser cache). Babel then transformed this script into a valid javascript and executed it, which in turn made React actually display “Hello World”.


List of people


First we need to go back to the server side and create a URI that will display a list of people. To do this, we need an http-handler that can look like this:


 func peopleHandler(m *model.Model) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { people, err := m.People() if err != nil { http.Error(w, "This is an error", http.StatusBadRequest) return } js, err := json.Marshal(people) if err != nil { http.Error(w, "This is an error", http.StatusBadRequest) return } fmt.Fprintf(w, string(js)) }) } 

And we need to register it:


  http.Handle("/people", peopleHandler(m)) 

Now, if we go to "/people" , we should get in reply "[]" . If we add an entry to our table of people with:


 INSERT INTO people (first, last) VALUES('John', 'Doe'); 

The answer should change to [{"Id":1,"First":"John","Last":"Doe"}] .


Finally, we need to connect our React / JSX code so that it displays everything.


To do this, we will create a PersonItem component and another one, called PeopleList , that will use PersonItem .


The PersonItem component should only know how to display itself as a table row:


 class PersonItem extends React.Component { render() { return ( <tr> <td> {this.props.id} </td> <td> {this.props.first} </td> <td> {this.props.last} </td> </tr> ); } } 

PeopleList bit more complicated:


 class PeopleList extends React.Component { constructor(props) { super(props); this.state = { people: [] }; } componentDidMount() { this.serverRequest = axios .get("/people") .then((result) => { this.setState({ people: result.data }); }); } render() { const people = this.state.people.map((person, i) => { return ( <PersonItem key={i} id={person.Id} first={person.First} last={person.Last} /> ); }); return ( <div> <table><tbody> <tr><th>Id</th><th>First</th><th>Last</th></tr> {people} </tbody></table> </div> ); } } 

It has a constructor that initializes the variable this.state . It also declares the componentDidMount() method, which will be called by React at the moment when the component should be ready for drawing, i.e. this is the right place (one of) to get data from the server. The method retrieves the data through an Axios call and stores the result in this.state.people . Finally, render() this.state.people through the contents of this.state.people , creating an instance of PersonItem for each item.


That's all, our application responds with a table (albeit a rather scary one) with a list of people from our database.


Conclusion


In fact, this is all you need to know to create a full-featured web application on Go. This application, of course, has a number of flaws, which, if possible, I will consider later. For example, browser transpilation is not ideal, and although this option is suitable for a not very valuable application, for which page load time is not important, we will probably want to find a way to pre-transplant. In addition, our JSX is limited to one file, this approach makes it difficult to manage any serious-sized application with a large number of components. The application has no navigation. No style. There are things that I forget ...


Enjoy!


PS All the code is completely here .


Continuation


')

Source: https://habr.com/ru/post/329612/


All Articles