[JavaScript] Classの追加(add)を操作する

タブメニューで、URLのパスに応じてメニューのis-activeをつけたい時
L ulのli要素にclassで”is-active”をつけたい
html

<div class="tabs">
      	<ul id="mylist">
      		<li><a href="/btc">BTC</a></li>
      		<li><a href="/eth">ETH</a></li>
      		<li><a href="/xrp">EXP</a></li>
      		<li><a href="/xlm">XLM</a></li>
      		<li><a href="/mona">MONA</a></li>
      	</ul>
      </div>

javascript側
L location.pathnameでURLを取得する
  L querySelectorAllでli要素を全て取得する
  L classList.addでclassを付与する

const pathname = location.pathname;
  	var i;
  	switch(pathname){
  		case '/btc':
  			i = 0;break;
  		case '/eth':
  			i = 1;break;
  		case '/xrp':
  			i = 2;break;
  		case '/xlm':
  			i = 3;break;
  		case '/mona':
  			i = 4;break;
  	}
  	var cols = document.querySelectorAll('#mylist li');
  	cols[i].classList.add('is-active');

OK
やりたいことの7割くらいまでは出来た

[Go] HandleFuncのhandlerをswitchでまとめて実装する

ルーティングによって処理を変えたいが、handlerの中身はほとんど一緒なため、handlerの中でswitch文を使って切り分ける
L html.EscapeString(r.URL.Path) でパスの値を取得できる

func apiHandler(w http.ResponseWriter, r *http.Request){

	var code string // 変数として宣言
	switch html.EscapeString(r.URL.Path) {
	case "/btc":
		code = "BTC_JPY" // ビットコイン
	case "/eth":
		code = "ETH_JPY" // イーサリアム
	case "/xrp":
		code = "XRP_JPY" // リップル
	case "/xlm":
		code = "XML_JPY" // ステラルーメン
	case "/mona":
		code = "MONA_JPY" // モナコイン
	default:
		code = "BTC_JPY"
	}

	uri := "https://api.bitflyer.com/v1/getticker?product_code=" + code
	req, _ := http.NewRequest("GET", uri, nil)

	// 省略
}


func main() {
	http.HandleFunc("/btc", apiHandler)
	http.HandleFunc("/eth", apiHandler)
	http.HandleFunc("/xrp", apiHandler)
	http.HandleFunc("/xlm", apiHandler)
	http.HandleFunc("/mona", apiHandler)
	log.Fatal(http.ListenAndServe(":8080",nil))
}

code := “BTC_JPY” とすると定数になるので、 var code string と変数として宣言する必要がある
なるほど、後はテンプレート側の処理

bulma cssとtypescriptを使いたい

[Go] CoinMarketCapのAPIを操作しよう

CoinMarketCapとは?
-> 急速に成長している仮想通貨スペースにおける世界で最も参照されている価格追跡ウェブサイトです。

まずDeveloper向けのアカウントを作成し、管理画面でAPI_KEYをコピーして、Developer向けページのIntroductionを一通り目を通します。
https://pro.coinmarketcap.com/account
https://coinmarketcap.com/api/documentation/v1/#section/Introduction

$ curl -H “X-CMC_PRO_API_KEY: ${API_KEY}” -H “Accept: application/json” -d “start=1&limit=5000&convert=USD” -G https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest

おおおおおおおおおおお、なんかすげえ

go

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
)

func main(){
	client := &http.Client{}
	req, err := http.NewRequest("GET", "https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest", nil)
	if err != nil {
		log.Print(err)
		os.Exit(1)
	}

	q := url.Values{}
	q.Add("start", "1")
	q.Add("limit", "5000")
	q.Add("convert", "USD")

	req.Header.Set("Accepts", "application/json")
	req.Header.Add("X-CMC_PRO_API_KEY", "${API KEY}")
	req.URL.RawQuery = q.Encode()

	resp, err := client.Do(req);
	if err != nil {
		fmt.Println("Error!")
		os.Exit(1)
	}
	fmt.Println(resp.Status)
	respBody, _ := ioutil.ReadAll(resp.Body)
	fmt.Println(string(respBody));
}

