coffeescript-ui
Version:
Coffeescript User Interface System
339 lines (266 loc) • 9.75 kB
Markdown
# Tutorial
This tutorial uses [webpack](https://webpack.github.io/) as main dependency to bundle all files together.
It's not obligatory to use it, but It's recommended
If you use webpack we recommend you to use [webpack.ProvidePlugin](https://webpack.js.org/plugins/provide-plugin/). This plugin is useful by letting you not to write ```require("coffeescript-ui/public/cui.js")``` in each file.
## Dependencies
**package.json**
```json
{
...
"devDependencies": {
"coffee-loader": "^0.7.3",
"coffee-script": "^1.12.7",
"coffeescript-ui": "git+https://github.com/programmfabrik/coffeescript-ui.git",
"css-loader": "^0.28.7",
"node-sass": "^4.5.3",
"sass-loader": "^6.0.2",
"style-loader": "^0.18.2",
"webpack": "^2.2.1"
}
}
```
## Get started
### Hello world
```coffeescript
CUI = require("coffeescript-ui/public/cui.js")
require('coffeescript-ui/public/cui.css')
CUI.alert({text: "Hello world"})
```
### Main class
First of all, we create a main class which will be the input file of webpack.
Also this class will have the responsibility of render the main structure of the site.
Inside that class we require **coffeescript-ui** so we can use its *ready* event and *dom.append* function.
**App.coffee**
```coffeescript
CUI = require("coffeescript-ui/public/cui.js")
require('coffeescript-ui/public/cui.css')
class App
constructor: ->
body = "Hello world!"
CUI.dom.append(document.body, body)
CUI.ready ->
new App()
```
### Add a main layout
There is different types of layouts that we can use as main dom element. We use **VerticalLayout** as example.
**App.coffee**
```coffeescript
CUI = require("coffeescript-ui/public/cui.js")
require('coffeescript-ui/public/cui.css')
class App
constructor: ->
body = new CUI.VerticalLayout
top:
content: new CUI.Label
text: "Welcome to Coffeescript UI Tutorial"
center:
content: new CUI.Label
icon: "fa-home"
text: "Hello world"
bottom:
content: new CUI.Label
text: "This is the bottom"
CUI.dom.append(document.body, body)
CUI.ready ->
new App()
```
### Add some styles
We create a **base.scss** inside *scss* directory and we require it in **App** class.
After that we have to choose which classes our elements should use.
**App.coffee**
```coffeescript
CUI = require("coffeescript-ui/public/cui.js")
require('coffeescript-ui/public/cui.css')
require('./scss/base.scss')
class App
constructor: ->
body = new CUI.VerticalLayout
class: "tutorial-main-layout"
top:
class: "tutorial-main-layout-top"
content: new CUI.Label
text: "Welcome to Coffeescript UI Tutorial"
icon: "fa-star"
center:
content: new CUI.Label
icon: "fa-home"
text: "Hello world"
bottom:
class: "tutorial-main-layout-bottom"
content: new CUI.Label
text: "This is the bottom"
CUI.dom.append(document.body, body)
CUI.ready ->
new App()
```
**scss/base.scss**
```scss
.tutorial-main-layout {
background-color: #eef0f1;
}
.tutorial-main-layout-top {
display: block;
font-size: 20px;
}
.tutorial-main-layout-bottom {
text-align: center;
font-size: 10px
}
```
### Fetching data
It's easy to fetch data with *CUI.XHR*. Also we combine it with *CUI.Deferred* as an example,
but It's not necesarry because the method *start()* of *CUI.XHR* class returns a CUI.Promise.
So, to separate responsibilities, we create a new class called "WeatherService". Its responsibility will be just fetch data from the api-rest.
(As an example, we get a json file instead of invoking a real api.)
**modules/weather/WeatherService.coffee**
```coffeescript
CUI = require("coffeescript-ui/public/cui.js")
class WeatherService
= "public/weather-service.json?city="
: (city) ->
deferred = new CUI.Deferred()
xhr = new CUI.XHR
url: + city
xhr.start().done((response) =>
deferred.resolve(response.data)
)
return deferred.promise()
module.exports = WeatherService
```
After that, we modify the main class so it can use this new class and get the data and render it.
**App.coffee**
```coffeescript
CUI = require("coffeescript-ui/public/cui.js")
require('coffeescript-ui/public/cui.css')
require('./scss/base.scss')
WeatherService = require('./modules/weather/WeatherService.coffee')
class App
constructor: ->
body = new CUI.VerticalLayout
class: "tutorial-main-layout"
top:
class: "tutorial-main-layout-top"
content: new CUI.Label
text: "Welcome to Coffeescript UI Tutorial"
icon: "fa-star"
center:
content: new CUI.Label
icon: "fa-spinner"
text: "Fetching data..."
bottom:
class: "tutorial-main-layout-bottom"
content: new CUI.Label
text: "This is the bottom"
(body)
CUI.dom.append(document.body, body)
fetchAndRenderData: (body) ->
WeatherService.getWeather("Berlin").done((data) ->
ul = CUI.dom.ul()
temperature = CUI.dom.append(CUI.dom.li(), data.temperature)
CUI.dom.append(ul, temperature)
skytext = CUI.dom.append(CUI.dom.li(), data.skytext)
CUI.dom.append(ul, skytext)
humidity = CUI.dom.append(CUI.dom.li(), data.humidity)
CUI.dom.append(ul, humidity)
body.replace(ul, "center")
)
CUI.ready ->
new App()
```
The function **fetchAndRenderData** is responsible of invoke **WeatherService** and render the data.
To render the data we use the method *append* of *VerticalLayout*, and we pass the "key" of which "slot" we want to replace.
In this case we replace the *center* "slot", which has the "loading" message.
Please, don't pay to much attention to **CUI.dom.append** invocations, because it's not the best way to render data.
### Create a new template
**CUI.dom.append** is an useful function, but it has to be the last resource to use.
So, we will use a custom template to render our data and remove those CUI.dom.append functions.
The first step is to create a *html* file, which has all the html structure of our desired *template*.
The main element has to have a *data-template* attribute with the name of our template, and optionally is possible to add *slots* inside that element.
*Slots* are used to put values inside them, and each one needs a *data-slot* attribute to access them.
**modules/weather/weather.html**
```html
<div data-template="weather">
<ul>
<li>
<label>Temperature:</label>
<span data-slot="temperature"></span>
</li>
<li>
<label>Skytext:</label>
<span data-slot="skytext"></span>
</li>
<li>
<label>Humidity:</label>
<span data-slot="humidity"></span>
</li>
</ul>
</div>
```
Then, we create a class which will have all the responsibilities about "weather".
This is not obligatory to use templates, but it's a good way to organise your code, and avoid having all the responsibilities in just a class.
**modules/weather/Weather.coffee**
```coffeescript
CUI = require("coffeescript-ui/public/cui.js")
WeatherService = require('./WeatherService.coffee')
htmlTemplate = require('./weather.html')
CUI.Template.loadTemplateText(htmlTemplate);
class Weather extends CUI.Element
initOpts: ->
super()
city:
mandatory: true
check: String
render: ->
template = new CUI.Template
name: "weather"
map:
temperature: true
skytext: true
humidity: true
waitBlock = new CUI.WaitBlock(element: template)
waitBlock.show()
WeatherService.getWeather()
.always =>
waitBlock.destroy()
.done((data) =>
for key of template.map
template.append(data[key], key)
)
template
module.exports = Weather
```
To wire our new template with our application is needed to use the function *CUI.Template.loadTemplateText*
This new class has two methods: *initOpts* and *render*.
The first one is used by the constructor of our extended class *CUI.Element* and It's basically used to map all *options* and check for restrictions on those *options*.
In this case we add a restriction on *city* attribute, it must be a String and mandatory.
The function **render** is the responsible for invoke the service to fetch the data, use the new template to render the data obtained and then return the template.
As we know, **WeatherService.getWeather** is a promise, so the render method will return an empty template until the data is fetched.
That's why we used a CUI element called **WaitBlock**. It basically render an spinner until we destroy it.
The main class suffered some modifications in the wake of these changes.
**App.coffee**
```coffeescript
CUI = require("coffeescript-ui/public/cui.js")
require('coffeescript-ui/public/cui.css')
require('./scss/base.scss')
Weather = require('./modules/weather/Weather.coffee')
class App
constructor: ->
body = new CUI.VerticalLayout
class: "tutorial-main-layout"
top:
class: "tutorial-main-layout-top"
content: new CUI.Label
text: "Welcome to Coffeescript UI Tutorial"
icon: "fa-star"
center:
content: new Weather(city: "Berlin").render()
bottom:
class: "tutorial-main-layout-bottom"
content: new CUI.Label
text: "This is the bottom"
CUI.dom.append(document.body, body)
CUI.ready ->
new App()
```
**------In progress------**