ajaxを用いた動的なチェックボックス


今回は、記事にタグ付するために以下の機能を実現する
①タグを選択するチェックボックス
②新しいタグを作成できる
③タグを作成した際、チェックボックスを自動で更新

#開発の方向性

* 存在するタグの数に応じてチェックボックスを作成する
* クライアントでタグを取得
    * tagデータをjson形式で返すapiを作る（データを返すurl、cakephp）（ajaxの通信先）
    * ajaxでapiにHttpリクエストを投げる
* 新しいタグを追加する
    * tag情報を送ると、保存してくれるapiを作る（ajaxの通信先）
    * タグの追加が終わったタイミングでtagデータを返すapiを呼び出し、追加したデータを取得する

#存在するタグの数に応じてチェックボックスを作成する

タグの数分、ループを回し、その都度`document.createElement("input");`
などでHtmlタグを生成する。

```js
function make_checkbox(tags) {
    var box = document.getElementsByClassName("box");
    var number_checkbox=Object.values(box).length;
    if(Object.keys(tags).length==number_checkbox+1){ //追加したタグの時
        checkbox_key = [Object.keys(tags)[Object.keys(tags).length-1]];
    }else{
        checkbox_key = Object.keys(tags);
    }
    checkbox_key.forEach(
        function(key){
            tag_name = Object.keys(tags[key]);
            var checkbox = document.createElement("input");
            checkbox.setAttribute("type", "checkbox");
            checkbox.setAttribute("name", "data[tag][]");
            checkbox.setAttribute("value", key);
            checkbox.setAttribute("id", "Tag"+key);
            checkbox.setAttribute("class", "box");
            if(tags[key][tag_name]){
                checkbox.setAttribute("checked","checked");
            }
            var label = document.createElement("label");
            label.setAttribute("for","Tag"+key)
            label.innerHTML     = tag_name;
            var checkboxs = document.getElementById("checkbox");
            checkboxs.appendChild(checkbox);
            checkboxs.appendChild(label);
        }
    )
}
```