取得する量が多すぎるな
1回のリクエストで25credit使うとなると、1日12回程度しかrequestできんな。流石にこれだと使い物にならんが、イメージは出来た。

[Go] Jsonで出力する

import (
	"encoding/json"
	"log"
	"net/http"
	"time"
)

func apiClockHandler(w http.ResponseWriter, r *http.Request){
	type ResponseBody struct {
		Time time.Time `json:"time"`
	}
	rb := &ResponseBody {
		Time: time.Now(),
	}

	w.Header().Set("Content-type", "application/json")

	if err := json.NewEncoder(w).Encode(rb); err != nil {
		log.Fatal(err)
	}
}

func main(){
	http.HandleFunc("/api/clock", apiClockHandler)
	log.Fatal(http.ListenAndServe(":8080",nil))
}

http://192.168.34.10:8080/api/clock

OK, ある程度のところまで来た

[Go] Bitflyerで取得した値をHTMLで表示する

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"html/template"
	"log"
	"fmt"
)

func bitflyerHandler(w http.ResponseWriter, r *http.Request){

	uri := "https://api.bitflyer.com/v1/getticker"
	req, _ := http.NewRequest("GET", uri, nil)

	client := new(http.Client)
	resp, _ := client.Do(req)
	defer resp.Body.Close()

	byteArray, _ := ioutil.ReadAll(resp.Body)

	var ticker Ticker
	json.Unmarshal([]byte(byteArray),&ticker)


	t := template.Must(template.ParseFiles("/home/vagrant/go/src/github.com/me/sample/src/bitflyer.html.tpl"))
	if err := t.ExecuteTemplate(w, "bitflyer.html.tpl", fmt.Sprintf("%.0f\n", ticker.Ltp)); err != nil {
		log.Fatal(err)
	}
}


func main() {
	// fmt.Printf("%.0f\n",ticker.Ltp)

	http.HandleFunc("/bitflyer/", bitflyerHandler)
	log.Fatal(http.ListenAndServe(":8080",nil))
}


type Ticker struct {
	Code string `json:"product_code"`
	Ltp float64 `json:"ltp"`
}

bitflyer.html.tpl

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document</title>
</head>
<body>
	{{ . }} 
</body>
</html>

なるほど、これをcoin marketでやるか

[Go] サーバーを起動してHTMLファイルを表示

import (
	"log"
	"net/http"
)

func main(){
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("/home/vagrant/go/src/github.com/me/sample/src"))))
	log.Fatal(http.ListenAndServe(":8080",nil))
}

L /home/vagrant/go/src/github.com/me/sample/src にhtmlファイルを配置する

index.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document</title>
</head>
<body>
	Hello world
</body>
</html>

$ go run server.go

ほう、Echo使わなくても表示できるやん
これを動的に表示したい

### 動的に表示

import (
	"fmt"
	"log"
	"net/http"
	"time"
)

func clockHandler(w http.ResponseWriter, r *http.Request){
	fmt.Fprintf(w, `
		<!DOCTYPE html>
        <html>
        <body>
            It's %d o'clock now.
        </body>
        </html>
		`, time.Now().Hour())
}

func main(){
	http.HandleFunc("/clock/", clockHandler)
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("/home/vagrant/go/src/github.com/me/sample/src"))))
	log.Fatal(http.ListenAndServe(":8080",nil))
}

Printfで渡してるだけやな

### データの渡し方

func clockHandler(w http.ResponseWriter, r *http.Request){
	t := template.Must(template.ParseFiles("/home/vagrant/go/src/github.com/me/sample/src/clock.html.tpl"))

	if err := t.ExecuteTemplate(w, "clock.html.tpl", time.Now()); err != nil {
		log.Fatal(err)
	}
}

func main(){
	http.HandleFunc("/clock/", clockHandler)
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("/home/vagrant/go/src/github.com/me/sample/src"))))
	log.Fatal(http.ListenAndServe(":8080",nil))
}

echoでのtemplateと書き方はそんなに変わらんな