* appendChildは、HTMLタグに入れ子構造で新たなHTMLタグを追加できる。* [参考] (https://www.sejuku.net/blog/49970)

```javascript

<div id="prime_minister_list"><div>

var abe = document.createElement("li"); //引数で指定したhtmlタグをさくせいする
abe.setAttribute("id", "abe_prime_minister"); //オプションの指定、例えば、name,id,value
abe.innerHTML     = "安倍晋三首相"; //タグの間の値
 
var prime_minister_list = document.getElementById("prime_minister_list"); //idがprime_minister_listのhtmlタグをとってくる
 
prime_minister_list.appendChild(abe); //とってきたhtmlタグに先ほど新しくつくったhtmlタグを挿入
```

#クライアントでタグを取得


##jQueryでajaxを利用するための設定

###jsファイルで読み込む

```
<head><script src="jsファイルのパス"></script>
```
以下エラー

 `GET http://localhost:8000/posts/edit/tag.js net::ERR_ABORTED 404 (Not Found)`

jsが読み込めない！！！
これが謎すぎるから、一旦無視
###解決
htmlヘルパーを使った
`echo $this->Html->script('scripts');`

jqueryを使いたいがネットワークから使う方法を検討してみる

## ネット経由で読み込む

jquery導入参考サイト
https://creive.me/archives/19581/#jQuery-2

```
<head><script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
</head>

```

##tagデータをjson形式で返すapiを作る（データを返すurl、cakephp）（ajaxの通信先）


*参考*
[ajaxの分かりやす参考資料](https://www.sejuku.net/blog/31050)
[はじめてajax](https://qiita.com/nekoneko-wanwan/items/bedc0e826c0842ca0b11#%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E4%B8%80%E8%A6%A7)


###webAPIとは
* 特定の機能を持つプログラム部品。
    * ある機能を提供してくれるだけしっていれば、その内部がどう実装されているか知る必要がなく、関数の考え方に似ている。
    * Web APIで機能を公開しているサーバに対して、インターネットを通じて依頼内容をHTTPリクエストの形で送信すると、処理結果がHTTPレスポンスの形で送られてくる
    * 送受信するデータの形式は、XMLやHTML、JSON、各種の画像ファイル形式
    
[webAPIの参考](https://qiita.com/busyoumono99/items/9b5ffd35dd521bafce47)

#jsonでデータを返すapiを作成
[このサイトの手順でさくせいした](https://book.cakephp.org/2/ja/views/json-and-xml-views.html)

* Router::parseExtensions('json'); をルータファイルに追加すると、 .json 拡張子の リクエストを受けた時や、 application/json ヘッダを受け取った時に CakePHP は自動的に ビュークラスを切り替えるようになります。
    * これをし設定しないと以下のようなエラーが出る
    
```
Error: The action addtag2.json is not defined in controller PostsController
```

`public $components = array('RequestHandler');`をしっかり描かないと以下のエラーが出るので注意！

```
Error: The view for TagsController::get_tag() was not found.
```

api（controllerのアクション＝url）
findでデータベースからtag情報を返す。
他のアクションとあまり変わらない。setを２回やってるくらい？

```php
public function get_tag(){
        $id = $this->request->query('id');
        $options['joins'] = array(
            array('table' => 'post_taggings',
                'type' => 'LEFT',
                'conditions' => array(
                    'Tag.id = post_taggings.tag_id',
                    "post_taggings.post_id = ${id}"
                )
            )
        );
        $options['fields'] = array('name','post_taggings.tag_id','id');
        $tags = $this->Tag->find('list',$options);
        $this->set(compact('tags'));
        $this->set('_serialize','tags');
    }
```

* formやajax全てのhttp通信でクライアントからサーバに送られた値は、postの場合：サーバ側で`$data = $this->request->data;` getの場合：`$page = $this->request->query();`で取得する。
* Joinの種類
    * 内部結合：共通部分だけをとってくる
    * 左外部結合：左にあるものを全部とってくる
    * 右外部結合：右にあるものを全部とってくる
    * 完全外部結合：どちらかにあれば全部とってくる

## ajaxでapiにHttpリクエストを投げる

```js
url = window.location.href; 
    var ary_url = url.split('/');
    let id = ary_url[ary_url.length - 1];
    $.ajax({
        type: 'GET',
        datatype:'json',
        url: "http://localhost:8000/tags/get_tag.json",
        data:{'id':id},
        success: function(data,dataType){         
           console.log(data);
           make_checkbox(data);
        },
        error: function(XMLHttpRequest, textStatus, errorThrown){
            alert('Error : ' + errorThrown);
        }
    }).done(function(){
        ajax_get_tag();
    }
```

```js
url = window.location.href; //http://localhost:8000/posts/edit/109が取得できる
    var ary_url = url.split('/'); // /で区切りリストを作る
    let id = ary_url[ary_url.length - 1]; 
```


#新しいタグを追加する
##tag情報を送ると、保存してくれるapiを作る（ajaxの通信先）

```php
 public function save_tag(){
        $data = $this->request->data['name'];
        $this->Tag->save(array('name' => $data));
        $this->set(compact('data'));
        $this->set('_serialize','data');
    }
```

##tag保存APIを呼び出す
tag保存APIへ通信が完了したタイミングでtagデータを返すapiを呼び出し、追加したデータを取得する


```php
function ajax_post_tag(){
    var element = document.getElementById( "hoge" ) ;
    var data = { 'name' : element.value };

    $.ajax({
        type: 'POST',
        datatype:'json',
        url: "http://localhost:8000/tags/save_tag.json",
        data:data,
        success: function(data,dataType){         
           console.log(data);
        },
        error: function(XMLHttpRequest, textStatus, errorThrown){
            alert('Error : ' + errorThrown);
        }
    })
}
```
*※webブラウジング（初期ロード時）の際*
`window.onload`は、webブラウジングのさいhtmlが全て読み込まれた後に実行される。

```js
window.onload = function(){
      ajax_get_tag(); //tag情報を取ってくる
}
```
#開発を通しての学び

* キャッシュクリア：jsやcssなどクライアント側の修正をしているときに、コードを直しても変化がない場合などがある。そのときはキャッシュクリアしよう。

* `if (!str) {変数strが空文字 or nullの時実行} `




* 入力フォームの値をボタンを押すと保存されるようにしたい

inputタグ間、例えば以下のように、text入力とbuttonがある。そのさい、buttonを押したときにtextのvalueがほしい。

```
<input id="hoge" type="text" value= " "><input type="button">
```
それを可能にするのが、**EHTMLInputElement**
https://syncer.jp/Web/API_Interface/Reference/IDL/HTMLInputElement/value/

* $this->set(配列名、値)`とする必要があるが、compact()を用いると$this->set(compact(配列名))だけでいける。
compact(引数)は、引数に指定した配列名をキーとして配列の値をvalueとする。
* letは、ブロックスコープ（{}）。varはfunctionスコープ（関数内で使える）