[Go] BitflyerのAPIを取得したい

### Bitflyerからの取得

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
)

func main() {
	uri := "https://api.bitflyer.com/v1/gethealth"
	req, _ := http.NewRequest("GET", uri, nil)

	client := new(http.Client)
	resp, _ := client.Do(req)
	defer resp.Body.Close()

	byteArray, _ := ioutil.ReadAll(resp.Body)
	fmt.Println(string(byteArray))
}

$ go run gethealth.go
{“status”:”NORMAL”}

### Jsonの扱い
GoでJsonの受け取り方
1. 構造体を定義

type User struct {
	Name string `json:"name"`
	Age int `json:"age"`
	Job string `json:"job"`
}
func main() {
	userStr :=
	 `{
	    "name": "tokumaru",
	    "age": 20,
	    "job": "student"
	  }`

	var user User
	json.Unmarshal([]byte(userStr),&user)

	fmt.Println(user.Name)
}

// 構造体を定義
type User struct {
	Name string `json:"name"`
	Age int `json:"age"`
	Job string `json:"job"`
}

$ go run test.go
tokumaru

↓上記をつなげる
### Bitcoinの価格を取得
bitcoinは小数点なので、float64で定義する。
表示する際は、fmt.Printf(“%.0f\n”,ticker.Ltp)として、小数点以下を切り捨てる

package main

import (
	"fmt"
	"encoding/json"
	"io/ioutil"
	"net/http"
)

func main() {
	uri := "https://api.bitflyer.com/v1/getticker"
	req, _ := http.NewRequest("GET", uri, nil)

	client := new(http.Client)
	resp, _ := client.Do(req)
	defer resp.Body.Close()

	byteArray, _ := ioutil.ReadAll(resp.Body)

	var ticker Ticker
	json.Unmarshal([]byte(byteArray),&ticker)

	fmt.Printf("%.0f\n",ticker.Ltp)
}

// 構造体を定義
type Ticker struct {
	Code string `json:"product_code"`
	Ltp float64 `json:"ltp"`
}

$ go run test.go
5014562

これをHTMLで表示させたい

[Go] Ubuntu20.04にインストール

GoのDownloadsページから最新版のバージョンを確認します。
https://golang.org/dl/

今回はgo1.17.1

$ sudo wget -c https://dl.google.com/go/go1.17.1.linux-amd64.tar.gz -O – | sudo tar -xz -C /usr/local
$ export PATH=$PATH:/usr/local/go/bin
$ source ~/.profile
$ go version
go version go1.17.1 linux/amd64

うむ、OK

[Go] MySQLに接続する

まずMySQLにデータを作ります。
mysql
create database go;
use go
create table users (
id int auto_increment primary key,
name varchar(255),
age int,
address varchar(255),
update_at datetime
);
mysql> describe users;
+———–+————–+——+—–+———+—————-+
| Field | Type | Null | Key | Default | Extra |
+———–+————–+——+—–+———+—————-+
| id | int | NO | PRI | NULL | auto_increment |
| name | varchar(255) | YES | | NULL | |
| age | int | YES | | NULL | |
| address | varchar(255) | YES | | NULL | |
| update_at | datetime | YES | | NULL | |
+———–+————–+——+—–+———+—————-+

package main

import (
	"fmt"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jinzhu/gorm"
)

func main(){
	_, err := sqlConnect()
	if err != nil {
		panic(err.Error())
	} else {
		fmt.Println("DB connect success!")
	}
}

func sqlConnect()(database *gorm.DB, err error){
	DBMS := "mysql"
	USER := "hoge"
	PASS := "fuga"
	PROTOCOL := "tcp(localhost:3306)"
	DBNAME := "go"

	CONNECT := USER + ":" + PASS + "@" + PROTOCOL + "/" + DBNAME + "?charset=utf8&parseTime=true&loc=Asia%2FTokyo"
	return gorm.Open(DBMS, CONNECT)
}

$ /home/vagrant/go/bin/dep ensure
$ go run main.go
DB connect success!

### データ挿入

import (
	"fmt"
	"time"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jinzhu/gorm"
)

func main(){
	db, err := sqlConnect()
	if err != nil {
		panic(err.Error())
	}
	defer db.Close()

	error := db.Create(&Users{
		Name: "山田太郎",
		Age: 20,
		Address: "東京都港区赤坂1丁目1-1",
		UpdateAt: getDate(),
	}).Error
	if error != nil {
		fmt.Println(error)
	} else {
		fmt.Println("data insert success!")
	}

}

func sqlConnect()(database *gorm.DB, err error){
	DBMS := "mysql"
	USER := "hoge"
	PASS := "fuga"
	PROTOCOL := "tcp(localhost:3306)"
	DBNAME := "go"

	CONNECT := USER + ":" + PASS + "@" + PROTOCOL + "/" + DBNAME + "?charset=utf8&parseTime=true&loc=Asia%2FTokyo"
	return gorm.Open(DBMS, CONNECT)
}

type Users struct {
	ID int
	Name string `json:"name"`
	Age int `json:"age"`
	Address string `json:"address"`
	UpdateAt string `json:"updateAt" sql:"not null;type:date"`
}

func getDate() string {
	const layout = "2006-01-01 15:05:30"
	now := time.Now()
	return now.Format(layout)
}

$ go run main.go
data insert success!

mysql> select * from users;
+—-+————–+——+———————————+———————+
| id | name | age | address | update_at |
+—-+————–+——+———————————+———————+
| 1 | 山田太郎 | 20 | 東京都港区赤坂1丁目1-1 | 2021-09-09 14:23:20 |
+—-+————–+——+———————————+———————+
1 row in set (0.00 sec)

### データの取得

func main(){
	db, err := sqlConnect()
	if err != nil {
		panic(err.Error())
	}
	defer db.Close()

	result := []*Users{}
	error := db.Find(&result).Error
	if error != nil || len(result) == 0 {
		return
	}
	for _, user := range result {
		fmt.Println(user.Name)
	}
}

$ go run main.go
山田太郎

### データの更新

func main(){
	db, err := sqlConnect()
	if err != nil {
		panic(err.Error())
	}
	defer db.Close()

	result := []*Users{}
	error := db.Find(&result).Error
	if error != nil || len(result) == 0 {
		return
	}
	for _, user := range result {
		fmt.Println(user.Name)
	}

	fmt.Println("update")

	error = db.Model(Users{}).Where("id = ?", 1).Update(&Users{
		Name: "佐藤はじめ",
		UpdateAt: getDate(),
	}).Error

	if error != nil {
		fmt.Println(error)
	}

	result = []*Users{}
	db.Find(&result)
	for _, user := range result {
		fmt.Println(user.Name)
	}
}

$ go run main.go
山田太郎
update
佐藤はじめ

### DELETE

	fmt.Println("Delete")

	error = db.Where("id = ?", 1).Delete(Users{}).Error
	

	if error != nil {
		fmt.Println(error)
	}

	result = []*Users{}
	db.Find(&result)
	for _, user := range result {
		fmt.Println(user.Name)
	}

$ go run main.go
佐藤はじめ
Delete

mysql> select * from users;
Empty set (0.00 sec)

OK、CRUDは一通りわかった🈴

[Go] HTMLテンプレートを使って表示

public/view/hello.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document</title>
</head>
<body>
	{{define "hello"}}Hello, {{.}}!{{end}}
</body>
</html>

main.go

import (
	"html/template"
	"io"
	"net/http"

	"github.com/labstack/echo"
)

type Template struct {
	templates *template.Template
}

func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
	return t.templates.ExecuteTemplate(w, name, data)
}

func Hello(c echo.Context) error {
	return c.Render(http.StatusOK, "hello", "World")
}

func main(){
	e := echo.New()
	t := &Template{
		templates: template.Must(template.ParseGlob("public/views/*.html")),
	}
	e.Renderer = t
	e.GET("/hello", Hello)

	e.Logger.Fatal(e.Start(":8000"))
}

うーむ、出来たのはわかったけど、イマイチ仕組みがわからん