[{"data":1,"prerenderedAt":13516},["ShallowReactive",2],{"tag-js-3":3},{"count":4,"content":5},30,[6,347,874,3548,5064,5172,6682,10053,12513,13179],{"id":7,"title":8,"body":9,"category":333,"createdAt":335,"description":336,"extension":337,"index":338,"meta":339,"navigation":181,"path":340,"publish":181,"seo":341,"series":338,"seriesTitle":338,"stem":342,"tag":343,"thumbnail":338,"updatedAt":338,"__hash__":346},"articles\u002Farticles\u002Fclick-event-on-lable.md","label要素と関連付けされたinput要素にクリックイベントを付与すると2回実行される。",{"type":10,"value":11,"toc":327},"minimark",[12,16,263,271,278,283,286,294,298,307,311,317,320,323],[13,14,15],"p",{},"こんにちはjunです。フォームとしての要素、ボタンとしての要素を掛け合わせて以下のようなものを作成していました。",[17,18,23],"pre",{"className":19,"code":20,"language":21,"meta":22,"style":22},"language-html shiki shiki-themes material-theme-ocean","\u003Clabel class=\"c-button\" for=\"form-input\">\n    \u003Cinput data-product_id=\"1\" id=\"form-input\" type=\"checkbox\" name=\"form-input\" value=\"\">\n    \u003Cspan class=\"text\">まとめて購入\u003C\u002Fspan>\n\u003C\u002Flabel>\n\n\u003Cscript>\n    $('.c-button').on('click',function(){\n        \u002F\u002F ...\n    })\n\u003C\u002Fscript>\n","html","",[24,25,26,70,135,167,176,183,193,238,245,254],"code",{"__ignoreMap":22},[27,28,31,35,39,43,46,49,53,55,58,60,62,65,67],"span",{"class":29,"line":30},"line",1,[27,32,34],{"class":33},"sAklC","\u003C",[27,36,38],{"class":37},"s-wAU","label",[27,40,42],{"class":41},"sJ14y"," class",[27,44,45],{"class":33},"=",[27,47,48],{"class":33},"\"",[27,50,52],{"class":51},"sfyAc","c-button",[27,54,48],{"class":33},[27,56,57],{"class":41}," for",[27,59,45],{"class":33},[27,61,48],{"class":33},[27,63,64],{"class":51},"form-input",[27,66,48],{"class":33},[27,68,69],{"class":33},">\n",[27,71,73,76,79,82,84,86,89,91,94,96,98,100,102,105,107,109,112,114,117,119,121,123,125,128,130,133],{"class":29,"line":72},2,[27,74,75],{"class":33},"    \u003C",[27,77,78],{"class":37},"input",[27,80,81],{"class":41}," data-product_id",[27,83,45],{"class":33},[27,85,48],{"class":33},[27,87,88],{"class":51},"1",[27,90,48],{"class":33},[27,92,93],{"class":41}," id",[27,95,45],{"class":33},[27,97,48],{"class":33},[27,99,64],{"class":51},[27,101,48],{"class":33},[27,103,104],{"class":41}," type",[27,106,45],{"class":33},[27,108,48],{"class":33},[27,110,111],{"class":51},"checkbox",[27,113,48],{"class":33},[27,115,116],{"class":41}," name",[27,118,45],{"class":33},[27,120,48],{"class":33},[27,122,64],{"class":51},[27,124,48],{"class":33},[27,126,127],{"class":41}," value",[27,129,45],{"class":33},[27,131,132],{"class":33},"\"\"",[27,134,69],{"class":33},[27,136,138,140,142,144,146,148,151,153,156,160,163,165],{"class":29,"line":137},3,[27,139,75],{"class":33},[27,141,27],{"class":37},[27,143,42],{"class":41},[27,145,45],{"class":33},[27,147,48],{"class":33},[27,149,150],{"class":51},"text",[27,152,48],{"class":33},[27,154,155],{"class":33},">",[27,157,159],{"class":158},"s0W1g","まとめて購入",[27,161,162],{"class":33},"\u003C\u002F",[27,164,27],{"class":37},[27,166,69],{"class":33},[27,168,170,172,174],{"class":29,"line":169},4,[27,171,162],{"class":33},[27,173,38],{"class":37},[27,175,69],{"class":33},[27,177,179],{"class":29,"line":178},5,[27,180,182],{"emptyLinePlaceholder":181},true,"\n",[27,184,186,188,191],{"class":29,"line":185},6,[27,187,34],{"class":33},[27,189,190],{"class":37},"script",[27,192,69],{"class":33},[27,194,196,200,203,206,209,211,214,217,220,222,224,227,229,232,235],{"class":29,"line":195},7,[27,197,199],{"class":198},"sdLwU","    $",[27,201,202],{"class":158},"(",[27,204,205],{"class":33},"'",[27,207,208],{"class":51},".c-button",[27,210,205],{"class":33},[27,212,213],{"class":158},")",[27,215,216],{"class":33},".",[27,218,219],{"class":198},"on",[27,221,202],{"class":158},[27,223,205],{"class":33},[27,225,226],{"class":51},"click",[27,228,205],{"class":33},[27,230,231],{"class":33},",",[27,233,234],{"class":41},"function",[27,236,237],{"class":33},"(){\n",[27,239,241],{"class":29,"line":240},8,[27,242,244],{"class":243},"sC9rS","        \u002F\u002F ...\n",[27,246,248,251],{"class":29,"line":247},9,[27,249,250],{"class":33},"    }",[27,252,253],{"class":158},")\n",[27,255,257,259,261],{"class":29,"line":256},10,[27,258,162],{"class":33},[27,260,190],{"class":37},[27,262,69],{"class":33},[13,264,265,267,268,270],{},[24,266,38],{},"をクリックするとチェックボックスにチェックが入り、さらにクリックイベントも走るという仕組みです。",[24,269,78],{},"だけをクリックしても同じ動作になります。",[13,272,273,274,277],{},"しかしチェックをしてみると挙動が何故か変。私の場合はON・OFF的な処理をしていたので、inputをクリックすると正常に動き、labelだと処理が行われていないという現象に見舞われました。原因はタイトル通り、上記のような構成をjqueryで実装すると、labelをクリックすると",[24,275,276],{},"$('.c-button')","へのクリックイベントが2回走ることが原因です。対処としては3つほどあります。",[279,280,282],"h2",{"id":281},"_1changeイベントに変更する","1：changeイベントに変更する",[13,284,285],{},"input要素を活かしたい場合はこれがベストだと思います。changeイベントはinputなどの入力値が変更された時に発火するイベントです。for属性を用いてinputと関連付けされいるので、labelをクリックすることでinputの値（チェックされたか）を変更させることができます。一方でinputだけをクリックしても発火します。",[13,287,288,289,293],{},"なぜクリックイベントが2回起きるのかがわかりませんが、for属性あたりとかがなんか関係しているのかもしれません。 ",[290,291,292],"strong",{},"change属性であれば値が変更されたか否かでの発火条件となる","ので問題なくなります。",[279,295,297],{"id":296},"_2inputだけにする","2:inputだけにする",[13,299,300,302,303,306],{},[24,301,276],{},"を",[24,304,305],{},"$('.c-button input')","にします。そうすれば一応inputだけクリックした時に発火します。ただし、labelでボタンのスタイルを表現していたりする場合はこの手は微妙です。",[279,308,310],{"id":309},"_3inputでなくアイコン画像にする","3:inputでなくアイコン・画像にする",[13,312,313,314,316],{},"もし",[24,315,78],{},"がフォーム送信的な意味をもたず実装しているならば（見た目だけ）、画像・アイコンで実装するというてもあります。その時はクラスを付与してアイコンを変えると言ったことが必要です。",[279,318,319],{"id":319},"まとめ",[13,321,322],{},"上記の解決法では１が一番ベストです。vanillajsで上記のような構成を作るとなぜか発生しないので、jqueryの仕様なのかもしれません。",[324,325,326],"style",{},"html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .sC9rS, html code.shiki .sC9rS{--shiki-default:#464B5D;--shiki-default-font-style:italic}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":22,"searchDepth":137,"depth":137,"links":328},[329,330,331,332],{"id":281,"depth":72,"text":282},{"id":296,"depth":72,"text":297},{"id":309,"depth":72,"text":310},{"id":319,"depth":72,"text":319},[334],"ministack","2021-04-30","要注意なクリックイベント。input要素にクリックイベントをつけている場合は注意。","md",null,{},"\u002Farticles\u002Fclick-event-on-lable",{"title":8,"description":336},"articles\u002Fclick-event-on-lable",[344,21,345],"js","jquery","EaIYy3tweEkOCbIAFQ9TqAjE0pXI6pYISdYQKN-YfEw",{"id":348,"title":349,"body":350,"category":861,"createdAt":863,"description":349,"extension":337,"index":338,"meta":864,"navigation":181,"path":865,"publish":181,"seo":866,"series":338,"seriesTitle":338,"stem":867,"tag":868,"thumbnail":872,"updatedAt":338,"__hash__":873},"articles\u002Farticles\u002Flaravel-mixx-heroku-fail.md","Laravel mixのアセットがherokuで動かない時の対処法",{"type":10,"value":351,"toc":852},[352,355,358,361,365,372,375,378,382,396,401,404,407,411,426,447,460,471,475,482,492,664,667,814,823,826,829,842,849],[13,353,354],{},"こんにちはjunです。最近よくLaravelの開発を行っていて、プロトタイプとかをherokuにあげるのですがLaravel mixのアセットが動かない（main.jsが404）でちょっと困ったので、今回はその対処法を書きたいと思います。",[13,356,357],{},"Laravel\bmixの説明を軽くするので、さっさと解決策みたいひとは「ビルドアセットをgitignoreしていた」から参照してください。なお使用環境は以下の通りです。",[13,359,360],{},"Laravel 6\nPHP 7.4\nNode.js 12.21（開発）\nNode.js 14.16（heroku）",[279,362,364],{"id":363},"laravel-mixって","Laravel Mixって？",[13,366,367,368,371],{},"Laravel MixはLaravelが公式に出している、JS・CSSのアセットコンパイラとモジュールバンドラです。",[24,369,370],{},"resource","配下のjsやsassをコンパイルしたり、バンドルするwebpackの設定がすでにLaraveに最適化されています。webpackをほとんど触らずともvue、react、sass、そのほかjsライブラリをもちいた開発ができます。",[13,373,374],{},"resource 配下のアセットはLaravel Mixによってpublic配下に自動的に吐かれるようになっています。開発時にはnpm run watchをしていますが、よく見るとpublic配下に開発用ビルドしたアセットファイルが逐一置かれています。",[13,376,377],{},"大体のLaraveフロントエンドではこのMixを用いて開発していることが多いと思います。しかしherokuや本番環境にあげた時にちょっと問題がおきました。",[279,379,381],{"id":380},"mainjs-is-not-found","main.js is not found",[13,383,384,385,387,388,391,392,395],{},"Laravel Mixでフロントを構築して、ある程度バック含めて完成したのでherokuにデプロイしてみました。ビルドは普通に成功し、満を辞して目的の画面をみても",[24,386,381],{},"となってしましました。",[24,389,390],{},"main.js","にはビルドしたvue.jsのプロジェクトがあるはずなのにと思い、サーバーに入って",[24,393,394],{},"public\u002Fjs","配下をみてみたところ、何もありませんでした。",[397,398,400],"h3",{"id":399},"原因その１ビルドアセットをgitignoreしていた","原因その１：ビルドアセットをgitignoreしていた",[13,402,403],{},"ビルドされたアセットはpublic配下に置かれますが、ファイル量が膨大だったり、開発モードと本番モードが混じる可能性があること、あとどうせコロコロ変わるのでバージョン管理から外そうと思い、public\u002Fjsとpublic\u002Fcssはバージョン管理から外していました。",[13,405,406],{},"herokuは基本的にgitを用いてデプロイします。管理対象外のファイルはもちろんherokuサーバー上にないので、アセットがあるはずもありません。であればherokuにデプロイした時にLaravel Mixを叩いて、ビルドすれば解決します。",[397,408,410],{"id":409},"原因その２laravel-mixがdevdependencies","原因その２：Laravel MixがdevDependencies",[13,412,413,414,417,418,421,422,425],{},"Herokuでは",[24,415,416],{},"NODE_ENV","が",[24,419,420],{},"production","（本番環境）で定義されていると、",[24,423,424],{},"devDependencies","がプリーニングされてしまい、ビルドがうまく走りません。",[427,428,432,433,436],"div",{"className":429},[430,431],"alert","alert-success","\nデフォルトでは、Heroku は ​package.json​ の ​dependencies​ および ​devDependencies​ に記載されているすべての依存関係をインストールします。\n",[13,434,435],{},"インストールおよび​ビルドステップ​を実行した後、 Heroku はアプリケーションをデプロイする前に、​devDependencies​ に宣言されているパッケージを取り除きます。",[13,437,438],{},[439,440,441],"small",{},[442,443,444],"a",{"href":444,"rel":445},"https:\u002F\u002Fdevcenter.heroku.com\u002Fja\u002Farticles\u002Fnodejs-support#skip-pruning",[446],"nofollow",[13,448,449,450,455,456,459],{},"インストールおよび​",[442,451,454],{"href":452,"rel":453},"https:\u002F\u002Fdevcenter.heroku.com\u002Farticles\u002Fnodejs-support#heroku-specific-build-steps",[446],"ビルドステップ","​を実行した後、 Heroku はアプリケーションをデプロイする前に、",[24,457,458],{},"​devDependencies​"," に宣言されているパッケージを取り除きます。",[13,461,462,463,466,467,470],{},"対策としては",[24,464,465],{},"package.json","をいじって",[24,468,469],{},"dependencies","に移動します。デフォルトでLaravel Mixはdevの方にインストールされます。それを移動してプッシュすればひとまずLaravel Mixのビルドがうまくいきます。",[397,472,474],{"id":473},"原因その３npm-run-productionを動かすように設定","原因その３：npm run productionを動かすように設定",[13,476,477,478,481],{},"その２に加えて、今度はherokuデプロイの際にLaravel Mixのビルドスクリプトがキックされるように設定します。herokuはNode.jsのプロジェクトがあり、そのスクリプトに",[24,479,480],{},"npm run build","がある場合はそれを実行してくれます。",[13,483,484,485,487,488,491],{},"初期の",[24,486,465],{},"のスクリプトは",[24,489,490],{},"build","がないので、mixが実行されません。",[17,493,498],{"className":494,"code":495,"filename":496,"language":497,"meta":22,"style":22},"language-json shiki shiki-themes material-theme-ocean","\"scripts\": {\n    \"dev\": \"npm run development\",\n    \"development\": \"cross-env NODE_ENV=development node_modules\u002Fwebpack\u002Fbin\u002Fwebpack.js --progress --config=node_modules\u002Flaravel-mix\u002Fsetup\u002Fwebpack.config.js\",\n    \"watch\": \"npm run development -- --watch\",\n    \"watch-poll\": \"npm run watch -- --watch-poll\",\n    \"hot\": \"cross-env NODE_ENV=development node_modules\u002Fwebpack-dev-server\u002Fbin\u002Fwebpack-dev-server.js --inline --hot --disable-host-check --config=node_modules\u002Flaravel-mix\u002Fsetup\u002Fwebpack.config.js\",\n    \"prod\": \"npm run production\",\n    \"production\": \"cross-env NODE_ENV=production node_modules\u002Fwebpack\u002Fbin\u002Fwebpack.js --no-progress --config=node_modules\u002Flaravel-mix\u002Fsetup\u002Fwebpack.config.js\"\n},\n","pakage.json","json",[24,499,500,515,539,559,579,599,619,639,657],{"__ignoreMap":22},[27,501,502,504,507,509,512],{"class":29,"line":30},[27,503,48],{"class":33},[27,505,506],{"class":51},"scripts",[27,508,48],{"class":33},[27,510,511],{"class":158},": ",[27,513,514],{"class":33},"{\n",[27,516,517,520,523,525,528,531,534,536],{"class":29,"line":72},[27,518,519],{"class":33},"    \"",[27,521,522],{"class":41},"dev",[27,524,48],{"class":33},[27,526,527],{"class":33},":",[27,529,530],{"class":33}," \"",[27,532,533],{"class":51},"npm run development",[27,535,48],{"class":33},[27,537,538],{"class":33},",\n",[27,540,541,543,546,548,550,552,555,557],{"class":29,"line":137},[27,542,519],{"class":33},[27,544,545],{"class":41},"development",[27,547,48],{"class":33},[27,549,527],{"class":33},[27,551,530],{"class":33},[27,553,554],{"class":51},"cross-env NODE_ENV=development node_modules\u002Fwebpack\u002Fbin\u002Fwebpack.js --progress --config=node_modules\u002Flaravel-mix\u002Fsetup\u002Fwebpack.config.js",[27,556,48],{"class":33},[27,558,538],{"class":33},[27,560,561,563,566,568,570,572,575,577],{"class":29,"line":169},[27,562,519],{"class":33},[27,564,565],{"class":41},"watch",[27,567,48],{"class":33},[27,569,527],{"class":33},[27,571,530],{"class":33},[27,573,574],{"class":51},"npm run development -- --watch",[27,576,48],{"class":33},[27,578,538],{"class":33},[27,580,581,583,586,588,590,592,595,597],{"class":29,"line":178},[27,582,519],{"class":33},[27,584,585],{"class":41},"watch-poll",[27,587,48],{"class":33},[27,589,527],{"class":33},[27,591,530],{"class":33},[27,593,594],{"class":51},"npm run watch -- --watch-poll",[27,596,48],{"class":33},[27,598,538],{"class":33},[27,600,601,603,606,608,610,612,615,617],{"class":29,"line":185},[27,602,519],{"class":33},[27,604,605],{"class":41},"hot",[27,607,48],{"class":33},[27,609,527],{"class":33},[27,611,530],{"class":33},[27,613,614],{"class":51},"cross-env NODE_ENV=development node_modules\u002Fwebpack-dev-server\u002Fbin\u002Fwebpack-dev-server.js --inline --hot --disable-host-check --config=node_modules\u002Flaravel-mix\u002Fsetup\u002Fwebpack.config.js",[27,616,48],{"class":33},[27,618,538],{"class":33},[27,620,621,623,626,628,630,632,635,637],{"class":29,"line":195},[27,622,519],{"class":33},[27,624,625],{"class":41},"prod",[27,627,48],{"class":33},[27,629,527],{"class":33},[27,631,530],{"class":33},[27,633,634],{"class":51},"npm run production",[27,636,48],{"class":33},[27,638,538],{"class":33},[27,640,641,643,645,647,649,651,654],{"class":29,"line":240},[27,642,519],{"class":33},[27,644,420],{"class":41},[27,646,48],{"class":33},[27,648,527],{"class":33},[27,650,530],{"class":33},[27,652,653],{"class":51},"cross-env NODE_ENV=production node_modules\u002Fwebpack\u002Fbin\u002Fwebpack.js --no-progress --config=node_modules\u002Flaravel-mix\u002Fsetup\u002Fwebpack.config.js",[27,655,656],{"class":33},"\"\n",[27,658,659,662],{"class":29,"line":247},[27,660,661],{"class":33},"}",[27,663,538],{"class":158},[13,665,666],{},"ちょうどprodというのがあるのでbuildに変えてやりましょう。",[17,668,670],{"className":494,"code":669,"filename":496,"language":497,"meta":22,"style":22},"\"scripts\": {\n    \"dev\": \"npm run development\",\n    \"development\": \"cross-env NODE_ENV=development node_modules\u002Fwebpack\u002Fbin\u002Fwebpack.js --progress --config=node_modules\u002Flaravel-mix\u002Fsetup\u002Fwebpack.config.js\",\n    \"watch\": \"npm run development -- --watch\",\n    \"watch-poll\": \"npm run watch -- --watch-poll\",\n    \"hot\": \"cross-env NODE_ENV=development node_modules\u002Fwebpack-dev-server\u002Fbin\u002Fwebpack-dev-server.js --inline --hot --disable-host-check --config=node_modules\u002Flaravel-mix\u002Fsetup\u002Fwebpack.config.js\",\n    \"build\": \"npm run production\",\n    \"production\": \"cross-env NODE_ENV=production node_modules\u002Fwebpack\u002Fbin\u002Fwebpack.js --no-progress --config=node_modules\u002Flaravel-mix\u002Fsetup\u002Fwebpack.config.js\"\n},\n",[24,671,672,684,702,720,738,756,774,792,808],{"__ignoreMap":22},[27,673,674,676,678,680,682],{"class":29,"line":30},[27,675,48],{"class":33},[27,677,506],{"class":51},[27,679,48],{"class":33},[27,681,511],{"class":158},[27,683,514],{"class":33},[27,685,686,688,690,692,694,696,698,700],{"class":29,"line":72},[27,687,519],{"class":33},[27,689,522],{"class":41},[27,691,48],{"class":33},[27,693,527],{"class":33},[27,695,530],{"class":33},[27,697,533],{"class":51},[27,699,48],{"class":33},[27,701,538],{"class":33},[27,703,704,706,708,710,712,714,716,718],{"class":29,"line":137},[27,705,519],{"class":33},[27,707,545],{"class":41},[27,709,48],{"class":33},[27,711,527],{"class":33},[27,713,530],{"class":33},[27,715,554],{"class":51},[27,717,48],{"class":33},[27,719,538],{"class":33},[27,721,722,724,726,728,730,732,734,736],{"class":29,"line":169},[27,723,519],{"class":33},[27,725,565],{"class":41},[27,727,48],{"class":33},[27,729,527],{"class":33},[27,731,530],{"class":33},[27,733,574],{"class":51},[27,735,48],{"class":33},[27,737,538],{"class":33},[27,739,740,742,744,746,748,750,752,754],{"class":29,"line":178},[27,741,519],{"class":33},[27,743,585],{"class":41},[27,745,48],{"class":33},[27,747,527],{"class":33},[27,749,530],{"class":33},[27,751,594],{"class":51},[27,753,48],{"class":33},[27,755,538],{"class":33},[27,757,758,760,762,764,766,768,770,772],{"class":29,"line":185},[27,759,519],{"class":33},[27,761,605],{"class":41},[27,763,48],{"class":33},[27,765,527],{"class":33},[27,767,530],{"class":33},[27,769,614],{"class":51},[27,771,48],{"class":33},[27,773,538],{"class":33},[27,775,776,778,780,782,784,786,788,790],{"class":29,"line":195},[27,777,519],{"class":33},[27,779,490],{"class":41},[27,781,48],{"class":33},[27,783,527],{"class":33},[27,785,530],{"class":33},[27,787,634],{"class":51},[27,789,48],{"class":33},[27,791,538],{"class":33},[27,793,794,796,798,800,802,804,806],{"class":29,"line":240},[27,795,519],{"class":33},[27,797,420],{"class":41},[27,799,48],{"class":33},[27,801,527],{"class":33},[27,803,530],{"class":33},[27,805,653],{"class":51},[27,807,656],{"class":33},[27,809,810,812],{"class":29,"line":247},[27,811,661],{"class":33},[27,813,538],{"class":158},[13,815,816,817,819,820,822],{},"こうすれば",[24,818,480],{}," が ",[24,821,634],{}," を実行して本番用のアセットを作ってくれます。これで解決です。",[279,824,825],{"id":825},"ビルドアセットがうまく出力されない時に考えるべきこと",[13,827,828],{},"今回はいくつかのファイルをビルドする必要があり、デプロイ作業が自動で行われる状況でした。開発環境ではアセットがあるのに、本番だとなくなっているという場合は以下のことを考えましょう。",[830,831,832,836,839],"ul",{},[833,834,835],"li",{},"ファイルはある？（サーバーに入ってlsしてチェック）",[833,837,838],{},"ビルドに必要なモジュールがインストールされているか？",[833,840,841],{},"ビルドスクリプトはキックされているか？",[13,843,844,845,848],{},"大体こうゆうときはビルドがうまくいっていないので、そこからチェックしていくといいです。ちなみに私が検索した時のワードは ",[290,846,847],{},"「heroku laravel mix ビルド」"," でした。それではまた。",[324,850,851],{},"html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":22,"searchDepth":137,"depth":137,"links":853},[854,855,860],{"id":363,"depth":72,"text":364},{"id":380,"depth":72,"text":381,"children":856},[857,858,859],{"id":399,"depth":137,"text":400},{"id":409,"depth":137,"text":410},{"id":473,"depth":137,"text":474},{"id":825,"depth":72,"text":825},[862],"devstack","2021-03-27",{},"\u002Farticles\u002Flaravel-mixx-heroku-fail",{"title":349,"description":349},"articles\u002Flaravel-mixx-heroku-fail",[344,869,870,871],"webpack","laravel","infrastructure","_mix\u002F8_laravel-icon.3f70de72f3.jpg","zvECdzfredOonr51btVrUrKqFCKw247gmW-jXba47XU",{"id":875,"title":876,"body":877,"category":3537,"createdAt":863,"description":3538,"extension":337,"index":30,"meta":3539,"navigation":181,"path":3540,"publish":181,"seo":3541,"series":3542,"seriesTitle":3543,"stem":3544,"tag":3545,"thumbnail":3546,"updatedAt":338,"__hash__":3547},"series\u002Fseries\u002Fwell-study-webpack-1.md","ちゃんと理解するWebpack5。１：webpack基礎とSass・jsのバンドル",{"type":10,"value":878,"toc":3523},[879,884,891,894,905,908,911,922,925,928,931,942,945,948,951,959,976,979,983,986,993,996,1002,1018,1024,1027,1223,1226,1291,1308,1313,1442,1458,1467,1473,1547,1555,1561,1570,1576,1579,1581,1592,1595,1615,1744,1911,2029,2036,2053,2059,2063,2066,2069,2072,2078,2166,2176,2180,2183,2187,2190,2204,2207,2219,2225,2228,2628,2632,2635,2641,2644,2677,2733,2766,2774,3064,3070,3193,3199,3202,3446,3458,3471,3502,3505,3511,3514,3517,3520],[427,880,883],{"className":881},[430,882],"alert-info","\n2021年10月30日：修正事項があっため追記しました。\n",[13,885,886,887,890],{},"こんにちはjunです。最近の自社開発では息を吸うようにNuxt.jsやVue.js、React.jsなどを使用しています。私もそれらのJSライブラリをよく使用するのですが、使いは初めて１年ほどにして「もっと",[290,888,889],{},"JSライブラリやNode.jsの開発をしっかり理解しないと」"," という場面が増えてきました。",[13,892,893],{},"VueやNuxt、Reactなどは特に、ライブラリ自身のチュートリアルやインストール時のセットアップが充実しすぎてwebpack.config.jsさえ見ることもなくなりました。すぐに開発できるのが強みですが、弊害として",[830,895,896,899,902],{},[833,897,898],{},"実際の細かい原理や仕組みを把握できない",[833,900,901],{},"なにか個別にカスタマイズしようとしてもわからない",[833,903,904],{},"応用がわからない",[13,906,907],{},"といったことが生じています。フレームワークやライブラリは便利ですが、簡単でもいいので構成の概念を知っておくと細かいカスタマイズや環境構築ができるようになります。",[13,909,910],{},"VueやReactを使わないプロジェクトなんてざらにありますし、webpackの設定ができるだけでもフロントエンドの開発でできることが違ってきます。数回に分けてwebpackを使用した以下のパターンに分けて環境構築解説をしていきたいと思います。",[830,912,913,916,919],{},[833,914,915],{},"webpack基礎とSass・js",[833,917,918],{},"babelの導入、画像のバンドル、複数ファイル出力",[833,920,921],{},"htmlの取り扱いとまとめ",[13,923,924],{},"それではまず基礎編から初めていきます。",[279,926,927],{"id":927},"webpackとは",[13,929,930],{},"まずwebpackとはについて解説します。webpackは静的モジュールバンドラーというもので、複数のJSファイルなどを１つのファイルにまとめることができます。1つにまとめることで",[830,932,933,936,939],{},[833,934,935],{},"依存性の解決（JSの読み込む順番などを気にしなくて済む）",[833,937,938],{},"モジュール化による保守性と安全性の向上",[833,940,941],{},"圧縮による軽量化",[13,943,944],{},"などが見込まれます。Browserifyというものなど、他にもバンドラーはありますが今は性能的、機能的にもwebpackがかなり主流になっています。",[13,946,947],{},"タスクランナー的な使い方も可能であり、watchして差分だけビルドして開発するなんてことも可能です。",[279,949,950],{"id":950},"基礎的な概念",[13,952,953,958],{},[442,954,957],{"href":955,"rel":956},"https:\u002F\u002Fwebpack.js.org\u002Fconcepts\u002F#browser-compatibility",[446],"公式ドキュメント","にもある通り、構築において以下の概念が重要となります。",[830,960,961,964,967,970,973],{},[833,962,963],{},"Entry（バンドルの起点となるファイル）",[833,965,966],{},"Output（バンドルファイルの吐き出し先）",[833,968,969],{},"Loaders（JS以外のファイルを扱えるようにする）",[833,971,972],{},"Plugins（最適化したり、さらなる機能を追加する）",[833,974,975],{},"Mode（開発か本番か）",[13,977,978],{},"今のところ特にパッとしなくてもいいので、上記にあげた５パターンを再現するためにはこの概念が必要であること、そしてその通りに設定していることを頭に入れておいてください。",[279,980,982],{"id":981},"まずは複数のjsファイルやライブラリをバンドルしてみよう","まずは複数のJSファイルやライブラリをバンドルしてみよう",[13,984,985],{},"まずはSassとかは忘れて、複数のJSファイルとモジュールの連携をやってみましょう。新しくディレクトリを作成してnpm initします。",[17,987,991],{"className":988,"code":990,"language":150},[989],"language-text","npm init\nnpm install -D webpack webpack-cli\n",[24,992,990],{"__ignoreMap":22},[13,994,995],{},"webpackは開発時しか使わないので -DをつけてdevDependenciesに入れます。インストール後にはnode_moduelsが作成されます。そして以下のディレクトリとファイルを作成します。",[17,997,1000],{"className":998,"code":999,"language":150},[989],"├── dist #作成\n    ├── index.html #作成\n├── node_modules\n├── package-lock.json\n├── package.json\n├── webpack.config.js\n└── src　#作成\n    ├── functions.js　#作成\n    └── main.js　#作成\n",[24,1001,999],{"__ignoreMap":22},[13,1003,1004,1005,1007,1008,1011,1012,1014,1015,1017],{},"開発しているときは",[24,1006,390],{},"と",[24,1009,1010],{},"functions.js","に記述していきます。",[24,1013,1010],{},"には共通の関数的な物を書き、モジュールとしてはおなじみの",[24,1016,345],{},"をインストールしておきます。",[17,1019,1022],{"className":1020,"code":1021,"language":150},[989],"npm install jquery --save\n",[24,1023,1021],{"__ignoreMap":22},[13,1025,1026],{},"index.htmlを適当に以下のようにしておきます。",[17,1028,1031],{"className":19,"code":1029,"filename":1030,"language":21,"meta":22,"style":22},"\u003C!DOCTYPE html>\n\u003Chtml>\n    \u003Chead>\n        \u003Cmeta charset=\"utf-8\">\n        \u003Ctitle>webpackの練習\u003C\u002Ftitle>\n    \u003C\u002Fhead>\n\n    \u003Cbody>\n        \u003Cmain>\n            \u003Cdiv id='app'>\n\n            \u003C\u002Fdiv>\n        \u003C\u002Fmain>\n    \u003C\u002Fbody>\n    \u003Cscript src='.\u002Fbundle.js'>\u003C\u002Fscript>\n\u003C\u002Fhtml>\n","dist\u002Findex.html",[24,1032,1033,1046,1054,1063,1085,1103,1112,1116,1125,1134,1154,1159,1169,1179,1188,1214],{"__ignoreMap":22},[27,1034,1035,1038,1041,1044],{"class":29,"line":30},[27,1036,1037],{"class":33},"\u003C!",[27,1039,1040],{"class":37},"DOCTYPE",[27,1042,1043],{"class":41}," html",[27,1045,69],{"class":33},[27,1047,1048,1050,1052],{"class":29,"line":72},[27,1049,34],{"class":33},[27,1051,21],{"class":37},[27,1053,69],{"class":33},[27,1055,1056,1058,1061],{"class":29,"line":137},[27,1057,75],{"class":33},[27,1059,1060],{"class":37},"head",[27,1062,69],{"class":33},[27,1064,1065,1068,1071,1074,1076,1078,1081,1083],{"class":29,"line":169},[27,1066,1067],{"class":33},"        \u003C",[27,1069,1070],{"class":37},"meta",[27,1072,1073],{"class":41}," charset",[27,1075,45],{"class":33},[27,1077,48],{"class":33},[27,1079,1080],{"class":51},"utf-8",[27,1082,48],{"class":33},[27,1084,69],{"class":33},[27,1086,1087,1089,1092,1094,1097,1099,1101],{"class":29,"line":178},[27,1088,1067],{"class":33},[27,1090,1091],{"class":37},"title",[27,1093,155],{"class":33},[27,1095,1096],{"class":158},"webpackの練習",[27,1098,162],{"class":33},[27,1100,1091],{"class":37},[27,1102,69],{"class":33},[27,1104,1105,1108,1110],{"class":29,"line":185},[27,1106,1107],{"class":33},"    \u003C\u002F",[27,1109,1060],{"class":37},[27,1111,69],{"class":33},[27,1113,1114],{"class":29,"line":195},[27,1115,182],{"emptyLinePlaceholder":181},[27,1117,1118,1120,1123],{"class":29,"line":240},[27,1119,75],{"class":33},[27,1121,1122],{"class":37},"body",[27,1124,69],{"class":33},[27,1126,1127,1129,1132],{"class":29,"line":247},[27,1128,1067],{"class":33},[27,1130,1131],{"class":37},"main",[27,1133,69],{"class":33},[27,1135,1136,1139,1141,1143,1145,1147,1150,1152],{"class":29,"line":256},[27,1137,1138],{"class":33},"            \u003C",[27,1140,427],{"class":37},[27,1142,93],{"class":41},[27,1144,45],{"class":33},[27,1146,205],{"class":33},[27,1148,1149],{"class":51},"app",[27,1151,205],{"class":33},[27,1153,69],{"class":33},[27,1155,1157],{"class":29,"line":1156},11,[27,1158,182],{"emptyLinePlaceholder":181},[27,1160,1162,1165,1167],{"class":29,"line":1161},12,[27,1163,1164],{"class":33},"            \u003C\u002F",[27,1166,427],{"class":37},[27,1168,69],{"class":33},[27,1170,1172,1175,1177],{"class":29,"line":1171},13,[27,1173,1174],{"class":33},"        \u003C\u002F",[27,1176,1131],{"class":37},[27,1178,69],{"class":33},[27,1180,1182,1184,1186],{"class":29,"line":1181},14,[27,1183,1107],{"class":33},[27,1185,1122],{"class":37},[27,1187,69],{"class":33},[27,1189,1191,1193,1195,1198,1200,1202,1205,1207,1210,1212],{"class":29,"line":1190},15,[27,1192,75],{"class":33},[27,1194,190],{"class":37},[27,1196,1197],{"class":41}," src",[27,1199,45],{"class":33},[27,1201,205],{"class":33},[27,1203,1204],{"class":51},".\u002Fbundle.js",[27,1206,205],{"class":33},[27,1208,1209],{"class":33},">\u003C\u002F",[27,1211,190],{"class":37},[27,1213,69],{"class":33},[27,1215,1217,1219,1221],{"class":29,"line":1216},16,[27,1218,162],{"class":33},[27,1220,21],{"class":37},[27,1222,69],{"class":33},[13,1224,1225],{},"それでmain.jsは適当にこうしておきましょう。",[17,1227,1232],{"className":1228,"code":1229,"filename":1230,"language":1231,"meta":22,"style":22},"language-javascript shiki shiki-themes material-theme-ocean","import $ from 'jquery';\n\n$('#app').text('hello world')\n","src\u002Fmain.js","javascript",[24,1233,1234,1256,1260],{"__ignoreMap":22},[27,1235,1236,1240,1243,1246,1249,1251,1253],{"class":29,"line":30},[27,1237,1239],{"class":1238},"s6cf3","import",[27,1241,1242],{"class":158}," $ ",[27,1244,1245],{"class":1238},"from",[27,1247,1248],{"class":33}," '",[27,1250,345],{"class":51},[27,1252,205],{"class":33},[27,1254,1255],{"class":33},";\n",[27,1257,1258],{"class":29,"line":72},[27,1259,182],{"emptyLinePlaceholder":181},[27,1261,1262,1265,1267,1269,1272,1274,1276,1278,1280,1282,1284,1287,1289],{"class":29,"line":137},[27,1263,1264],{"class":198},"$",[27,1266,202],{"class":158},[27,1268,205],{"class":33},[27,1270,1271],{"class":51},"#app",[27,1273,205],{"class":33},[27,1275,213],{"class":158},[27,1277,216],{"class":33},[27,1279,150],{"class":198},[27,1281,202],{"class":158},[27,1283,205],{"class":33},[27,1285,1286],{"class":51},"hello world",[27,1288,205],{"class":33},[27,1290,253],{"class":158},[13,1292,1293,1296,1297,1300,1301,1304,1305,1307],{},[24,1294,1295],{},"node_modules"," からjQueryをインポートして、",[24,1298,1299],{},"index.html","を操作します。しかし今のままではバンドルされていないので、main.jsの内容は利用できません。",[24,1302,1303],{},"webpack.config.js","を設定して、",[24,1306,390],{},"をビルドしてみましょう。",[13,1309,1310,1312],{},[24,1311,1303],{},"を以下のように設定してみましょう。",[17,1314,1316],{"className":1228,"code":1315,"filename":1303,"language":1231,"meta":22,"style":22},"module.exports = {\n    \u002F\u002Fバンドル対象のファイル\n    entry: `.\u002Fsrc\u002Fmain.js`,\n  \n    mode:\"development\",\n    \u002F\u002F ファイルの出力設定\n    output: {\n      \u002F\u002F  出力ファイルのディレクトリ名\n      path: `${__dirname}\u002Fdist`,\n      \u002F\u002F 出力ファイル名\n      filename: \"bundle.js\"\n    }\n};\n",[24,1317,1318,1329,1334,1352,1357,1372,1377,1386,1391,1413,1418,1432,1437],{"__ignoreMap":22},[27,1319,1320,1323,1326],{"class":29,"line":30},[27,1321,1322],{"class":33},"module.exports",[27,1324,1325],{"class":33}," =",[27,1327,1328],{"class":33}," {\n",[27,1330,1331],{"class":29,"line":72},[27,1332,1333],{"class":243},"    \u002F\u002Fバンドル対象のファイル\n",[27,1335,1336,1339,1341,1344,1347,1350],{"class":29,"line":137},[27,1337,1338],{"class":37},"    entry",[27,1340,527],{"class":33},[27,1342,1343],{"class":33}," `",[27,1345,1346],{"class":51},".\u002Fsrc\u002Fmain.js",[27,1348,1349],{"class":33},"`",[27,1351,538],{"class":33},[27,1353,1354],{"class":29,"line":169},[27,1355,1356],{"class":158},"  \n",[27,1358,1359,1362,1364,1366,1368,1370],{"class":29,"line":178},[27,1360,1361],{"class":37},"    mode",[27,1363,527],{"class":33},[27,1365,48],{"class":33},[27,1367,545],{"class":51},[27,1369,48],{"class":33},[27,1371,538],{"class":33},[27,1373,1374],{"class":29,"line":185},[27,1375,1376],{"class":243},"    \u002F\u002F ファイルの出力設定\n",[27,1378,1379,1382,1384],{"class":29,"line":195},[27,1380,1381],{"class":37},"    output",[27,1383,527],{"class":33},[27,1385,1328],{"class":33},[27,1387,1388],{"class":29,"line":240},[27,1389,1390],{"class":243},"      \u002F\u002F  出力ファイルのディレクトリ名\n",[27,1392,1393,1396,1398,1401,1404,1406,1409,1411],{"class":29,"line":247},[27,1394,1395],{"class":37},"      path",[27,1397,527],{"class":33},[27,1399,1400],{"class":33}," `${",[27,1402,1403],{"class":158},"__dirname",[27,1405,661],{"class":33},[27,1407,1408],{"class":51},"\u002Fdist",[27,1410,1349],{"class":33},[27,1412,538],{"class":33},[27,1414,1415],{"class":29,"line":256},[27,1416,1417],{"class":243},"      \u002F\u002F 出力ファイル名\n",[27,1419,1420,1423,1425,1427,1430],{"class":29,"line":1156},[27,1421,1422],{"class":37},"      filename",[27,1424,527],{"class":33},[27,1426,530],{"class":33},[27,1428,1429],{"class":51},"bundle.js",[27,1431,656],{"class":33},[27,1433,1434],{"class":29,"line":1161},[27,1435,1436],{"class":33},"    }\n",[27,1438,1439],{"class":29,"line":1171},[27,1440,1441],{"class":33},"};\n",[13,1443,1444,1445,1447,1448,1450,1451,1454,1455,1457],{},"ここで先ほどの５つの概念を思い出してください。Entry、Output、Modeの設定が書かれています。パスは",[24,1446,1303],{},"から見たパスになります。エントリーはsrc配下の",[24,1449,390],{},"、吐き出し先は同じ階層の",[24,1452,1453],{},"dist","に",[24,1456,1429],{},"という名前で吐き出します。",[13,1459,1460,1462,1463,1466],{},[24,1461,1322],{},"は",[24,1464,1465],{},"node.js","のモジュールとして利用するために必要です。webpackはnode.jsの環境下でビルドを行うからです。",[13,1468,1469,1470,1472],{},"設定が終わったら",[24,1471,465],{},"に以下の内容を付け足しておきます。",[17,1474,1476],{"className":494,"code":1475,"filename":465,"language":497,"meta":22,"style":22},"\"scripts\": {\n    \"build\": \"npx webpack-cli build\", #これ\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n},\n",[24,1477,1478,1490,1512,1541],{"__ignoreMap":22},[27,1479,1480,1482,1484,1486,1488],{"class":29,"line":30},[27,1481,48],{"class":33},[27,1483,506],{"class":51},[27,1485,48],{"class":33},[27,1487,511],{"class":158},[27,1489,514],{"class":33},[27,1491,1492,1494,1496,1498,1500,1502,1505,1507,1509],{"class":29,"line":72},[27,1493,519],{"class":33},[27,1495,490],{"class":41},[27,1497,48],{"class":33},[27,1499,527],{"class":33},[27,1501,530],{"class":33},[27,1503,1504],{"class":51},"npx webpack-cli build",[27,1506,48],{"class":33},[27,1508,231],{"class":33},[27,1510,1511],{"class":158}," #これ\n",[27,1513,1514,1516,1519,1521,1523,1525,1528,1531,1534,1536,1539],{"class":29,"line":137},[27,1515,519],{"class":33},[27,1517,1518],{"class":41},"test",[27,1520,48],{"class":33},[27,1522,527],{"class":33},[27,1524,530],{"class":33},[27,1526,1527],{"class":51},"echo ",[27,1529,1530],{"class":158},"\\\"",[27,1532,1533],{"class":51},"Error: no test specified",[27,1535,1530],{"class":158},[27,1537,1538],{"class":51}," && exit 1",[27,1540,656],{"class":33},[27,1542,1543,1545],{"class":29,"line":169},[27,1544,661],{"class":33},[27,1546,538],{"class":158},[13,1548,1549,1551,1552,1554],{},[24,1550,480],{},"を叩くと",[24,1553,869],{},"のバンドルが走ります。それではやってみましょう。",[17,1556,1559],{"className":1557,"code":1558,"language":150},[989],"npm run build\n> webpack_parctice@1.0.0 build \u002FUsers\u002Fjunjiishii\u002FDesktop\u002Fmy_apps\u002Fwebpack_parctice\n> webpack\n\nasset bundle.js 323 KiB [emitted] (name: main)\nruntime modules 937 bytes 4 modules\ncacheable modules 282 KiB\n  .\u002Fsrc\u002Fmain.js 54 bytes [built] [code generated]\n  .\u002Fnode_modules\u002Fjquery\u002Fdist\u002Fjquery.js 282 KiB [built] [code generated]\nwebpack 5.28.0 compiled successfully in 234 ms\n",[24,1560,1558],{"__ignoreMap":22},[13,1562,1563,1564,1566,1567,1569],{},"jqueryがきちんとnode_modulesから読み込まれいます。そして",[24,1565,1453],{},"配下を見るとbundle.jsができています。",[24,1568,1429],{},"の中身をみてみますと、",[1571,1572],"image-render",{":src":1573,":width":1574,":center":1575},"'_mix\u002Fsch-2021-03-27-17.29.11.png'","'500px'","true",[13,1577,1578],{},"わお。必要なjsファイルが一つにまとめられているので、こうなっています。main.js一行とjqueryがくっついています。それではindex.htmlをみてみると..",[1571,1580],{":src":1573,":width":1574,":center":1575},[13,1582,1583,1584,1586,1587,1007,1589,1591],{},"はい。",[24,1585,390],{},"でhello worldを入れたのできちんと",[24,1588,390],{},[24,1590,345],{},"がバンドルされ、動作していることが確認されました。",[279,1593,1594],{"id":1594},"自前のファイルをバンドルしよう",[13,1596,1597,1598,1600,1601,1603,1604,1606,1607,1609,1610,1609,1612,1614],{},"先程は",[24,1599,1295],{},"の",[24,1602,345],{},"をインポートして利用してみました。次は",[24,1605,1010],{},"の関数群をインポートして利用できるようにしてみましょう。",[24,1608,1299],{},"、",[24,1611,390],{},[24,1613,1010],{},"を以下のように変更します。",[17,1616,1618],{"className":19,"code":1617,"filename":1030,"language":21,"meta":22,"style":22},"\u003C!-- body のみ見せます -->\n\u003Cbody>\n    \u003Cmain>\n        \u003Cdiv id='app'>\u003C\u002Fdiv>\n        \u003Cinput type=\"text\" value=\"\" id=\"inputs\">\n        \u003Cbutton id=\"submmit\" >追加する\u003C\u002Fbutton>\n    \u003C\u002Fmain>\n\u003C\u002Fbody>\n",[24,1619,1620,1625,1633,1641,1663,1698,1728,1736],{"__ignoreMap":22},[27,1621,1622],{"class":29,"line":30},[27,1623,1624],{"class":243},"\u003C!-- body のみ見せます -->\n",[27,1626,1627,1629,1631],{"class":29,"line":72},[27,1628,34],{"class":33},[27,1630,1122],{"class":37},[27,1632,69],{"class":33},[27,1634,1635,1637,1639],{"class":29,"line":137},[27,1636,75],{"class":33},[27,1638,1131],{"class":37},[27,1640,69],{"class":33},[27,1642,1643,1645,1647,1649,1651,1653,1655,1657,1659,1661],{"class":29,"line":169},[27,1644,1067],{"class":33},[27,1646,427],{"class":37},[27,1648,93],{"class":41},[27,1650,45],{"class":33},[27,1652,205],{"class":33},[27,1654,1149],{"class":51},[27,1656,205],{"class":33},[27,1658,1209],{"class":33},[27,1660,427],{"class":37},[27,1662,69],{"class":33},[27,1664,1665,1667,1669,1671,1673,1675,1677,1679,1681,1683,1685,1687,1689,1691,1694,1696],{"class":29,"line":178},[27,1666,1067],{"class":33},[27,1668,78],{"class":37},[27,1670,104],{"class":41},[27,1672,45],{"class":33},[27,1674,48],{"class":33},[27,1676,150],{"class":51},[27,1678,48],{"class":33},[27,1680,127],{"class":41},[27,1682,45],{"class":33},[27,1684,132],{"class":33},[27,1686,93],{"class":41},[27,1688,45],{"class":33},[27,1690,48],{"class":33},[27,1692,1693],{"class":51},"inputs",[27,1695,48],{"class":33},[27,1697,69],{"class":33},[27,1699,1700,1702,1705,1707,1709,1711,1714,1716,1719,1722,1724,1726],{"class":29,"line":185},[27,1701,1067],{"class":33},[27,1703,1704],{"class":37},"button",[27,1706,93],{"class":41},[27,1708,45],{"class":33},[27,1710,48],{"class":33},[27,1712,1713],{"class":51},"submmit",[27,1715,48],{"class":33},[27,1717,1718],{"class":33}," >",[27,1720,1721],{"class":158},"追加する",[27,1723,162],{"class":33},[27,1725,1704],{"class":37},[27,1727,69],{"class":33},[27,1729,1730,1732,1734],{"class":29,"line":195},[27,1731,1107],{"class":33},[27,1733,1131],{"class":37},[27,1735,69],{"class":33},[27,1737,1738,1740,1742],{"class":29,"line":240},[27,1739,162],{"class":33},[27,1741,1122],{"class":37},[27,1743,69],{"class":33},[17,1745,1747],{"className":1228,"code":1746,"filename":1010,"language":1231,"meta":22,"style":22},"import $ from 'jquery';\n\nexport default {\n    addNewText(to,input){\n        let text = $(input).val();\n        $(to).append('\u003Cp>'+text+'\u003C\u002Fp>');　#XSSできちゃうので本番では使わないように。。\n        $(input).val('');\n    }\n}\n",[24,1748,1749,1765,1769,1779,1797,1826,1879,1902,1906],{"__ignoreMap":22},[27,1750,1751,1753,1755,1757,1759,1761,1763],{"class":29,"line":30},[27,1752,1239],{"class":1238},[27,1754,1242],{"class":158},[27,1756,1245],{"class":1238},[27,1758,1248],{"class":33},[27,1760,345],{"class":51},[27,1762,205],{"class":33},[27,1764,1255],{"class":33},[27,1766,1767],{"class":29,"line":72},[27,1768,182],{"emptyLinePlaceholder":181},[27,1770,1771,1774,1777],{"class":29,"line":137},[27,1772,1773],{"class":1238},"export",[27,1775,1776],{"class":1238}," default",[27,1778,1328],{"class":33},[27,1780,1781,1784,1786,1790,1792,1794],{"class":29,"line":169},[27,1782,1783],{"class":37},"    addNewText",[27,1785,202],{"class":33},[27,1787,1789],{"class":1788},"s7ZW3","to",[27,1791,231],{"class":33},[27,1793,78],{"class":1788},[27,1795,1796],{"class":33},"){\n",[27,1798,1799,1802,1805,1807,1810,1812,1814,1816,1818,1821,1824],{"class":29,"line":178},[27,1800,1801],{"class":41},"        let",[27,1803,1804],{"class":158}," text",[27,1806,1325],{"class":33},[27,1808,1809],{"class":198}," $",[27,1811,202],{"class":37},[27,1813,78],{"class":158},[27,1815,213],{"class":37},[27,1817,216],{"class":33},[27,1819,1820],{"class":198},"val",[27,1822,1823],{"class":37},"()",[27,1825,1255],{"class":33},[27,1827,1828,1831,1833,1835,1837,1839,1842,1844,1846,1849,1851,1854,1856,1858,1860,1863,1865,1867,1870,1873,1876],{"class":29,"line":185},[27,1829,1830],{"class":198},"        $",[27,1832,202],{"class":37},[27,1834,1789],{"class":158},[27,1836,213],{"class":37},[27,1838,216],{"class":33},[27,1840,1841],{"class":198},"append",[27,1843,202],{"class":37},[27,1845,205],{"class":33},[27,1847,1848],{"class":51},"\u003Cp>",[27,1850,205],{"class":33},[27,1852,1853],{"class":33},"+",[27,1855,150],{"class":158},[27,1857,1853],{"class":33},[27,1859,205],{"class":33},[27,1861,1862],{"class":51},"\u003C\u002Fp>",[27,1864,205],{"class":33},[27,1866,213],{"class":37},[27,1868,1869],{"class":33},";",[27,1871,1872],{"class":37},"　#",[27,1874,1875],{"class":158},"XSSできちゃうので本番では使わないように",[27,1877,1878],{"class":37},"。。\n",[27,1880,1881,1883,1885,1887,1889,1891,1893,1895,1898,1900],{"class":29,"line":195},[27,1882,1830],{"class":198},[27,1884,202],{"class":37},[27,1886,78],{"class":158},[27,1888,213],{"class":37},[27,1890,216],{"class":33},[27,1892,1820],{"class":198},[27,1894,202],{"class":37},[27,1896,1897],{"class":33},"''",[27,1899,213],{"class":37},[27,1901,1255],{"class":33},[27,1903,1904],{"class":29,"line":240},[27,1905,1436],{"class":33},[27,1907,1908],{"class":29,"line":247},[27,1909,1910],{"class":33},"}\n",[17,1912,1914],{"className":1228,"code":1913,"filename":1230,"language":1231,"meta":22,"style":22},"import $ from 'jquery';\nimport funcs from '.\u002Ffunctions';\n\n$('#submmit').on('click',()=>{\n    return funcs.addNewText('#app','#inputs');\n})\n\n",[24,1915,1916,1932,1950,1954,1989,2023],{"__ignoreMap":22},[27,1917,1918,1920,1922,1924,1926,1928,1930],{"class":29,"line":30},[27,1919,1239],{"class":1238},[27,1921,1242],{"class":158},[27,1923,1245],{"class":1238},[27,1925,1248],{"class":33},[27,1927,345],{"class":51},[27,1929,205],{"class":33},[27,1931,1255],{"class":33},[27,1933,1934,1936,1939,1941,1943,1946,1948],{"class":29,"line":72},[27,1935,1239],{"class":1238},[27,1937,1938],{"class":158}," funcs ",[27,1940,1245],{"class":1238},[27,1942,1248],{"class":33},[27,1944,1945],{"class":51},".\u002Ffunctions",[27,1947,205],{"class":33},[27,1949,1255],{"class":33},[27,1951,1952],{"class":29,"line":137},[27,1953,182],{"emptyLinePlaceholder":181},[27,1955,1956,1958,1960,1962,1965,1967,1969,1971,1973,1975,1977,1979,1981,1984,1987],{"class":29,"line":169},[27,1957,1264],{"class":198},[27,1959,202],{"class":158},[27,1961,205],{"class":33},[27,1963,1964],{"class":51},"#submmit",[27,1966,205],{"class":33},[27,1968,213],{"class":158},[27,1970,216],{"class":33},[27,1972,219],{"class":198},[27,1974,202],{"class":158},[27,1976,205],{"class":33},[27,1978,226],{"class":51},[27,1980,205],{"class":33},[27,1982,1983],{"class":33},",()",[27,1985,1986],{"class":41},"=>",[27,1988,514],{"class":33},[27,1990,1991,1994,1997,1999,2002,2004,2006,2008,2010,2012,2014,2017,2019,2021],{"class":29,"line":178},[27,1992,1993],{"class":1238},"    return",[27,1995,1996],{"class":158}," funcs",[27,1998,216],{"class":33},[27,2000,2001],{"class":198},"addNewText",[27,2003,202],{"class":37},[27,2005,205],{"class":33},[27,2007,1271],{"class":51},[27,2009,205],{"class":33},[27,2011,231],{"class":33},[27,2013,205],{"class":33},[27,2015,2016],{"class":51},"#inputs",[27,2018,205],{"class":33},[27,2020,213],{"class":37},[27,2022,1255],{"class":33},[27,2024,2025,2027],{"class":29,"line":185},[27,2026,661],{"class":33},[27,2028,253],{"class":158},[13,2030,2031,2032,2035],{},"functions.jsには便利関数を入れているのを想定しているので、オブジェクトに関数を入れておきます。それをexportします。",[24,2033,2034],{},"addNewText()","は指定した入力フォームの文字列を、指定したDOMにpタグとして入れてくれる神メソッドです。TODO LIST的な物を開発していると思ってください。",[13,2037,2038,2040,2041,2044,2045,2048,2049,2052],{},[24,2039,390],{},"では",[24,2042,2043],{},"import funcs from '.\u002Ffunctions';","で読み込みます。",[24,2046,2047],{},"funcs","というのはオブジェクトなので",[24,2050,2051],{},"funcs.addNewText()","で使用できます。イベントリスナーでボタンを押したら追加できるようにしてみましょう。",[13,2054,2055,2056,2058],{},"ビルドしたら再度",[24,2057,1299],{},"をみてみます。",[1571,2060],{":src":2061,":width":2062},"'_mix\u002Fsch-2021-03-27-18.04.19-768x283.png'","'300px'",[1571,2064],{":src":2065,":width":2062},"'_mix\u002Fsch-2021-03-27-18.05.16.png'",[13,2067,2068],{},"入力内容を入れて、追加するを押すとこのように文字が追加されました。webpackが各ファイルのimportの関係性を解決してくれるので、これで複数ファイルのバンドルができるようになりました。",[397,2070,2071],{"id":2071},"watchモードを追加しておく",[13,2073,2074,2075,2077],{},"jsのコードを書いてビルドしたらスクリプトにエラーが起きていたという時、毎回ビルドするのは面倒です。webpackにはwatchモードという物があり、変更を検知して差分ビルドをしてくれます。",[24,2076,465],{},"に以下のように記述します。",[17,2079,2081],{"className":494,"code":2080,"filename":465,"language":497,"meta":22,"style":22},"\"scripts\": {\n  \"build\": \"npx webpack-cli build\",\n  \"watch\": \"npx webpack-cli watch\",　\b#これ\n  \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n},\n",[24,2082,2083,2095,2114,2136,2160],{"__ignoreMap":22},[27,2084,2085,2087,2089,2091,2093],{"class":29,"line":30},[27,2086,48],{"class":33},[27,2088,506],{"class":51},[27,2090,48],{"class":33},[27,2092,511],{"class":158},[27,2094,514],{"class":33},[27,2096,2097,2100,2102,2104,2106,2108,2110,2112],{"class":29,"line":72},[27,2098,2099],{"class":33},"  \"",[27,2101,490],{"class":41},[27,2103,48],{"class":33},[27,2105,527],{"class":33},[27,2107,530],{"class":33},[27,2109,1504],{"class":51},[27,2111,48],{"class":33},[27,2113,538],{"class":33},[27,2115,2116,2118,2120,2122,2124,2126,2129,2131,2133],{"class":29,"line":137},[27,2117,2099],{"class":33},[27,2119,565],{"class":41},[27,2121,48],{"class":33},[27,2123,527],{"class":33},[27,2125,530],{"class":33},[27,2127,2128],{"class":51},"npx webpack-cli watch",[27,2130,48],{"class":33},[27,2132,231],{"class":33},[27,2134,2135],{"class":158},"　\b#これ\n",[27,2137,2138,2140,2142,2144,2146,2148,2150,2152,2154,2156,2158],{"class":29,"line":169},[27,2139,2099],{"class":33},[27,2141,1518],{"class":41},[27,2143,48],{"class":33},[27,2145,527],{"class":33},[27,2147,530],{"class":33},[27,2149,1527],{"class":51},[27,2151,1530],{"class":158},[27,2153,1533],{"class":51},[27,2155,1530],{"class":158},[27,2157,1538],{"class":51},[27,2159,656],{"class":33},[27,2161,2162,2164],{"class":29,"line":178},[27,2163,661],{"class":33},[27,2165,538],{"class":158},[13,2167,2168,2169,2172,2173,2175],{},"これで",[24,2170,2171],{},"npm run watch"," を行うことで",[24,2174,565],{},"モードでビルドが動くようになります。",[279,2177,2179],{"id":2178},"sassをコンパイルバンドルしてみる","Sassをコンパイル・バンドルしてみる",[13,2181,2182],{},"Webpackは基本的にjsファイルのバンドルを想定していますが、css・sassもバンドルできます。今回はsassの説明をします。",[397,2184,2186],{"id":2185},"必要なモジュールを追加インストール設定","必要なモジュールを追加インストール&設定",[13,2188,2189],{},"webpackがsassをバンドルしてビルドするには",[830,2191,2192,2195,2198,2201],{},[833,2193,2194],{},"sass-loader",[833,2196,2197],{},"node-sass",[833,2199,2200],{},"css-loader",[833,2202,2203],{},"mini-css-extract-plugin",[13,2205,2206],{},"の４つのdevDependenciesが必要になります。",[13,2208,2209,2211,2212,2214,2215,2218],{},[290,2210,2194],{},"は重要な概念に出てきた「loader」に該当します。webpackがsassファイルをバンドルするのに必要です。",[290,2213,2197],{},"はnode.jsでsassのコンパイルをするために必要です。sassをコンパイルして生成されたcssを扱うために",[290,2216,2217],{},"css-loader、mini-css-extract-plugin","が必要です。そのためまずはインストールしてみましょう。",[17,2220,2223],{"className":2221,"code":2222,"language":150},[989],"npm install -D sass-loader node-sass css-loader mini-css-extract-plugin\n",[24,2224,2222],{"__ignoreMap":22},[13,2226,2227],{},"ひとまず入れられたら、webpack.config.jsを以下のように変更します。",[17,2229,2231],{"className":1228,"code":2230,"filename":1303,"language":1231,"meta":22,"style":22},"const MiniCssExtractPlugin = require('mini-css-extract-plugin');\n\nmodule.exports = {\n    \u002F\u002Fバンドル対象のファイル\n    entry: '.\u002Fsrc\u002Fjs\u002Fmain.js',\n  \n    mode:\"development\",\n    \n    \u002F\u002F ここから追加\n    module: {\n        rules: [\n          {\n            test: \u002F\\.(sa|sc|c)ss$\u002F,\n            exclude: \u002Fnode_modules\u002F,\n            use: [\n              MiniCssExtractPlugin.loader,\n              {\n                loader: 'css-loader',\n                options: { url: false }\n              },\n              'sass-loader'\n            ]\n          }\n        ]\n      },\n    \u002F\u002F ファイルの出力設定\n    output: {\n      \u002F\u002F  出力ファイルのディレクトリ名\n      path: `${__dirname}\u002Fdist`,\n      \u002F\u002F 出力ファイル名\n      filename: \"bundle.js\"\n    },\n 　　\u002F\u002F ここも追加\n    plugins: [\n      new MiniCssExtractPlugin({\n          filename:'style.css'\n      })\n    ]\n};\n",[24,2232,2233,2258,2262,2270,2274,2289,2293,2307,2312,2317,2326,2336,2341,2382,2397,2406,2418,2424,2440,2463,2469,2480,2486,2492,2498,2504,2509,2518,2523,2542,2546,2559,2565,2571,2581,2594,2609,2617,2623],{"__ignoreMap":22},[27,2234,2235,2238,2241,2243,2246,2248,2250,2252,2254,2256],{"class":29,"line":30},[27,2236,2237],{"class":41},"const",[27,2239,2240],{"class":158}," MiniCssExtractPlugin ",[27,2242,45],{"class":33},[27,2244,2245],{"class":198}," require",[27,2247,202],{"class":158},[27,2249,205],{"class":33},[27,2251,2203],{"class":51},[27,2253,205],{"class":33},[27,2255,213],{"class":158},[27,2257,1255],{"class":33},[27,2259,2260],{"class":29,"line":72},[27,2261,182],{"emptyLinePlaceholder":181},[27,2263,2264,2266,2268],{"class":29,"line":137},[27,2265,1322],{"class":33},[27,2267,1325],{"class":33},[27,2269,1328],{"class":33},[27,2271,2272],{"class":29,"line":169},[27,2273,1333],{"class":243},[27,2275,2276,2278,2280,2282,2285,2287],{"class":29,"line":178},[27,2277,1338],{"class":37},[27,2279,527],{"class":33},[27,2281,1248],{"class":33},[27,2283,2284],{"class":51},".\u002Fsrc\u002Fjs\u002Fmain.js",[27,2286,205],{"class":33},[27,2288,538],{"class":33},[27,2290,2291],{"class":29,"line":185},[27,2292,1356],{"class":158},[27,2294,2295,2297,2299,2301,2303,2305],{"class":29,"line":195},[27,2296,1361],{"class":37},[27,2298,527],{"class":33},[27,2300,48],{"class":33},[27,2302,545],{"class":51},[27,2304,48],{"class":33},[27,2306,538],{"class":33},[27,2308,2309],{"class":29,"line":240},[27,2310,2311],{"class":158},"    \n",[27,2313,2314],{"class":29,"line":247},[27,2315,2316],{"class":243},"    \u002F\u002F ここから追加\n",[27,2318,2319,2322,2324],{"class":29,"line":256},[27,2320,2321],{"class":37},"    module",[27,2323,527],{"class":33},[27,2325,1328],{"class":33},[27,2327,2328,2331,2333],{"class":29,"line":1156},[27,2329,2330],{"class":37},"        rules",[27,2332,527],{"class":33},[27,2334,2335],{"class":158}," [\n",[27,2337,2338],{"class":29,"line":1161},[27,2339,2340],{"class":33},"          {\n",[27,2342,2343,2346,2348,2351,2354,2356,2359,2362,2365,2367,2370,2372,2375,2377,2380],{"class":29,"line":1171},[27,2344,2345],{"class":37},"            test",[27,2347,527],{"class":33},[27,2349,2350],{"class":33}," \u002F",[27,2352,2353],{"class":158},"\\.",[27,2355,202],{"class":33},[27,2357,2358],{"class":51},"sa",[27,2360,2361],{"class":33},"|",[27,2363,2364],{"class":51},"sc",[27,2366,2361],{"class":33},[27,2368,2369],{"class":51},"c",[27,2371,213],{"class":33},[27,2373,2374],{"class":51},"ss",[27,2376,1264],{"class":1238},[27,2378,2379],{"class":33},"\u002F",[27,2381,538],{"class":33},[27,2383,2384,2387,2389,2391,2393,2395],{"class":29,"line":1181},[27,2385,2386],{"class":37},"            exclude",[27,2388,527],{"class":33},[27,2390,2350],{"class":33},[27,2392,1295],{"class":51},[27,2394,2379],{"class":33},[27,2396,538],{"class":33},[27,2398,2399,2402,2404],{"class":29,"line":1190},[27,2400,2401],{"class":37},"            use",[27,2403,527],{"class":33},[27,2405,2335],{"class":158},[27,2407,2408,2411,2413,2416],{"class":29,"line":1216},[27,2409,2410],{"class":158},"              MiniCssExtractPlugin",[27,2412,216],{"class":33},[27,2414,2415],{"class":158},"loader",[27,2417,538],{"class":33},[27,2419,2421],{"class":29,"line":2420},17,[27,2422,2423],{"class":33},"              {\n",[27,2425,2427,2430,2432,2434,2436,2438],{"class":29,"line":2426},18,[27,2428,2429],{"class":37},"                loader",[27,2431,527],{"class":33},[27,2433,1248],{"class":33},[27,2435,2200],{"class":51},[27,2437,205],{"class":33},[27,2439,538],{"class":33},[27,2441,2443,2446,2448,2451,2454,2456,2460],{"class":29,"line":2442},19,[27,2444,2445],{"class":37},"                options",[27,2447,527],{"class":33},[27,2449,2450],{"class":33}," {",[27,2452,2453],{"class":37}," url",[27,2455,527],{"class":33},[27,2457,2459],{"class":2458},"sbqyR"," false",[27,2461,2462],{"class":33}," }\n",[27,2464,2466],{"class":29,"line":2465},20,[27,2467,2468],{"class":33},"              },\n",[27,2470,2472,2475,2477],{"class":29,"line":2471},21,[27,2473,2474],{"class":33},"              '",[27,2476,2194],{"class":51},[27,2478,2479],{"class":33},"'\n",[27,2481,2483],{"class":29,"line":2482},22,[27,2484,2485],{"class":158},"            ]\n",[27,2487,2489],{"class":29,"line":2488},23,[27,2490,2491],{"class":33},"          }\n",[27,2493,2495],{"class":29,"line":2494},24,[27,2496,2497],{"class":158},"        ]\n",[27,2499,2501],{"class":29,"line":2500},25,[27,2502,2503],{"class":33},"      },\n",[27,2505,2507],{"class":29,"line":2506},26,[27,2508,1376],{"class":243},[27,2510,2512,2514,2516],{"class":29,"line":2511},27,[27,2513,1381],{"class":37},[27,2515,527],{"class":33},[27,2517,1328],{"class":33},[27,2519,2521],{"class":29,"line":2520},28,[27,2522,1390],{"class":243},[27,2524,2526,2528,2530,2532,2534,2536,2538,2540],{"class":29,"line":2525},29,[27,2527,1395],{"class":37},[27,2529,527],{"class":33},[27,2531,1400],{"class":33},[27,2533,1403],{"class":158},[27,2535,661],{"class":33},[27,2537,1408],{"class":51},[27,2539,1349],{"class":33},[27,2541,538],{"class":33},[27,2543,2544],{"class":29,"line":4},[27,2545,1417],{"class":243},[27,2547,2549,2551,2553,2555,2557],{"class":29,"line":2548},31,[27,2550,1422],{"class":37},[27,2552,527],{"class":33},[27,2554,530],{"class":33},[27,2556,1429],{"class":51},[27,2558,656],{"class":33},[27,2560,2562],{"class":29,"line":2561},32,[27,2563,2564],{"class":33},"    },\n",[27,2566,2568],{"class":29,"line":2567},33,[27,2569,2570],{"class":243}," 　　\u002F\u002F ここも追加\n",[27,2572,2574,2577,2579],{"class":29,"line":2573},34,[27,2575,2576],{"class":37},"    plugins",[27,2578,527],{"class":33},[27,2580,2335],{"class":158},[27,2582,2584,2587,2590,2592],{"class":29,"line":2583},35,[27,2585,2586],{"class":33},"      new",[27,2588,2589],{"class":198}," MiniCssExtractPlugin",[27,2591,202],{"class":158},[27,2593,514],{"class":33},[27,2595,2597,2600,2602,2604,2607],{"class":29,"line":2596},36,[27,2598,2599],{"class":37},"          filename",[27,2601,527],{"class":33},[27,2603,205],{"class":33},[27,2605,2606],{"class":51},"style.css",[27,2608,2479],{"class":33},[27,2610,2612,2615],{"class":29,"line":2611},37,[27,2613,2614],{"class":33},"      }",[27,2616,253],{"class":158},[27,2618,2620],{"class":29,"line":2619},38,[27,2621,2622],{"class":158},"    ]\n",[27,2624,2626],{"class":29,"line":2625},39,[27,2627,1441],{"class":33},[397,2629,2631],{"id":2630},"アセットのディレクトリとindexhtmlの変更","アセットのディレクトリとindex.htmlの変更",[13,2633,2634],{},"srcにsassのディレクトリを作成し、そしてややこしいのでjsのフォルダも作りました。",[17,2636,2639],{"className":2637,"code":2638,"language":150},[989],".\n├── dist\n│   ├── index.html\n├── package-lock.json\n├── package.json\n├── src\n│   ├── js\n│   │   ├── functions.js\n│   │   └── main.js\n│   └── sass\n│       ├── component.scss\n│       ├── style.scss\n│       └── variable.scss\n└── webpack.config.js\n",[24,2640,2638],{"__ignoreMap":22},[13,2642,2643],{},"sassの３ファイルは以下のような構成になっています。",[17,2645,2650],{"className":2646,"code":2647,"filename":2648,"language":2649,"meta":22,"style":22},"language-scss shiki shiki-themes material-theme-ocean","$base_color:red;\n$box_size:30px;\n","variacle.scss","scss",[24,2651,2652,2664],{"__ignoreMap":22},[27,2653,2654,2657,2659,2662],{"class":29,"line":30},[27,2655,2656],{"class":158},"$base_color",[27,2658,527],{"class":33},[27,2660,2661],{"class":158},"red",[27,2663,1255],{"class":33},[27,2665,2666,2669,2671,2675],{"class":29,"line":72},[27,2667,2668],{"class":158},"$box_size",[27,2670,527],{"class":33},[27,2672,2674],{"class":2673},"sx098","30px",[27,2676,1255],{"class":33},[17,2678,2681],{"className":2646,"code":2679,"filename":2680,"language":2649,"meta":22,"style":22},".box{\n    background-color: $base_color;\n    width: 20px;\n    height: 20px;\n}\n","component.scss",[24,2682,2683,2693,2706,2718,2729],{"__ignoreMap":22},[27,2684,2685,2687,2691],{"class":29,"line":30},[27,2686,216],{"class":33},[27,2688,2690],{"class":2689},"s5Dmg","box",[27,2692,514],{"class":33},[27,2694,2695,2699,2701,2704],{"class":29,"line":72},[27,2696,2698],{"class":2697},"s6YsC","    background-color",[27,2700,527],{"class":33},[27,2702,2703],{"class":158}," $base_color",[27,2705,1255],{"class":33},[27,2707,2708,2711,2713,2716],{"class":29,"line":137},[27,2709,2710],{"class":2697},"    width",[27,2712,527],{"class":33},[27,2714,2715],{"class":2673}," 20px",[27,2717,1255],{"class":33},[27,2719,2720,2723,2725,2727],{"class":29,"line":169},[27,2721,2722],{"class":2697},"    height",[27,2724,527],{"class":33},[27,2726,2715],{"class":2673},[27,2728,1255],{"class":33},[27,2730,2731],{"class":29,"line":178},[27,2732,1910],{"class":33},[17,2734,2737],{"className":2646,"code":2735,"filename":2736,"language":2649,"meta":22,"style":22},"@import '.\u002Fvariable.scss';\n@import '.\u002Fcomponent.scss';\n","style.scss",[24,2738,2739,2753],{"__ignoreMap":22},[27,2740,2741,2744,2746,2749,2751],{"class":29,"line":30},[27,2742,2743],{"class":1238},"@import",[27,2745,1248],{"class":33},[27,2747,2748],{"class":51},".\u002Fvariable.scss",[27,2750,205],{"class":33},[27,2752,1255],{"class":33},[27,2754,2755,2757,2759,2762,2764],{"class":29,"line":72},[27,2756,2743],{"class":1238},[27,2758,1248],{"class":33},[27,2760,2761],{"class":51},".\u002Fcomponent.scss",[27,2763,205],{"class":33},[27,2765,1255],{"class":33},[13,2767,2768,2770,2771,2773],{},[24,2769,1299],{},"もバンドルされた",[24,2772,2606],{},"を読み込むようにしましょう。",[17,2775,2778],{"className":19,"code":2776,"filename":2777,"language":21,"meta":22,"style":22},"\u003C!DOCTYPE html>\n\u003Chtml>\n    \u003Chead>\n        \u003Cmeta charset=\"utf-8\">\n        \u003Ctitle>webpackの練習\u003C\u002Ftitle>\n　　　　　\u003C!-- これ -->\n        \u003Clink rel=\"stylesheet\" href=\".\u002Fstyle.css\">\n    \u003C\u002Fhead>\n\n    \u003Cbody>\n        \u003Cmain>\n            \u003Cdiv id='app'>\n\n            \u003C\u002Fdiv>\n            \u003Cinput type=\"text\" value=\"\" id=\"inputs\">\n            \u003Cbutton id=\"submmit\" >追加する\u003C\u002Fbutton>\n            \u003Cdiv class='box'>\u003C\u002Fdiv>\n        \u003C\u002Fmain>\n    \u003C\u002Fbody>\n    \u003Cscript src='.\u002Fbundle.js'>\u003C\u002Fscript>\n\u003C\u002Fhtml>\n","dist\u002Findexx.html",[24,2779,2780,2790,2798,2806,2824,2840,2845,2878,2886,2890,2898,2906,2924,2928,2936,2970,2996,3018,3026,3034,3056],{"__ignoreMap":22},[27,2781,2782,2784,2786,2788],{"class":29,"line":30},[27,2783,1037],{"class":33},[27,2785,1040],{"class":37},[27,2787,1043],{"class":41},[27,2789,69],{"class":33},[27,2791,2792,2794,2796],{"class":29,"line":72},[27,2793,34],{"class":33},[27,2795,21],{"class":37},[27,2797,69],{"class":33},[27,2799,2800,2802,2804],{"class":29,"line":137},[27,2801,75],{"class":33},[27,2803,1060],{"class":37},[27,2805,69],{"class":33},[27,2807,2808,2810,2812,2814,2816,2818,2820,2822],{"class":29,"line":169},[27,2809,1067],{"class":33},[27,2811,1070],{"class":37},[27,2813,1073],{"class":41},[27,2815,45],{"class":33},[27,2817,48],{"class":33},[27,2819,1080],{"class":51},[27,2821,48],{"class":33},[27,2823,69],{"class":33},[27,2825,2826,2828,2830,2832,2834,2836,2838],{"class":29,"line":178},[27,2827,1067],{"class":33},[27,2829,1091],{"class":37},[27,2831,155],{"class":33},[27,2833,1096],{"class":158},[27,2835,162],{"class":33},[27,2837,1091],{"class":37},[27,2839,69],{"class":33},[27,2841,2842],{"class":29,"line":185},[27,2843,2844],{"class":243},"　　　　　\u003C!-- これ -->\n",[27,2846,2847,2849,2852,2855,2857,2859,2862,2864,2867,2869,2871,2874,2876],{"class":29,"line":195},[27,2848,1067],{"class":33},[27,2850,2851],{"class":37},"link",[27,2853,2854],{"class":41}," rel",[27,2856,45],{"class":33},[27,2858,48],{"class":33},[27,2860,2861],{"class":51},"stylesheet",[27,2863,48],{"class":33},[27,2865,2866],{"class":41}," href",[27,2868,45],{"class":33},[27,2870,48],{"class":33},[27,2872,2873],{"class":51},".\u002Fstyle.css",[27,2875,48],{"class":33},[27,2877,69],{"class":33},[27,2879,2880,2882,2884],{"class":29,"line":240},[27,2881,1107],{"class":33},[27,2883,1060],{"class":37},[27,2885,69],{"class":33},[27,2887,2888],{"class":29,"line":247},[27,2889,182],{"emptyLinePlaceholder":181},[27,2891,2892,2894,2896],{"class":29,"line":256},[27,2893,75],{"class":33},[27,2895,1122],{"class":37},[27,2897,69],{"class":33},[27,2899,2900,2902,2904],{"class":29,"line":1156},[27,2901,1067],{"class":33},[27,2903,1131],{"class":37},[27,2905,69],{"class":33},[27,2907,2908,2910,2912,2914,2916,2918,2920,2922],{"class":29,"line":1161},[27,2909,1138],{"class":33},[27,2911,427],{"class":37},[27,2913,93],{"class":41},[27,2915,45],{"class":33},[27,2917,205],{"class":33},[27,2919,1149],{"class":51},[27,2921,205],{"class":33},[27,2923,69],{"class":33},[27,2925,2926],{"class":29,"line":1171},[27,2927,182],{"emptyLinePlaceholder":181},[27,2929,2930,2932,2934],{"class":29,"line":1181},[27,2931,1164],{"class":33},[27,2933,427],{"class":37},[27,2935,69],{"class":33},[27,2937,2938,2940,2942,2944,2946,2948,2950,2952,2954,2956,2958,2960,2962,2964,2966,2968],{"class":29,"line":1190},[27,2939,1138],{"class":33},[27,2941,78],{"class":37},[27,2943,104],{"class":41},[27,2945,45],{"class":33},[27,2947,48],{"class":33},[27,2949,150],{"class":51},[27,2951,48],{"class":33},[27,2953,127],{"class":41},[27,2955,45],{"class":33},[27,2957,132],{"class":33},[27,2959,93],{"class":41},[27,2961,45],{"class":33},[27,2963,48],{"class":33},[27,2965,1693],{"class":51},[27,2967,48],{"class":33},[27,2969,69],{"class":33},[27,2971,2972,2974,2976,2978,2980,2982,2984,2986,2988,2990,2992,2994],{"class":29,"line":1216},[27,2973,1138],{"class":33},[27,2975,1704],{"class":37},[27,2977,93],{"class":41},[27,2979,45],{"class":33},[27,2981,48],{"class":33},[27,2983,1713],{"class":51},[27,2985,48],{"class":33},[27,2987,1718],{"class":33},[27,2989,1721],{"class":158},[27,2991,162],{"class":33},[27,2993,1704],{"class":37},[27,2995,69],{"class":33},[27,2997,2998,3000,3002,3004,3006,3008,3010,3012,3014,3016],{"class":29,"line":2420},[27,2999,1138],{"class":33},[27,3001,427],{"class":37},[27,3003,42],{"class":41},[27,3005,45],{"class":33},[27,3007,205],{"class":33},[27,3009,2690],{"class":51},[27,3011,205],{"class":33},[27,3013,1209],{"class":33},[27,3015,427],{"class":37},[27,3017,69],{"class":33},[27,3019,3020,3022,3024],{"class":29,"line":2426},[27,3021,1174],{"class":33},[27,3023,1131],{"class":37},[27,3025,69],{"class":33},[27,3027,3028,3030,3032],{"class":29,"line":2442},[27,3029,1107],{"class":33},[27,3031,1122],{"class":37},[27,3033,69],{"class":33},[27,3035,3036,3038,3040,3042,3044,3046,3048,3050,3052,3054],{"class":29,"line":2465},[27,3037,75],{"class":33},[27,3039,190],{"class":37},[27,3041,1197],{"class":41},[27,3043,45],{"class":33},[27,3045,205],{"class":33},[27,3047,1204],{"class":51},[27,3049,205],{"class":33},[27,3051,1209],{"class":33},[27,3053,190],{"class":37},[27,3055,69],{"class":33},[27,3057,3058,3060,3062],{"class":29,"line":2471},[27,3059,162],{"class":33},[27,3061,21],{"class":37},[27,3063,69],{"class":33},[13,3065,3066,3067,3069],{},"そしてエントリーファイルであった",[24,3068,2284],{},"に以下のようにscssをインポートします。",[17,3071,3074],{"className":1228,"code":3072,"filename":3073,"language":1231,"meta":22,"style":22},"import $ from 'jquery';\nimport funcs from '.\u002Ffunctions';\nimport '~\u002Fsass\u002Fstyle.scss';\n\n$('#submmit').on('click',()=>{\n    return funcs.addNewText('#app','#inputs');\n})\n\n","src\u002Fjs\u002Fmain.js",[24,3075,3076,3092,3108,3121,3125,3157,3187],{"__ignoreMap":22},[27,3077,3078,3080,3082,3084,3086,3088,3090],{"class":29,"line":30},[27,3079,1239],{"class":1238},[27,3081,1242],{"class":158},[27,3083,1245],{"class":1238},[27,3085,1248],{"class":33},[27,3087,345],{"class":51},[27,3089,205],{"class":33},[27,3091,1255],{"class":33},[27,3093,3094,3096,3098,3100,3102,3104,3106],{"class":29,"line":72},[27,3095,1239],{"class":1238},[27,3097,1938],{"class":158},[27,3099,1245],{"class":1238},[27,3101,1248],{"class":33},[27,3103,1945],{"class":51},[27,3105,205],{"class":33},[27,3107,1255],{"class":33},[27,3109,3110,3112,3114,3117,3119],{"class":29,"line":137},[27,3111,1239],{"class":1238},[27,3113,1248],{"class":33},[27,3115,3116],{"class":51},"~\u002Fsass\u002Fstyle.scss",[27,3118,205],{"class":33},[27,3120,1255],{"class":33},[27,3122,3123],{"class":29,"line":169},[27,3124,182],{"emptyLinePlaceholder":181},[27,3126,3127,3129,3131,3133,3135,3137,3139,3141,3143,3145,3147,3149,3151,3153,3155],{"class":29,"line":178},[27,3128,1264],{"class":198},[27,3130,202],{"class":158},[27,3132,205],{"class":33},[27,3134,1964],{"class":51},[27,3136,205],{"class":33},[27,3138,213],{"class":158},[27,3140,216],{"class":33},[27,3142,219],{"class":198},[27,3144,202],{"class":158},[27,3146,205],{"class":33},[27,3148,226],{"class":51},[27,3150,205],{"class":33},[27,3152,1983],{"class":33},[27,3154,1986],{"class":41},[27,3156,514],{"class":33},[27,3158,3159,3161,3163,3165,3167,3169,3171,3173,3175,3177,3179,3181,3183,3185],{"class":29,"line":185},[27,3160,1993],{"class":1238},[27,3162,1996],{"class":158},[27,3164,216],{"class":33},[27,3166,2001],{"class":198},[27,3168,202],{"class":37},[27,3170,205],{"class":33},[27,3172,1271],{"class":51},[27,3174,205],{"class":33},[27,3176,231],{"class":33},[27,3178,205],{"class":33},[27,3180,2016],{"class":51},[27,3182,205],{"class":33},[27,3184,213],{"class":37},[27,3186,1255],{"class":33},[27,3188,3189,3191],{"class":29,"line":195},[27,3190,661],{"class":33},[27,3192,253],{"class":158},[13,3194,3195,3196,3198],{},"こうするとwebpackが",[24,3197,3116],{},"のインポートを捉え、適切にバンドルしてくれます。",[397,3200,3201],{"id":3201},"それぞれのモジュールの説明",[17,3203,3205],{"className":1228,"code":3204,"filename":1303,"language":1231,"meta":22,"style":22},"const MiniCssExtractPlugin = require('mini-css-extract-plugin');\n...\n...\nmodule: {\n    rules: [\n        {\n        test: \u002F\\.(sa|sc|c)ss$\u002F,\n        exclude: \u002Fnode_modules\u002F,\n        use: [\n            MiniCssExtractPlugin.loader,\n            {\n            loader: 'css-loader',\n            options: { url: false }\n            },\n            'sass-loader'\n        ]\n        }\n    ]\n},\n...\nplugins: [\n    new MiniCssExtractPlugin({\n    　　filename: 'style.css'\n    })\n]\n",[24,3206,3207,3229,3234,3238,3247,3256,3261,3294,3309,3318,3329,3334,3349,3366,3371,3380,3384,3389,3393,3398,3402,3411,3422,3435,3441],{"__ignoreMap":22},[27,3208,3209,3211,3213,3215,3217,3219,3221,3223,3225,3227],{"class":29,"line":30},[27,3210,2237],{"class":41},[27,3212,2240],{"class":158},[27,3214,45],{"class":33},[27,3216,2245],{"class":198},[27,3218,202],{"class":158},[27,3220,205],{"class":33},[27,3222,2203],{"class":51},[27,3224,205],{"class":33},[27,3226,213],{"class":158},[27,3228,1255],{"class":33},[27,3230,3231],{"class":29,"line":72},[27,3232,3233],{"class":33},"...\n",[27,3235,3236],{"class":29,"line":137},[27,3237,3233],{"class":33},[27,3239,3240,3243,3245],{"class":29,"line":169},[27,3241,3242],{"class":2689},"module",[27,3244,527],{"class":33},[27,3246,1328],{"class":33},[27,3248,3249,3252,3254],{"class":29,"line":178},[27,3250,3251],{"class":2689},"    rules",[27,3253,527],{"class":33},[27,3255,2335],{"class":37},[27,3257,3258],{"class":29,"line":185},[27,3259,3260],{"class":33},"        {\n",[27,3262,3263,3266,3268,3270,3272,3274,3276,3278,3280,3282,3284,3286,3288,3290,3292],{"class":29,"line":195},[27,3264,3265],{"class":37},"        test",[27,3267,527],{"class":33},[27,3269,2350],{"class":33},[27,3271,2353],{"class":158},[27,3273,202],{"class":33},[27,3275,2358],{"class":51},[27,3277,2361],{"class":33},[27,3279,2364],{"class":51},[27,3281,2361],{"class":33},[27,3283,2369],{"class":51},[27,3285,213],{"class":33},[27,3287,2374],{"class":51},[27,3289,1264],{"class":1238},[27,3291,2379],{"class":33},[27,3293,538],{"class":33},[27,3295,3296,3299,3301,3303,3305,3307],{"class":29,"line":240},[27,3297,3298],{"class":37},"        exclude",[27,3300,527],{"class":33},[27,3302,2350],{"class":33},[27,3304,1295],{"class":51},[27,3306,2379],{"class":33},[27,3308,538],{"class":33},[27,3310,3311,3314,3316],{"class":29,"line":247},[27,3312,3313],{"class":37},"        use",[27,3315,527],{"class":33},[27,3317,2335],{"class":37},[27,3319,3320,3323,3325,3327],{"class":29,"line":256},[27,3321,3322],{"class":158},"            MiniCssExtractPlugin",[27,3324,216],{"class":33},[27,3326,2415],{"class":158},[27,3328,538],{"class":33},[27,3330,3331],{"class":29,"line":1156},[27,3332,3333],{"class":33},"            {\n",[27,3335,3336,3339,3341,3343,3345,3347],{"class":29,"line":1161},[27,3337,3338],{"class":37},"            loader",[27,3340,527],{"class":33},[27,3342,1248],{"class":33},[27,3344,2200],{"class":51},[27,3346,205],{"class":33},[27,3348,538],{"class":33},[27,3350,3351,3354,3356,3358,3360,3362,3364],{"class":29,"line":1171},[27,3352,3353],{"class":37},"            options",[27,3355,527],{"class":33},[27,3357,2450],{"class":33},[27,3359,2453],{"class":37},[27,3361,527],{"class":33},[27,3363,2459],{"class":2458},[27,3365,2462],{"class":33},[27,3367,3368],{"class":29,"line":1181},[27,3369,3370],{"class":33},"            },\n",[27,3372,3373,3376,3378],{"class":29,"line":1190},[27,3374,3375],{"class":33},"            '",[27,3377,2194],{"class":51},[27,3379,2479],{"class":33},[27,3381,3382],{"class":29,"line":1216},[27,3383,2497],{"class":37},[27,3385,3386],{"class":29,"line":2420},[27,3387,3388],{"class":33},"        }\n",[27,3390,3391],{"class":29,"line":2426},[27,3392,2622],{"class":37},[27,3394,3395],{"class":29,"line":2442},[27,3396,3397],{"class":33},"},\n",[27,3399,3400],{"class":29,"line":2465},[27,3401,3233],{"class":33},[27,3403,3404,3407,3409],{"class":29,"line":2471},[27,3405,3406],{"class":2689},"plugins",[27,3408,527],{"class":33},[27,3410,2335],{"class":158},[27,3412,3413,3416,3418,3420],{"class":29,"line":2482},[27,3414,3415],{"class":33},"    new",[27,3417,2589],{"class":198},[27,3419,202],{"class":158},[27,3421,514],{"class":33},[27,3423,3424,3427,3429,3431,3433],{"class":29,"line":2488},[27,3425,3426],{"class":37},"    　　filename",[27,3428,527],{"class":33},[27,3430,1248],{"class":33},[27,3432,2606],{"class":51},[27,3434,2479],{"class":33},[27,3436,3437,3439],{"class":29,"line":2494},[27,3438,250],{"class":33},[27,3440,253],{"class":158},[27,3442,3443],{"class":29,"line":2500},[27,3444,3445],{"class":158},"]\n",[13,3447,3448,3450,3451,3453,3454,3457],{},[24,3449,1303],{},"では特にこの箇所が重要になります。",[24,3452,3242],{},"の中身でsassや画像などのファイルを取り扱えるようになります。",[24,3455,3456],{},"rules","という箇所に取り扱うファイルの拡張子を正規表現で捉え、それに対するローダーの使用などを定義します。",[13,3459,3460,3463,3464,3466,3467,3470],{},[24,3461,3462],{},"MiniCssExtractPlugin","はcssをstyle.cssのような外部ファイルとして出力するために必要なプラグインです。",[24,3465,390],{},"にて",[24,3468,3469],{},"import '~\u002Fsass\u002Fstyle.scss';","という記述があったと思います。このプラグインを使わない場合、webpackでのcssバンドルは外部ファイルでなく、HTMLに直接インライン記述をしようとします。（そうした方が処理と通信が早くなるらしい）",[13,3472,3473,3474,3476,3477,3479,3480,1600,3482,3484,3485,3487,3488,3490,3491,3494,3495,3497,3498,3501],{},"しかし外部ファイル",[24,3475,2606],{},"として今回は出力したいので、",[24,3478,3462],{},"を用いて",[24,3481,390],{},[24,3483,3469],{},"のインポートを参考に、dist配下に",[24,3486,2606],{},"を出力します。ように外部ファイルとして出力します。",[24,3489,3462],{},"のローダーとプラグインに記述しておきます。プラグインの箇所では出力ファイル名を指定します。",[24,3492,3493],{},"filename: 'style.css'","としていますが、指定しない場合はエントリーファイルの",[24,3496,390],{},"から",[24,3499,3500],{},"main.css","が出力されます。",[13,3503,3504],{},"以上でsassのバンドルができるようになりました。早速やってみます。",[17,3506,3509],{"className":3507,"code":3508,"language":150},[989],"npm run build\n",[24,3510,3508],{"__ignoreMap":22},[13,3512,3513],{},"するとdistにstyle.cssができました。中身をみてみるとsassで定義した依存性や変数が当てはまっています。",[279,3515,3516],{"id":3516},"基礎編終了",[13,3518,3519],{},"以上がwebpackを用いたjsファイルのバンドルとsassのコンパイルです。大切なのは5つの概念と必要なモジュールを読み込み、設定することです。次回は忌まわしきIEでjsが動くようにすること、画像の依存性解決、複数条件のバンドルを解説します。",[324,3521,3522],{},"html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s6cf3, html code.shiki .s6cf3{--shiki-default:#89DDFF;--shiki-default-font-style:italic}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .sC9rS, html code.shiki .sC9rS{--shiki-default:#464B5D;--shiki-default-font-style:italic}html pre.shiki code .s7ZW3, html code.shiki .s7ZW3{--shiki-default:#BABED8;--shiki-default-font-style:italic}html pre.shiki code .sbqyR, html code.shiki .sbqyR{--shiki-default:#FF9CAC}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}html pre.shiki code .s5Dmg, html code.shiki .s5Dmg{--shiki-default:#FFCB6B}html pre.shiki code .s6YsC, html code.shiki .s6YsC{--shiki-default:#B2CCD6}",{"title":22,"searchDepth":137,"depth":137,"links":3524},[3525,3526,3527,3528,3531,3536],{"id":927,"depth":72,"text":927},{"id":950,"depth":72,"text":950},{"id":981,"depth":72,"text":982},{"id":1594,"depth":72,"text":1594,"children":3529},[3530],{"id":2071,"depth":137,"text":2071},{"id":2178,"depth":72,"text":2179,"children":3532},[3533,3534,3535],{"id":2185,"depth":137,"text":2186},{"id":2630,"depth":137,"text":2631},{"id":3201,"depth":137,"text":3201},{"id":3516,"depth":72,"text":3516},[862],"webpack基礎とSass・jsのバンドル",{},"\u002Fseries\u002Fwell-study-webpack-1",{"title":876,"description":3538},"well-study-webpack","ちゃんと理解するWebpack5。","series\u002Fwell-study-webpack-1",[344,869],"_mix\u002Flogo-on-white-bg-768x299.png","tvz3F4ALarSfdIbpPp8wmTnJpLE5NRMu4vTA4PHN1Dc",{"id":3549,"title":3550,"body":3551,"category":5055,"createdAt":5056,"description":3550,"extension":337,"index":338,"meta":5057,"navigation":181,"path":5058,"publish":181,"seo":5059,"series":338,"seriesTitle":338,"stem":5060,"tag":5061,"thumbnail":5062,"updatedAt":338,"__hash__":5063},"articles\u002Farticles\u002Fgoogle-map-vuejs.md","Google Maps APIとVue.js を使ってGoogle Map 上にルートを描画する",{"type":10,"value":3552,"toc":5042},[3553,3556,3560,3569,3578,3581,3584,3588,3597,3603,3607,3622,3922,3928,3931,3935,3938,3963,3966,3975,3986,4039,4042,4045,4052,4340,4348,4351,4359,4374,4380,4384,4390,4425,4428,4431,4611,4617,4620,4644,4647,4650,4653,4656,4663,4666,4784,4794,4797,4800,4803,4806,4810,4813,4816,4831,4838,5001,5020,5023,5026,5030,5033,5036,5039],[13,3554,3555],{},"こんにちはjunです。自主開発で地図上に描画する機能を実装するときに「Vueを用いてGoogle Map上にラインを描画できないかな？」と思いその方法を調べた結果、なんとか実装できました。今回の記事ではGoogle Map APIを用いてVue（またはVanilla js）で地図の表示とラインの描画機能を実装したいと思います。Google Map APIの取得の仕方とVue.jsのインストールなどはある程度省きます。",[279,3557,3559],{"id":3558},"google-map-apiを取得する","Google Map APIを取得する",[13,3561,3562,3563,3568],{},"詳細な取得方法は省略しますが、最低限必要なものだけ解説します。自前のGoogleアカウントを用いて、",[442,3564,3567],{"href":3565,"rel":3566},"https:\u002F\u002Fcloud.google.com\u002Fmaps-platform?hl=ja",[446],"Google Cloud Platform","でGoogle Map APIを取得します。",[13,3570,3571,3572,3577],{},"毎月$200分までのリクエストは無料なので開発環境には十分でしょう。そしてweb上にGoogleMapを表示するには",[442,3573,3576],{"href":3574,"rel":3575},"https:\u002F\u002Fconsole.cloud.google.com\u002Fapis\u002Flibrary\u002Fmaps-backend.googleapis.com?",[446],"Maps JavaScript API","が必要になります。",[1571,3579],{":src":3580,":width":1574,":center":1575},"'_mix\u002Fsch-2021-03-07-17.15.12.png'",[13,3582,3583],{},"このAPIを有効にしてAPIキーを手に入れます。まずAPI側のセットアップは以上です。",[279,3585,3587],{"id":3586},"vue側の準備","Vue側の準備",[13,3589,3590,3591,3596],{},"今回は",[442,3592,3595],{"href":3593,"rel":3594},"https:\u002F\u002Fcli.vuejs.org\u002F",[446],"Vue CLI","を用いて開発します。vue cliを用いて適当にプロジェクトを作成します。",[17,3598,3601],{"className":3599,"code":3600,"language":150},[989],"$ vue create vue_map\n",[24,3602,3600],{"__ignoreMap":22},[397,3604,3606],{"id":3605},"api-用のjsを読み込む","api 用のJSを読み込む",[13,3608,3609,3612,3613,1007,3615,3618,3619,3621],{},[24,3610,3611],{},"\u002Fsrc"," 配下にある",[24,3614,390],{},[24,3616,3617],{},"\u002Fpublic","配下にある",[24,3620,1299],{},"に以下のコードを挿入します。",[17,3623,3626],{"className":19,"code":3624,"filename":3625,"language":21,"meta":22,"style":22},"\u003C!DOCTYPE html>\n\u003Chtml lang=\"\">\n  \u003Chead>\n    \u003Cmeta charset=\"utf-8\">\n    \u003Cmeta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    \u003Cmeta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\">\n    \u003Clink rel=\"icon\" href=\"\u003C%= BASE_URL %>favicon.ico\">\n    \n　　\u003C!-- このスクリプトを挿入-->\n    \u003Cscript src=\"http:\u002F\u002Fmaps.google.com\u002Fmaps\u002Fapi\u002Fjs?key=YOUR_API_KEY&language=ja\">\u003C\u002Fscript>\n   \n　　 \u003Ctitle>\u003C%= htmlWebpackPlugin.options.title %>\u003C\u002Ftitle>\n  \u003C\u002Fhead>\n  \u003Cbody>\n    \u003Cnoscript>\n      \u003Cstrong>We're sorry but \u003C%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.\u003C\u002Fstrong>\n    \u003C\u002Fnoscript>\n    \u003Cdiv id=\"app\">\u003C\u002Fdiv>\n    \u003C!-- built files will be auto injected -->\n  \u003C\u002Fbody>\n\u003C\u002Fhtml>\n","public\u002Findex.html",[24,3627,3628,3638,3653,3662,3680,3712,3742,3772,3776,3781,3804,3809,3827,3836,3844,3853,3871,3879,3901,3906,3914],{"__ignoreMap":22},[27,3629,3630,3632,3634,3636],{"class":29,"line":30},[27,3631,1037],{"class":33},[27,3633,1040],{"class":37},[27,3635,1043],{"class":41},[27,3637,69],{"class":33},[27,3639,3640,3642,3644,3647,3649,3651],{"class":29,"line":72},[27,3641,34],{"class":33},[27,3643,21],{"class":37},[27,3645,3646],{"class":41}," lang",[27,3648,45],{"class":33},[27,3650,132],{"class":33},[27,3652,69],{"class":33},[27,3654,3655,3658,3660],{"class":29,"line":137},[27,3656,3657],{"class":33},"  \u003C",[27,3659,1060],{"class":37},[27,3661,69],{"class":33},[27,3663,3664,3666,3668,3670,3672,3674,3676,3678],{"class":29,"line":169},[27,3665,75],{"class":33},[27,3667,1070],{"class":37},[27,3669,1073],{"class":41},[27,3671,45],{"class":33},[27,3673,48],{"class":33},[27,3675,1080],{"class":51},[27,3677,48],{"class":33},[27,3679,69],{"class":33},[27,3681,3682,3684,3686,3689,3691,3693,3696,3698,3701,3703,3705,3708,3710],{"class":29,"line":178},[27,3683,75],{"class":33},[27,3685,1070],{"class":37},[27,3687,3688],{"class":41}," http-equiv",[27,3690,45],{"class":33},[27,3692,48],{"class":33},[27,3694,3695],{"class":51},"X-UA-Compatible",[27,3697,48],{"class":33},[27,3699,3700],{"class":41}," content",[27,3702,45],{"class":33},[27,3704,48],{"class":33},[27,3706,3707],{"class":51},"IE=edge",[27,3709,48],{"class":33},[27,3711,69],{"class":33},[27,3713,3714,3716,3718,3720,3722,3724,3727,3729,3731,3733,3735,3738,3740],{"class":29,"line":185},[27,3715,75],{"class":33},[27,3717,1070],{"class":37},[27,3719,116],{"class":41},[27,3721,45],{"class":33},[27,3723,48],{"class":33},[27,3725,3726],{"class":51},"viewport",[27,3728,48],{"class":33},[27,3730,3700],{"class":41},[27,3732,45],{"class":33},[27,3734,48],{"class":33},[27,3736,3737],{"class":51},"width=device-width,initial-scale=1.0",[27,3739,48],{"class":33},[27,3741,69],{"class":33},[27,3743,3744,3746,3748,3750,3752,3754,3757,3759,3761,3763,3765,3768,3770],{"class":29,"line":195},[27,3745,75],{"class":33},[27,3747,2851],{"class":37},[27,3749,2854],{"class":41},[27,3751,45],{"class":33},[27,3753,48],{"class":33},[27,3755,3756],{"class":51},"icon",[27,3758,48],{"class":33},[27,3760,2866],{"class":41},[27,3762,45],{"class":33},[27,3764,48],{"class":33},[27,3766,3767],{"class":51},"\u003C%= BASE_URL %>favicon.ico",[27,3769,48],{"class":33},[27,3771,69],{"class":33},[27,3773,3774],{"class":29,"line":240},[27,3775,2311],{"class":158},[27,3777,3778],{"class":29,"line":247},[27,3779,3780],{"class":243},"　　\u003C!-- このスクリプトを挿入-->\n",[27,3782,3783,3785,3787,3789,3791,3793,3796,3798,3800,3802],{"class":29,"line":256},[27,3784,75],{"class":33},[27,3786,190],{"class":37},[27,3788,1197],{"class":41},[27,3790,45],{"class":33},[27,3792,48],{"class":33},[27,3794,3795],{"class":51},"http:\u002F\u002Fmaps.google.com\u002Fmaps\u002Fapi\u002Fjs?key=YOUR_API_KEY&language=ja",[27,3797,48],{"class":33},[27,3799,1209],{"class":33},[27,3801,190],{"class":37},[27,3803,69],{"class":33},[27,3805,3806],{"class":29,"line":1156},[27,3807,3808],{"class":158},"   \n",[27,3810,3811,3814,3816,3818,3821,3823,3825],{"class":29,"line":1161},[27,3812,3813],{"class":33},"　　 \u003C",[27,3815,1091],{"class":37},[27,3817,155],{"class":33},[27,3819,3820],{"class":158},"\u003C%= htmlWebpackPlugin.options.title %>",[27,3822,162],{"class":33},[27,3824,1091],{"class":37},[27,3826,69],{"class":33},[27,3828,3829,3832,3834],{"class":29,"line":1171},[27,3830,3831],{"class":33},"  \u003C\u002F",[27,3833,1060],{"class":37},[27,3835,69],{"class":33},[27,3837,3838,3840,3842],{"class":29,"line":1181},[27,3839,3657],{"class":33},[27,3841,1122],{"class":37},[27,3843,69],{"class":33},[27,3845,3846,3848,3851],{"class":29,"line":1190},[27,3847,75],{"class":33},[27,3849,3850],{"class":37},"noscript",[27,3852,69],{"class":33},[27,3854,3855,3858,3860,3862,3865,3867,3869],{"class":29,"line":1216},[27,3856,3857],{"class":33},"      \u003C",[27,3859,290],{"class":37},[27,3861,155],{"class":33},[27,3863,3864],{"class":158},"We're sorry but \u003C%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.",[27,3866,162],{"class":33},[27,3868,290],{"class":37},[27,3870,69],{"class":33},[27,3872,3873,3875,3877],{"class":29,"line":2420},[27,3874,1107],{"class":33},[27,3876,3850],{"class":37},[27,3878,69],{"class":33},[27,3880,3881,3883,3885,3887,3889,3891,3893,3895,3897,3899],{"class":29,"line":2426},[27,3882,75],{"class":33},[27,3884,427],{"class":37},[27,3886,93],{"class":41},[27,3888,45],{"class":33},[27,3890,48],{"class":33},[27,3892,1149],{"class":51},[27,3894,48],{"class":33},[27,3896,1209],{"class":33},[27,3898,427],{"class":37},[27,3900,69],{"class":33},[27,3902,3903],{"class":29,"line":2442},[27,3904,3905],{"class":243},"    \u003C!-- built files will be auto injected -->\n",[27,3907,3908,3910,3912],{"class":29,"line":2465},[27,3909,3831],{"class":33},[27,3911,1122],{"class":37},[27,3913,69],{"class":33},[27,3915,3916,3918,3920],{"class":29,"line":2471},[27,3917,162],{"class":33},[27,3919,21],{"class":37},[27,3921,69],{"class":33},[13,3923,3924,3927],{},[24,3925,3926],{},"http:\u002F\u002Fmaps.google.com\u002Fmaps\u002Fapi\u002Fjs","からAPIのソースを手に入れます。できたらnpmあたりでローカルに予めインストールしたいなと思ったのですが、node.js専用だったりとひとまず開発したかったので、今回は外部ファイルとします。このときVue.jsより早く読み込まれるようにしましょう。",[13,3929,3930],{},"スクリプトのurlにはAPIキーを含めます。公開環境で使用する場合は必ずAPIキーが自サイトのみで使用される条件を設定しておきましょう。でないと第三者に好き勝手にキーを使われて、請求だけはあなたのクレジットカードにきます。",[397,3932,3934],{"id":3933},"プラグインとしてvueに登録しておく","プラグインとしてVueに登録しておく",[13,3936,3937],{},"上記のソースを読み込めばwindow.googleを見ると、今回使用するAPIを操作できるクラスなりメソッドがあります。",[17,3939,3941],{"className":1228,"code":3940,"language":1231,"meta":22,"style":22},"console.lgo(window.google);\n\u002F**\n*{maps: {…}}\n*\u002F\n",[24,3942,3943,3948,3953,3958],{"__ignoreMap":22},[27,3944,3945],{"class":29,"line":30},[27,3946,3947],{},"console.lgo(window.google);\n",[27,3949,3950],{"class":29,"line":72},[27,3951,3952],{},"\u002F**\n",[27,3954,3955],{"class":29,"line":137},[27,3956,3957],{},"*{maps: {…}}\n",[27,3959,3960],{"class":29,"line":169},[27,3961,3962],{},"*\u002F\n",[13,3964,3965],{},"例えば地図を特定のDOMにマウントするときは以下のように呼びます。",[17,3967,3969],{"className":1228,"code":3968,"language":1231,"meta":22,"style":22},"window.google.maps.Map(document.getElementById('map'),{option});\n",[24,3970,3971],{"__ignoreMap":22},[27,3972,3973],{"class":29,"line":30},[27,3974,3968],{},[13,3976,3977,3978,3981,3982,3985],{},"毎回、",[24,3979,3980],{},"window.google.maps","とやるのは面倒なので",[24,3983,3984],{},"this.$gm","と呼び出せるようにプラグイン化しておきます。",[17,3987,3989],{"className":1228,"code":3988,"filename":1230,"language":1231,"meta":22,"style":22},"import Vue from 'vue'\nimport App from '.\u002FApp.vue'\n\n\u002F\u002Fこれ\nVue.prototype.$gm = window.google.maps;\n\nVue.config.productionTip = false\nnew Vue({\n  render: h => h(App),\n}).$mount('#app')\n",[24,3990,3991,3996,4001,4005,4010,4015,4019,4024,4029,4034],{"__ignoreMap":22},[27,3992,3993],{"class":29,"line":30},[27,3994,3995],{},"import Vue from 'vue'\n",[27,3997,3998],{"class":29,"line":72},[27,3999,4000],{},"import App from '.\u002FApp.vue'\n",[27,4002,4003],{"class":29,"line":137},[27,4004,182],{"emptyLinePlaceholder":181},[27,4006,4007],{"class":29,"line":169},[27,4008,4009],{},"\u002F\u002Fこれ\n",[27,4011,4012],{"class":29,"line":178},[27,4013,4014],{},"Vue.prototype.$gm = window.google.maps;\n",[27,4016,4017],{"class":29,"line":185},[27,4018,182],{"emptyLinePlaceholder":181},[27,4020,4021],{"class":29,"line":195},[27,4022,4023],{},"Vue.config.productionTip = false\n",[27,4025,4026],{"class":29,"line":240},[27,4027,4028],{},"new Vue({\n",[27,4030,4031],{"class":29,"line":247},[27,4032,4033],{},"  render: h => h(App),\n",[27,4035,4036],{"class":29,"line":256},[27,4037,4038],{},"}).$mount('#app')\n",[13,4040,4041],{},"これで前準備が完了しました。",[279,4043,4044],{"id":4044},"とりあえず地図をマウントする",[13,4046,4047,4048,4051],{},"準備が整ったのでvueを用いてGoogle Mapをマウントしましょう。インストール直後にある",[24,4049,4050],{},"App.vue","を以下のように書きます。",[17,4053,4058],{"className":4054,"code":4055,"filename":4056,"language":4057,"meta":22,"style":22},"language-vue shiki shiki-themes material-theme-ocean","\u003Ctemplate>\n  \u003Cdiv id=\"app\" class=\"mt-4\">\n      \u003Cdiv id=\"map\">\u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default {\n  name: 'App',\n  data(){\n    return{\n      map:undefined,\n    }\n  },\n  mounted(){\n   const map = new this.$gm.Map(document.getElementById('map'),{\n      center: { lat: 34.855273888888888, lng: 135.30649 }, \u002F\u002F自由な緯度・経度を入力\n      zoom: 10,\n    });\n　　this.map = map;\n  }\n}\n\u003C\u002Fscript>\n","src\u002FApp.vue","vue",[24,4059,4060,4069,4098,4121,4129,4137,4141,4149,4157,4173,4180,4186,4194,4198,4203,4210,4257,4290,4302,4310,4323,4328,4332],{"__ignoreMap":22},[27,4061,4062,4064,4067],{"class":29,"line":30},[27,4063,34],{"class":33},[27,4065,4066],{"class":37},"template",[27,4068,69],{"class":33},[27,4070,4071,4073,4075,4077,4079,4081,4083,4085,4087,4089,4091,4094,4096],{"class":29,"line":72},[27,4072,3657],{"class":33},[27,4074,427],{"class":37},[27,4076,93],{"class":41},[27,4078,45],{"class":33},[27,4080,48],{"class":33},[27,4082,1149],{"class":51},[27,4084,48],{"class":33},[27,4086,42],{"class":41},[27,4088,45],{"class":33},[27,4090,48],{"class":33},[27,4092,4093],{"class":51},"mt-4",[27,4095,48],{"class":33},[27,4097,69],{"class":33},[27,4099,4100,4102,4104,4106,4108,4110,4113,4115,4117,4119],{"class":29,"line":137},[27,4101,3857],{"class":33},[27,4103,427],{"class":37},[27,4105,93],{"class":41},[27,4107,45],{"class":33},[27,4109,48],{"class":33},[27,4111,4112],{"class":51},"map",[27,4114,48],{"class":33},[27,4116,1209],{"class":33},[27,4118,427],{"class":37},[27,4120,69],{"class":33},[27,4122,4123,4125,4127],{"class":29,"line":169},[27,4124,3831],{"class":33},[27,4126,427],{"class":37},[27,4128,69],{"class":33},[27,4130,4131,4133,4135],{"class":29,"line":178},[27,4132,162],{"class":33},[27,4134,4066],{"class":37},[27,4136,69],{"class":33},[27,4138,4139],{"class":29,"line":185},[27,4140,182],{"emptyLinePlaceholder":181},[27,4142,4143,4145,4147],{"class":29,"line":195},[27,4144,34],{"class":33},[27,4146,190],{"class":37},[27,4148,69],{"class":33},[27,4150,4151,4153,4155],{"class":29,"line":240},[27,4152,1773],{"class":1238},[27,4154,1776],{"class":1238},[27,4156,1328],{"class":33},[27,4158,4159,4162,4164,4166,4169,4171],{"class":29,"line":247},[27,4160,4161],{"class":37},"  name",[27,4163,527],{"class":33},[27,4165,1248],{"class":33},[27,4167,4168],{"class":51},"App",[27,4170,205],{"class":33},[27,4172,538],{"class":33},[27,4174,4175,4178],{"class":29,"line":256},[27,4176,4177],{"class":37},"  data",[27,4179,237],{"class":33},[27,4181,4182,4184],{"class":29,"line":1156},[27,4183,1993],{"class":1238},[27,4185,514],{"class":33},[27,4187,4188,4191],{"class":29,"line":1161},[27,4189,4190],{"class":37},"      map",[27,4192,4193],{"class":33},":undefined,\n",[27,4195,4196],{"class":29,"line":1171},[27,4197,1436],{"class":33},[27,4199,4200],{"class":29,"line":1181},[27,4201,4202],{"class":33},"  },\n",[27,4204,4205,4208],{"class":29,"line":1190},[27,4206,4207],{"class":37},"  mounted",[27,4209,237],{"class":33},[27,4211,4212,4215,4218,4220,4223,4226,4229,4231,4234,4236,4239,4241,4244,4246,4248,4250,4252,4254],{"class":29,"line":1216},[27,4213,4214],{"class":41},"   const",[27,4216,4217],{"class":158}," map",[27,4219,1325],{"class":33},[27,4221,4222],{"class":33}," new",[27,4224,4225],{"class":33}," this.",[27,4227,4228],{"class":158},"$gm",[27,4230,216],{"class":33},[27,4232,4233],{"class":198},"Map",[27,4235,202],{"class":37},[27,4237,4238],{"class":158},"document",[27,4240,216],{"class":33},[27,4242,4243],{"class":198},"getElementById",[27,4245,202],{"class":37},[27,4247,205],{"class":33},[27,4249,4112],{"class":51},[27,4251,205],{"class":33},[27,4253,213],{"class":37},[27,4255,4256],{"class":33},",{\n",[27,4258,4259,4262,4264,4266,4269,4271,4274,4276,4279,4281,4284,4287],{"class":29,"line":2420},[27,4260,4261],{"class":37},"      center",[27,4263,527],{"class":33},[27,4265,2450],{"class":33},[27,4267,4268],{"class":37}," lat",[27,4270,527],{"class":33},[27,4272,4273],{"class":2673}," 34.855273888888888",[27,4275,231],{"class":33},[27,4277,4278],{"class":37}," lng",[27,4280,527],{"class":33},[27,4282,4283],{"class":2673}," 135.30649",[27,4285,4286],{"class":33}," },",[27,4288,4289],{"class":243}," \u002F\u002F自由な緯度・経度を入力\n",[27,4291,4292,4295,4297,4300],{"class":29,"line":2426},[27,4293,4294],{"class":37},"      zoom",[27,4296,527],{"class":33},[27,4298,4299],{"class":2673}," 10",[27,4301,538],{"class":33},[27,4303,4304,4306,4308],{"class":29,"line":2442},[27,4305,250],{"class":33},[27,4307,213],{"class":37},[27,4309,1255],{"class":33},[27,4311,4312,4315,4317,4319,4321],{"class":29,"line":2465},[27,4313,4314],{"class":33},"　　this.",[27,4316,4112],{"class":158},[27,4318,1325],{"class":33},[27,4320,4217],{"class":158},[27,4322,1255],{"class":33},[27,4324,4325],{"class":29,"line":2471},[27,4326,4327],{"class":33},"  }\n",[27,4329,4330],{"class":29,"line":2482},[27,4331,1910],{"class":33},[27,4333,4334,4336,4338],{"class":29,"line":2488},[27,4335,162],{"class":33},[27,4337,190],{"class":37},[27,4339,69],{"class":33},[13,4341,4342,4347],{},[442,4343,4346],{"href":4344,"rel":4345},"https:\u002F\u002Fdevelopers.google.com\u002Fmaps\u002Fdocumentation\u002Fjavascript\u002Freference\u002Fmap?hl=ja",[446],"Mapsクラス","のレファランスの通り、第一引数にマウント対象のDOMを入れ、第二引数にはオプションを入れます。DOMが必要なのでmounted()で初期処理を行います。",[13,4349,4350],{},"オプションには",[830,4352,4353,4356],{},[833,4354,4355],{},"center：初期表示の地図の中央座標",[833,4357,4358],{},"zoom：拡大の縮尺（10ぐらいがちょうどいい）",[13,4360,4361,4362,4365,4366,4369,4370,4373],{},"を入れておきましょう。",[24,4363,4364],{},"lat","は緯度、",[24,4367,4368],{},"lng","は経度を示しています。このインスタンスは後で使い回すので",[24,4371,4372],{},"this.map = map"," としてインスタンスを入れておきましょう。ひとまずこの処理を実装してブラウザで見てみましょう。",[17,4375,4378],{"className":4376,"code":4377,"language":150},[989],"$ npm run serve\n",[24,4379,4377],{"__ignoreMap":22},[1571,4381],{":src":4382,":width":4383},"'_mix\u002Fsch-2021-03-07-17.39.26-768x352.png'","'100%'",[13,4385,4386,4389],{},[24,4387,4388],{},"localhost","をみたとき、このようになっていれば問題ありません。ストリートビューや前面表示がない方がいい人はオプションを以下のようにしておきます。",[17,4391,4393],{"className":1228,"code":4392,"filename":1230,"language":1231,"meta":22,"style":22},"new this.$gm.Map(document.getElementById('map'),{\n      center: { lat: 34.855273888888888, lng: 135.30649 },\n      zoom: 10,\n      streetViewControl:false, \u002F\u002F ストリートビュー非表示\n      fullscreenControl:false  \u002F\u002F フルスクリーン非表示\n});\n",[24,4394,4395,4400,4405,4410,4415,4420],{"__ignoreMap":22},[27,4396,4397],{"class":29,"line":30},[27,4398,4399],{},"new this.$gm.Map(document.getElementById('map'),{\n",[27,4401,4402],{"class":29,"line":72},[27,4403,4404],{},"      center: { lat: 34.855273888888888, lng: 135.30649 },\n",[27,4406,4407],{"class":29,"line":137},[27,4408,4409],{},"      zoom: 10,\n",[27,4411,4412],{"class":29,"line":169},[27,4413,4414],{},"      streetViewControl:false, \u002F\u002F ストリートビュー非表示\n",[27,4416,4417],{"class":29,"line":178},[27,4418,4419],{},"      fullscreenControl:false  \u002F\u002F フルスクリーン非表示\n",[27,4421,4422],{"class":29,"line":185},[27,4423,4424],{},"});\n",[279,4426,4427],{"id":4427},"地図にクリックイベントを追加",[13,4429,4430],{},"Google Mapの地図にはクリックやドラッグなどのイベントリスナーを追加できます。特定の位置やマーカーをクリックしたら何やかんやするというのができます。まずは簡単にクリックした地図位置の緯度・経度を取得してみましょう。以下のようにします。",[17,4432,4434],{"className":1228,"code":4433,"filename":1230,"language":1231,"meta":22,"style":22},"\u003Ctemplate>\n  \u003Cdiv id=\"app\" class=\"mt-4\">\n      \u003Cdiv id=\"map\">\u003C\u002Fdiv>\n      {{position}}\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\n\nexport default {\n  name: 'App',\n  data(){\n      return{\n        map:undefined,\n        position:undefined\n      }\n  },\n  methods:{\n   clickOnMap(mapEvent){\n    console.log(mapEvent.latLng.toString())\n    this.position = mapEvent.latLng.toString();\n   }\n  },\n  mounted(){\n   const map = new this.$gm.Map(document.getElementById('map'),{\n      center: { lat: 34.855273888888888, lng: 135.30649 }, \u002F\u002F自由な緯度・経度を入力\n      zoom: 10,\n    });\n\n    \u002F\u002Fリスナーの登録\n    map.addListener('click',(mapsMouseEvent)=>{\n      return this.clickOnMap(mapsMouseEvent);\n    });\n\n　　this.map = map;\n  }\n}\n",[24,4435,4436,4441,4446,4451,4456,4461,4466,4470,4475,4479,4484,4489,4494,4499,4504,4509,4514,4518,4523,4528,4533,4538,4543,4547,4552,4557,4562,4566,4571,4575,4580,4585,4590,4594,4598,4603,4607],{"__ignoreMap":22},[27,4437,4438],{"class":29,"line":30},[27,4439,4440],{},"\u003Ctemplate>\n",[27,4442,4443],{"class":29,"line":72},[27,4444,4445],{},"  \u003Cdiv id=\"app\" class=\"mt-4\">\n",[27,4447,4448],{"class":29,"line":137},[27,4449,4450],{},"      \u003Cdiv id=\"map\">\u003C\u002Fdiv>\n",[27,4452,4453],{"class":29,"line":169},[27,4454,4455],{},"      {{position}}\n",[27,4457,4458],{"class":29,"line":178},[27,4459,4460],{},"  \u003C\u002Fdiv>\n",[27,4462,4463],{"class":29,"line":185},[27,4464,4465],{},"\u003C\u002Ftemplate>\n",[27,4467,4468],{"class":29,"line":195},[27,4469,182],{"emptyLinePlaceholder":181},[27,4471,4472],{"class":29,"line":240},[27,4473,4474],{},"\u003Cscript>\n",[27,4476,4477],{"class":29,"line":247},[27,4478,182],{"emptyLinePlaceholder":181},[27,4480,4481],{"class":29,"line":256},[27,4482,4483],{},"export default {\n",[27,4485,4486],{"class":29,"line":1156},[27,4487,4488],{},"  name: 'App',\n",[27,4490,4491],{"class":29,"line":1161},[27,4492,4493],{},"  data(){\n",[27,4495,4496],{"class":29,"line":1171},[27,4497,4498],{},"      return{\n",[27,4500,4501],{"class":29,"line":1181},[27,4502,4503],{},"        map:undefined,\n",[27,4505,4506],{"class":29,"line":1190},[27,4507,4508],{},"        position:undefined\n",[27,4510,4511],{"class":29,"line":1216},[27,4512,4513],{},"      }\n",[27,4515,4516],{"class":29,"line":2420},[27,4517,4202],{},[27,4519,4520],{"class":29,"line":2426},[27,4521,4522],{},"  methods:{\n",[27,4524,4525],{"class":29,"line":2442},[27,4526,4527],{},"   clickOnMap(mapEvent){\n",[27,4529,4530],{"class":29,"line":2465},[27,4531,4532],{},"    console.log(mapEvent.latLng.toString())\n",[27,4534,4535],{"class":29,"line":2471},[27,4536,4537],{},"    this.position = mapEvent.latLng.toString();\n",[27,4539,4540],{"class":29,"line":2482},[27,4541,4542],{},"   }\n",[27,4544,4545],{"class":29,"line":2488},[27,4546,4202],{},[27,4548,4549],{"class":29,"line":2494},[27,4550,4551],{},"  mounted(){\n",[27,4553,4554],{"class":29,"line":2500},[27,4555,4556],{},"   const map = new this.$gm.Map(document.getElementById('map'),{\n",[27,4558,4559],{"class":29,"line":2506},[27,4560,4561],{},"      center: { lat: 34.855273888888888, lng: 135.30649 }, \u002F\u002F自由な緯度・経度を入力\n",[27,4563,4564],{"class":29,"line":2511},[27,4565,4409],{},[27,4567,4568],{"class":29,"line":2520},[27,4569,4570],{},"    });\n",[27,4572,4573],{"class":29,"line":2525},[27,4574,182],{"emptyLinePlaceholder":181},[27,4576,4577],{"class":29,"line":4},[27,4578,4579],{},"    \u002F\u002Fリスナーの登録\n",[27,4581,4582],{"class":29,"line":2548},[27,4583,4584],{},"    map.addListener('click',(mapsMouseEvent)=>{\n",[27,4586,4587],{"class":29,"line":2561},[27,4588,4589],{},"      return this.clickOnMap(mapsMouseEvent);\n",[27,4591,4592],{"class":29,"line":2567},[27,4593,4570],{},[27,4595,4596],{"class":29,"line":2573},[27,4597,182],{"emptyLinePlaceholder":181},[27,4599,4600],{"class":29,"line":2583},[27,4601,4602],{},"　　this.map = map;\n",[27,4604,4605],{"class":29,"line":2596},[27,4606,4327],{},[27,4608,4609],{"class":29,"line":2611},[27,4610,1910],{},[13,4612,4613,4616],{},[24,4614,4615],{},"map.addListener('click',(mapsMouseEvent)=>{...})","とDOMにイベントを追加する時と似ていますね。第二引数に実行するコールバックを入れます。ちなみにコールバックの引数には自動的に地図上の座標なり他の情報が入っています。",[13,4618,4619],{},"このイベントリスナーには以下のメソッドが実行されるようになっており、クリック位置の経度、緯度を取得します。",[17,4621,4623],{"className":1228,"code":4622,"filename":1230,"language":1231,"meta":22,"style":22},"clickOnMap(mapEvent){\n        console.log(mapEvent.latLng.toString())\n        this.position = mapEvent.latLng.toString();\n},\n",[24,4624,4625,4630,4635,4640],{"__ignoreMap":22},[27,4626,4627],{"class":29,"line":30},[27,4628,4629],{},"clickOnMap(mapEvent){\n",[27,4631,4632],{"class":29,"line":72},[27,4633,4634],{},"        console.log(mapEvent.latLng.toString())\n",[27,4636,4637],{"class":29,"line":137},[27,4638,4639],{},"        this.position = mapEvent.latLng.toString();\n",[27,4641,4642],{"class":29,"line":169},[27,4643,3397],{},[13,4645,4646],{},"クリックすると以下のようになります。（真ん中の赤いのは気にしないでください。）",[1571,4648],{":src":4649,":width":4383},"'_mix\u002Fsch-2021-03-07-18.02.03-768x326.png'",[13,4651,4652],{},"左下に座標が表示されました。ひとまずこれで地図に対するイベントリスナーの登録と情報の取得については理解できたと思います。",[279,4654,4655],{"id":4655},"ラインの描画",[13,4657,4658,4659,4662],{},"それでは今度はクリックした際にラインを描画できるようにします。ラインを描画するためには",[24,4660,4661],{},"google.maps.Polyline","クラスのインスタンスを作成し、先ほどのMapインスタンスにセットすることで描画ができます。",[13,4664,4665],{},"上記の通り地図上の情報を取得できたように、今度は緯度経度の情報を元に地図に何かセットします。まずはMapの初期表示と同時にラインを描画しましょう。",[17,4667,4669],{"className":1228,"code":4668,"filename":1230,"language":1231,"meta":22,"style":22},"...  \nmounted(){\n  const map = new this.$gm.Map(document.getElementById('map'),{\n    center: { lat: 34.855273888888888, lng: 135.30649 },\n    zoom: 10,\n  });\n  map.addListener('click',(mapsMouseEvent)=>{\n    return this.clickOnMap(mapsMouseEvent);\n  });\n  const LINE = new this.$gm.Polyline({\n      path:[\n        { lat: 34.855273888888888, lng: 135.30649 },\n        { lat: 34.854465, lng: 135.8 },\n      ],\n      geodesic: true,\n      strokeColor: \"#FF0000\",\n      strokeOpacity: 1.0,\n      strokeWeight: 2,\n  })\n  LINE.setMap(map);\n  this.map = map;\n} \n...\n",[24,4670,4671,4676,4681,4686,4691,4696,4701,4706,4711,4715,4720,4725,4730,4735,4740,4745,4750,4755,4760,4765,4770,4775,4780],{"__ignoreMap":22},[27,4672,4673],{"class":29,"line":30},[27,4674,4675],{},"...  \n",[27,4677,4678],{"class":29,"line":72},[27,4679,4680],{},"mounted(){\n",[27,4682,4683],{"class":29,"line":137},[27,4684,4685],{},"  const map = new this.$gm.Map(document.getElementById('map'),{\n",[27,4687,4688],{"class":29,"line":169},[27,4689,4690],{},"    center: { lat: 34.855273888888888, lng: 135.30649 },\n",[27,4692,4693],{"class":29,"line":178},[27,4694,4695],{},"    zoom: 10,\n",[27,4697,4698],{"class":29,"line":185},[27,4699,4700],{},"  });\n",[27,4702,4703],{"class":29,"line":195},[27,4704,4705],{},"  map.addListener('click',(mapsMouseEvent)=>{\n",[27,4707,4708],{"class":29,"line":240},[27,4709,4710],{},"    return this.clickOnMap(mapsMouseEvent);\n",[27,4712,4713],{"class":29,"line":247},[27,4714,4700],{},[27,4716,4717],{"class":29,"line":256},[27,4718,4719],{},"  const LINE = new this.$gm.Polyline({\n",[27,4721,4722],{"class":29,"line":1156},[27,4723,4724],{},"      path:[\n",[27,4726,4727],{"class":29,"line":1161},[27,4728,4729],{},"        { lat: 34.855273888888888, lng: 135.30649 },\n",[27,4731,4732],{"class":29,"line":1171},[27,4733,4734],{},"        { lat: 34.854465, lng: 135.8 },\n",[27,4736,4737],{"class":29,"line":1181},[27,4738,4739],{},"      ],\n",[27,4741,4742],{"class":29,"line":1190},[27,4743,4744],{},"      geodesic: true,\n",[27,4746,4747],{"class":29,"line":1216},[27,4748,4749],{},"      strokeColor: \"#FF0000\",\n",[27,4751,4752],{"class":29,"line":2420},[27,4753,4754],{},"      strokeOpacity: 1.0,\n",[27,4756,4757],{"class":29,"line":2426},[27,4758,4759],{},"      strokeWeight: 2,\n",[27,4761,4762],{"class":29,"line":2442},[27,4763,4764],{},"  })\n",[27,4766,4767],{"class":29,"line":2465},[27,4768,4769],{},"  LINE.setMap(map);\n",[27,4771,4772],{"class":29,"line":2471},[27,4773,4774],{},"  this.map = map;\n",[27,4776,4777],{"class":29,"line":2482},[27,4778,4779],{},"} \n",[27,4781,4782],{"class":29,"line":2488},[27,4783,3233],{},[13,4785,4786,4789,4790,4793],{},[24,4787,4788],{},"this.$gm.Polyline","でラインインスタンスを作成し、オプションをオブジェクト形式で指定すればラインの色、パス（２点以上の座標）、太さなどを指定できます。最後に",[24,4791,4792],{},"LINE.setMap(map)","で地図にはめると、以下のように描画されます。",[1571,4795],{":src":4796,":width":4383},"'_mix\u002Fsch-2021-03-10-21.17.39-768x346.png'",[13,4798,4799],{},"地図に赤線が表示されました。今は２点の適当な座標間ですが細かくパスを設定すれば以下のようにも描画できます。",[1571,4801],{":src":4802,":width":2062,":center":1575},"'_mix\u002Fsch-2021-03-10-21.19.41.png'",[13,4804,4805],{},"この座標情報は友人のGPSから引っ張ってきたもので、詳細な座標情報が配列であったのでこれほど細かく描画されます。",[279,4807,4809],{"id":4808},"クリックした任意の地点でラインを描画していく","クリックした任意の地点でラインを描画していく。",[13,4811,4812],{},"初期表示でラインを描画することができたので、次はユーザーが任意の地点をクリックしたらその地点に向かってラインが描画されるようにしましょう。",[13,4814,4815],{},"先ほどクリックイベントから座標情報をしたのを覚えていますか？それを応用します。",[4817,4818,4819,4822,4825,4828],"ol",{},[833,4820,4821],{},"クリック位置から緯度・経度を取得",[833,4823,4824],{},"Google Map用の座標インスタンスに変換",[833,4826,4827],{},"ラインインスタンスにその座標（パス）を追加する。",[833,4829,4830],{},"クリックされたら繰り返す。",[13,4832,4833,4834,4837],{},"クリックイベントに付与した",[24,4835,4836],{},"drawLine","メソッドこんな感じです。",[17,4839,4841],{"className":1228,"code":4840,"filename":1230,"language":1231,"meta":22,"style":22},"data(){\n  return{\n    map:undefined,\n    bounds:undefined,\n    path:[],\n    line:undefined\n  }\n},\nmethods:{\n  drawLine(mapEvent){\n    let latLng = mapEvent.latLng;\n\n    \u002F\u002F １点目をクリック\n    if(this.line === undefined){\n      const newLine = new this.$gm.Polyline({\n        path:[latLng],\n        geodesic: true,\n        strokeColor: \"#FF0000\",\n        strokeOpacity: 1.0,\n        strokeWeight: 2,\n      })\n      newLine.setMap(this.map);\n      this.path.push(latLng);\n      this.line = newLine;\n      return;\n    }\n\n    \u002F\u002F ２点目以降\n    this.path.push(latLng);\n    this.line.setPath([...this.path]);\n    return;\n  },\n},\n",[24,4842,4843,4848,4853,4858,4863,4868,4873,4877,4881,4886,4891,4896,4900,4905,4910,4915,4920,4925,4930,4935,4940,4945,4950,4955,4960,4965,4969,4973,4978,4983,4988,4993,4997],{"__ignoreMap":22},[27,4844,4845],{"class":29,"line":30},[27,4846,4847],{},"data(){\n",[27,4849,4850],{"class":29,"line":72},[27,4851,4852],{},"  return{\n",[27,4854,4855],{"class":29,"line":137},[27,4856,4857],{},"    map:undefined,\n",[27,4859,4860],{"class":29,"line":169},[27,4861,4862],{},"    bounds:undefined,\n",[27,4864,4865],{"class":29,"line":178},[27,4866,4867],{},"    path:[],\n",[27,4869,4870],{"class":29,"line":185},[27,4871,4872],{},"    line:undefined\n",[27,4874,4875],{"class":29,"line":195},[27,4876,4327],{},[27,4878,4879],{"class":29,"line":240},[27,4880,3397],{},[27,4882,4883],{"class":29,"line":247},[27,4884,4885],{},"methods:{\n",[27,4887,4888],{"class":29,"line":256},[27,4889,4890],{},"  drawLine(mapEvent){\n",[27,4892,4893],{"class":29,"line":1156},[27,4894,4895],{},"    let latLng = mapEvent.latLng;\n",[27,4897,4898],{"class":29,"line":1161},[27,4899,182],{"emptyLinePlaceholder":181},[27,4901,4902],{"class":29,"line":1171},[27,4903,4904],{},"    \u002F\u002F １点目をクリック\n",[27,4906,4907],{"class":29,"line":1181},[27,4908,4909],{},"    if(this.line === undefined){\n",[27,4911,4912],{"class":29,"line":1190},[27,4913,4914],{},"      const newLine = new this.$gm.Polyline({\n",[27,4916,4917],{"class":29,"line":1216},[27,4918,4919],{},"        path:[latLng],\n",[27,4921,4922],{"class":29,"line":2420},[27,4923,4924],{},"        geodesic: true,\n",[27,4926,4927],{"class":29,"line":2426},[27,4928,4929],{},"        strokeColor: \"#FF0000\",\n",[27,4931,4932],{"class":29,"line":2442},[27,4933,4934],{},"        strokeOpacity: 1.0,\n",[27,4936,4937],{"class":29,"line":2465},[27,4938,4939],{},"        strokeWeight: 2,\n",[27,4941,4942],{"class":29,"line":2471},[27,4943,4944],{},"      })\n",[27,4946,4947],{"class":29,"line":2482},[27,4948,4949],{},"      newLine.setMap(this.map);\n",[27,4951,4952],{"class":29,"line":2488},[27,4953,4954],{},"      this.path.push(latLng);\n",[27,4956,4957],{"class":29,"line":2494},[27,4958,4959],{},"      this.line = newLine;\n",[27,4961,4962],{"class":29,"line":2500},[27,4963,4964],{},"      return;\n",[27,4966,4967],{"class":29,"line":2506},[27,4968,1436],{},[27,4970,4971],{"class":29,"line":2511},[27,4972,182],{"emptyLinePlaceholder":181},[27,4974,4975],{"class":29,"line":2520},[27,4976,4977],{},"    \u002F\u002F ２点目以降\n",[27,4979,4980],{"class":29,"line":2525},[27,4981,4982],{},"    this.path.push(latLng);\n",[27,4984,4985],{"class":29,"line":4},[27,4986,4987],{},"    this.line.setPath([...this.path]);\n",[27,4989,4990],{"class":29,"line":2548},[27,4991,4992],{},"    return;\n",[27,4994,4995],{"class":29,"line":2561},[27,4996,4202],{},[27,4998,4999],{"class":29,"line":2567},[27,5000,3397],{},[13,5002,5003,5004,5007,5008,5011,5012,5015,5016,5019],{},"１点目の時はインスタンスを作成し",[24,5005,5006],{},"data()","に格納します。そしてクリックごとに",[24,5009,5010],{},"path","に挿入していき、",[24,5013,5014],{},"this.line.setPath([...this.path])","で更新したパスの配列を展開、インスタンスのパスも更新します。するとクリックするたびにその地点に向かって線画描画されます。静止画ですが",[290,5017,5018],{},"VUE","という文字を一筆でかいてみました笑",[1571,5021],{":src":5022,":width":4383},"'_mix\u002Fscp-2021-03-10-21.42.12-768x408.png'",[13,5024,5025],{},"末尾のパスを消して再セットすればやり直し機能ができますし、特定のパスの座標を変更すれば途中の変更もできます。今回は面倒なのでやりませんでした。パスのインスタンスを複数管理できれば、複数ライン・レイヤーなど高度なライン描画を実現できます。詳細な実装をしたらまた記事を書いてみます。",[279,5027,5029],{"id":5028},"vueじゃなくてもよくね","Vueじゃなくてもよくね？",[13,5031,5032],{},"はい。その通りです。ほとんどがGoogle map javascript apiから提供されたオブジェクトをいじっているだけです。なのでVanilla.jsな環境でも普通に描画機能は実装できます。今回はvue.jsを利用して自主開発していたのでvue.jsを使っただけです。",[279,5034,5035],{"id":5035},"いろいろ試してみる",[13,5037,5038],{},"Google Map APIには他にもマーカ・カスタムデザインマーカを入れたり、DOM要素を地図に入れ込むなんてこともできるそうです。これらの描画情報をJSONに格納して、DBに入れておけば地図共有アプリなんかもできそうですね。いろいろやってみます。閲覧ありがとうございました。",[324,5040,5041],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .sC9rS, html code.shiki .sC9rS{--shiki-default:#464B5D;--shiki-default-font-style:italic}html pre.shiki code .s6cf3, html code.shiki .s6cf3{--shiki-default:#89DDFF;--shiki-default-font-style:italic}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}",{"title":22,"searchDepth":137,"depth":137,"links":5043},[5044,5045,5049,5050,5051,5052,5053,5054],{"id":3558,"depth":72,"text":3559},{"id":3586,"depth":72,"text":3587,"children":5046},[5047,5048],{"id":3605,"depth":137,"text":3606},{"id":3933,"depth":137,"text":3934},{"id":4044,"depth":72,"text":4044},{"id":4427,"depth":72,"text":4427},{"id":4655,"depth":72,"text":4655},{"id":4808,"depth":72,"text":4809},{"id":5028,"depth":72,"text":5029},{"id":5035,"depth":72,"text":5035},[862],"2021-03-10",{},"\u002Farticles\u002Fgoogle-map-vuejs",{"title":3550,"description":3550},"articles\u002Fgoogle-map-vuejs",[344,4057],"_mix\u002Fscp-2021-03-10-21.42.12-768x408.png","oZOhHuAchOiO9qVocS-_1TA4WK0zOIDu_l3spKlDDsE",{"id":5065,"title":5066,"body":5067,"category":5163,"createdAt":5164,"description":5066,"extension":337,"index":338,"meta":5165,"navigation":181,"path":5166,"publish":181,"seo":5167,"series":338,"seriesTitle":338,"stem":5168,"tag":5169,"thumbnail":338,"updatedAt":338,"__hash__":5171},"articles\u002Farticles\u002Fwhat-ssg-ssr-spa.md","Nuxt.jsのSPA、SSR、SSGって何？",{"type":10,"value":5068,"toc":5156},[5069,5072,5076,5090,5103,5117,5121,5124,5127,5130,5134,5137,5140,5143,5146,5150,5153],[13,5070,5071],{},"こんにちはjunです。Nuxt.jsを学びはじめにこれら３つの用語が？？？となったのを思い出し、復習がてら解説したいともいます。",[279,5073,5075],{"id":5074},"ssrssgspaって何","SSR・SSG・SPAって何？",[13,5077,5078,5079,5082,5083,5085,5086,5089],{},"SSRは ",[290,5080,5081],{},"S","erver ",[290,5084,5081],{},"ide ",[290,5087,5088],{},"R","enderingのことでVueなどによるjsレンダリングをブラウザではなくサーバーで行うことです。",[13,5091,5092,5093,5095,5096,5098,5099,5102],{},"SSGは ",[290,5094,5081],{},"tatic ",[290,5097,5081],{},"ite ",[290,5100,5101],{},"G","enerateのことで静的書き出しとも言われています。HTML、CSS、JSだけで構成されるファイルを生成することをいいます。",[13,5104,5105,5106,5108,5109,5112,5113,5116],{},"SPAは ",[290,5107,5081],{},"ingle ",[290,5110,5111],{},"P","age ",[290,5114,5115],{},"A","pplication と言われレンダーから何もかもブラウザ側で実行します。初期表示は遅いのでローディングは必須ですが、ページ遷移を感じさせず軽快なUIは非常に魅力的です。SEOを気にせず、操作するブラウザ環境が比較的新しいものに限定するならば選択しても良いと思います。内部的なwebアプリの操作画面などに使用されることが多いです。",[397,5118,5120],{"id":5119},"ssrの利点","SSRの利点",[13,5122,5123],{},"javaScriptは基本的にブラウザ側、つまり閲覧者のマシンで行われます。VueやReactはjsを用いてHTMLをレンダー（構築）するので、ブラウザ側でHTMLが完成します。PHPなどで書かれたwebサービスはサーバー側で完成したHTMLを閲覧者に配信します。",[13,5125,5126],{},"ブラウザでのレンダリングを前提に構築すると、サーバー側のHTMLは最低限の記述しかないためSEOが弱かったり、ブラウザ側の表示に時間がかかったり、古いブラウザや低スペックのスマホだと表示・動作が遅かったりするデメリットがあります。それを解決するのがSSRです。",[13,5128,5129],{},"本来ブラウザで実行されるjsをサーバー側で実行して、完成したHTMLを配信します。この場合、サーバーにはnode.jsが必要になりますが、クローラーは完成したHTMLを読み込み、またユーザー側も高速で表示することができます。",[397,5131,5133],{"id":5132},"ssgの利点","SSGの利点",[13,5135,5136],{},"昨今のwebアプリ・サイトはサーバーからの情報を当て込んだり、Ajaxなどで常に通信していることが多いです。しかしそれはwebアプリ（ブラウザ側）からサーバーへアクセスできる穴が必要で、そこを狙った脆弱性を通じてサーバーが攻撃されることがあります。（webアプリのソースとサーバで動く言語、DBが同居しているので危ない）",[13,5138,5139],{},"他にもサーバー側の実行が遅いと結果的にブラウザの表示が遅くなったり、もっと高速に表示したい場合はSSG、静的書き出しを行います。静的に書き出す際にjsを実行しますし、設計によってはあらかじめサーバーからの取得した情報も読み込んでおきます。",[13,5141,5142],{},"静的に書き出したものはHTML、CSS、JSだけであり基本的にはサーバーと通信して、DBからデータを取ってきたりなどもしません。そのためPHPやDBがインストールされていないサーバーに置いて、配信することが可能です。",[13,5144,5145],{},"配信側にサーバーへ対する攻撃手段が残らないのでセキュリティも格段に上がりますし、表示速度もある程度上がります。これがSSGの利点です。",[397,5147,5149],{"id":5148},"spaの利点","SPAの利点",[13,5151,5152],{},"SPAの利点は実装のしやすさと軽快なUIにあります。レンダーはブラウザで行われるのでサーバーには最低限のHTMLとJSが配信される様にしておけばOKです。SSG・SSRはサーバーにnode.jsを入れたり、webサーバーとあれこりしたりと配信する前に手間がかかります。",[13,5154,5155],{},"SPAはそんなめんどくさいことは必要なく、クラアントに適切なHTMLとJSを渡すだけで解決します。SEOに弱く、また表示と操作がブラウザの性能に依存するので会員制のサイトで、その操作部分などSEOを気にしないサイトやwebアプリであればガンガン使ってもいいと思います。",{"title":22,"searchDepth":137,"depth":137,"links":5157},[5158],{"id":5074,"depth":72,"text":5075,"children":5159},[5160,5161,5162],{"id":5119,"depth":137,"text":5120},{"id":5132,"depth":137,"text":5133},{"id":5148,"depth":137,"text":5149},[334],"2020-11-23",{},"\u002Farticles\u002Fwhat-ssg-ssr-spa",{"title":5066,"description":5066},"articles\u002Fwhat-ssg-ssr-spa",[344,5170],"nuxt","usMpTQM9YqI1I4JGQYjVe7BELZbLtw1Et1nerwUPpek",{"id":5173,"title":5174,"body":5175,"category":6670,"createdAt":6671,"description":6672,"extension":337,"index":30,"meta":6673,"navigation":181,"path":6674,"publish":181,"seo":6675,"series":6676,"seriesTitle":6672,"stem":6677,"tag":6678,"thumbnail":6680,"updatedAt":338,"__hash__":6681},"series\u002Fseries\u002Fheadlesscms-strapi-1.md","headlessCMSのstrapi をnuxt.jsで静的書き出しを行う。その１：strapiとnuxtの連携",{"type":10,"value":5176,"toc":6642},[5177,5180,5183,5203,5206,5209,5220,5224,5230,5233,5239,5243,5246,5252,5255,5262,5273,5280,5283,5286,5292,5298,5301,5308,5311,5314,5317,5320,5323,5329,5339,5345,5348,5356,5359,5362,5365,5368,5372,5375,5381,5384,5390,5393,5396,5399,5402,5413,5416,5420,5423,5426,5429,5432,5435,5438,5444,5447,5450,5456,5459,5462,5465,5468,5485,5488,5491,5494,5497,5500,5503,5506,5509,5512,5515,5518,5521,5524,5528,5531,5534,5537,5541,5544,5547,5550,5553,5556,5567,5574,5578,5585,5588,5591,5595,5598,5902,5905,6033,6047,6050,6056,6062,6068,6082,6086,6089,6516,6519,6524,6527,6530,6538,6542,6545,6586,6593,6599,6606,6609,6612,6615,6618,6632,6636,6639],[13,5178,5179],{},"こんにちはjunです。新しいCMSを採用してみようという動きが社内であり、その過程で私がstrapiというheadlessCMSとnuxt.jsを用いて静的サイトを作成するとこまでやりました。",[13,5181,5182],{},"JAMstack構成でCMSを用いたwebサイトを作成できるということもあり、今までのCMSに変わっていく予感があったので調べてみました。シリーズ記事にして以下",[830,5184,5185,5188,5191,5194,5197,5200],{},[833,5186,5187],{},"headlessCMSとは何か、なぜ使うのか",[833,5189,5190],{},"strapiの使い方",[833,5192,5193],{},"strapiとnuxtの連携方法",[833,5195,5196],{},"strapiでブログライクなデータ構築",[833,5198,5199],{},"nuxtでの構築",[833,5201,5202],{},"静的書き出し",[13,5204,5205],{},"の６つを中心に説明していきます。また完成したフロントとバックのソースはgithubに上げる予定です。今回の記事では上３つを行います。",[13,5207,5208],{},"なお解説で登場するアプリケーションのバージョンは以下の通りです。",[830,5210,5211,5214,5217],{},[833,5212,5213],{},"node.js 13.12.0",[833,5215,5216],{},"strapi 3.2.5",[833,5218,5219],{},"nuxt 2.14.6",[279,5221,5223],{"id":5222},"headlesscmsって何","headlessCMSって何？",[13,5225,5226,5227],{},"headlessCMSのheadというのはビューのことを言います。ビューは今あなたが見ているこのサイトの見た目そのもの、HTMLのことです。そしてその ",[290,5228,5229],{},"ビューの構築がCMSから切り離されているのが、ビューの生成処理が存在しないのがheadlessCMSです。",[13,5231,5232],{},"wordpressなどの一般的なCMSはビューの生成をwordpressという１つのシステムで行っています。リクエストに応じて対応するデータを引っ張ってきて、HTMLを生成してレスポンスとして返します。",[13,5234,5235,5236],{},"しかしheadlessCMSのビューは ",[290,5237,5238],{},"独立したフロントエンド プロジェクトを立てて、そのプロジェクトからAPIを呼び出してデータを取得して、主にjsを用いてHTMLをレンダーします。",[397,5240,5242],{"id":5241},"どうしてヘッドをレスするの","どうしてヘッドをレスするの？",[13,5244,5245],{},"例えばwordpressの場合はHTML構造とデータの出力をwordpressというPHPシステム1つで行います。つまりフロントエンド の構築をPHPで行います。しかしその場合VueやReactを用いたフロントの構築は難しいですし、フロントはwordpressのシステムに従った構築を行う必要があります。最近のUIが優れたサイトや、生産的にフロントを作る場合は大変です。",[13,5247,5248,5249],{},"できるだけフロントはJSで構築したい！でもデータもバックから呼び出したい！そんな時にheadlessCMSを用います。 ",[290,5250,5251],{},"フロントがCMSから分離することでバックエンドの都合や制限を受けにくくなります。（いわゆる疎結合な状態）",[13,5253,5254],{},"例えばwordpressのテーマ構築にはindex.php、page.php、header\u002Ffooter.php、single.phpと言った決まりきったビューファイルがあります。フロント構築はこの制限されたファイルを上手く使用する必要があります。デザインの都合や仕様によっては開発が困難になったりします。",[13,5256,5257,5258,5261],{},"さらに ",[290,5259,5260],{},"Nuxt.jsやNext.jsを用いることで静的書き出しを行うことができます。"," 静的に書き出すことでCMSの課題であった不正ログインや攻撃のリスク、そしてメモリ消費による処理能力ダウンを防ぐことができます。（ただし構成と運用によります）",[830,5263,5264,5267,5270],{},[833,5265,5266],{},"jsフレームワークを用いてリッチで生産性高くフロントエンド を開発できる。",[833,5268,5269],{},"静的書き出しを行うことでセキュリティやパフォーマンスをあげることが可能",[833,5271,5272],{},"バックエンドへAPIを送ることでJSONでデータを受け取れる。JSONならば様々な媒体へデータを配信できるので、再利用性が高い（ブログの内容をスマホアプリに使用するなど）",[13,5274,5275,5276,5279],{},"上記の様な構成を ",[290,5277,5278],{},"JAMStack","、javaScript API Markup Stackといいます。",[279,5281,5282],{"id":5282},"strapiとは",[13,5284,5285],{},"headlessCMSはまだ登場してからまだ日が浅く、また種類も多いです。その中でも特に有名なのはContentful、strapiそして日本ではmicroCMSが人気です。",[13,5287,5288,5291],{},[290,5289,5290],{},"strapiはheadlessCMSの中でもオープンソースでありバックエンドを自由にカスタマイズ可能です。"," ContentfulとmicroCMSはそれらの会社がバックエンドをホスティングしており、指定のAPIキーとエンドポイントへAIPを送ります。その分お金はかかりますが、アーキテクチャによっては完全なサーバレス構成が可能になります。",[13,5293,5294,5295],{},"今回の記事では ",[290,5296,5297],{},"カスタマイズ性に優れたstrapiを用いて説明を行います。",[279,5299,5300],{"id":5300},"アプリの構成",[13,5302,5303,5304,5307],{},"今回の説明では以下の図の様に ",[290,5305,5306],{},"バックエンドにstrapi、フロントエンド にnuxt.jsを用いて構築した上で静的書き出しを行います。"," ローカルのプロジェクトでバックとフロントはポートを分けて実質別のサーバの様にします。そしてデータベースは用意が面倒だったのでMAMPのmysqlを使用しています。",[1571,5309],{":src":5310,":width":1574,":center":1575},"'_mix\u002FheadlessFlow.png'",[13,5312,5313],{},"そしてお知らせの様な定型の項目に沿って入力するコンテンツだけでなく、今のCMSの様に自由に項目を入力できる様なページも作ろうと思います。",[279,5315,5316],{"id":5316},"strapiとnuxtのインストール",[397,5318,5319],{"id":5319},"バックエンドのstrapiをいれる",[13,5321,5322],{},"まずは適当にフロントとバックをいれるheadlessCMSディレクトリを作成",[17,5324,5327],{"className":5325,"code":5326,"language":150},[989],"~ % mkdir headless && cd headless\n",[24,5328,5326],{"__ignoreMap":22},[13,5330,5331,5332,5338],{},"(公式サイト)",[27,5333,5334],{},[442,5335,5336],{"href":5336,"rel":5337},"https:\u002F\u002Fstrapi.io\u002Fdocumentation\u002Fdeveloper-docs\u002Flatest\u002Fsetup-deployment-guides\u002Finstallation\u002Fcli.html#step-1-make-sure-requirements-are-met",[446],"にもある様にnpxを用いてstrapiをインストール。カスタムモードで行います。",[17,5340,5343],{"className":5341,"code":5342,"language":150},[989],"strapinpx create-strapi-app backend\n? Choose your installation type Custom (manual settings)\n? Choose your default database client mysql\n? Database name: strapi\n? Host: 127.0.0.1 (DBのホスト先）\n? Port: 8889 (DBのポート）\n? Username: (DBのユーザー）\n? Password: (DBのパスワード）\n? Enable SSL connection: No(開発環境だから）\n\nCreating a project with custom database options.\nCreating files.\nDependencies installed successfully.\n\nheadless % cd backend && npm run develop\n",[24,5344,5342],{"__ignoreMap":22},[397,5346,5347],{"id":5347},"strapiユーザーを作成する",[13,5349,202,5350,5355],{},[442,5351,5354],{"href":5352,"rel":5353},"http:\u002F\u002Flocalhost:1337\u002Fadmin)%5Bhttp:\u002F\u002Flocalhost:1337\u002Fadmin",[446],"http:\u002F\u002Flocalhost:1337\u002Fadmin)[http:\u002F\u002Flocalhost:1337\u002Fadmin"," ]ビルドをすると1337のポートが開くので指示されたURLにアクセスします。すると以下の様なユーザー作成画面が表示されます。最初に作成されるユーザーはスーパーユーザーになります。",[1571,5357],{":src":5358,":width":1574,":center":1575},"'_mix\u002Fstrapi_create_user-735x1024.png'",[13,5360,5361],{},"ここで任意の名前、アドレス、パスワードを設定します。「READY TO START」を押してログインします。",[1571,5363],{":src":5364,":width":4383},"'_mix\u002Fstapi_fitst.png'",[13,5366,5367],{},"最近になって日本語が当てられる様になったらしいですが、ほとんどが英語のままです。日本語表示はあまり期待しない方がいいです。とりあえずバックエンドはこれでインストール完了です。",[397,5369,5371],{"id":5370},"nuxtjs-のインストール","nuxt.js のインストール",[13,5373,5374],{},"ではフロントを構築するnuxt.jsを入れましょう。",[17,5376,5379],{"className":5377,"code":5378,"language":150},[989],"headless % npx create-nuxt-app frontend\n",[24,5380,5378],{"__ignoreMap":22},[13,5382,5383],{},"静的に書き出すのでUniversalモードでデプロイターゲットは Static を選びましょう。UIは作るのが面倒なのでbootstrap使います。あしからず。",[17,5385,5388],{"className":5386,"code":5387,"language":150},[989],"? Project name: frontend\n? Programming language: JavaScript\n? Package manager: Npm\n? UI framework: Bootstrap Vue\n? Testing framework: None\n? Rendering mode: Universal (SSR \u002F SSG)\n? Deployment target: Static (Static\u002FJAMStack hosting)\n? Development tools: (Press \u003Cspace> to select, \u003Ca> to toggle all, \u003Ci> to invert selection)\n? What is your GitHub username? jun\n? Version control system: Git\n\nSuccessfully created project frontend\n\nheadless % cd frontend && npm run dev\n",[24,5389,5387],{"__ignoreMap":22},[13,5391,5392],{},"npm run dev で開発サーバーを立ち上げます。localhost:3000 にアクセスするとお馴染みのnuxt.jsの画面がみれます。",[279,5394,5395],{"id":5395},"コンテンツタイプを作成する",[13,5397,5398],{},"フロントにデータを表示しようにも、バックにデータがなければ何も始まりません。まずはstrapiで表示するデータを作成します。そこでstrapiで「コンテンツタイプ」というものでコンテンツを定義してデータの型を作成します。",[13,5400,5401],{},"とりあえず以下の様なカラム を持つコンテンツ「お知らせ」を作ります。",[830,5403,5404,5407,5410],{},[833,5405,5406],{},"タイトル",[833,5408,5409],{},"内容（リッチテキスト）",[833,5411,5412],{},"サムネイル",[13,5414,5415],{},"headlessCMSではまず最初に「どんなデータ構成を持つコンテンツを作るか？」ということを考えます。コンテンツを構成する要素を洗い出し、ユーザーが登録する項目を決定します。strapiでの操作はまず画面左の「Contents-Type Builder」をクリックします。画面が切り替わり、「コンテンツタイプ」と書かれているとこの「Create collection type」をクリックして追加します。",[1571,5417],{":src":5418,":width":5419,":center":1575},"'_mix\u002Fsch-2020-11-03-0.00.31-300x197.png'","'400px'",[13,5421,5422],{},"するとコンテンツタイプの名前をいれる様に言われますので、入力します。「お知らせ」なので「newsItem」としておきます。（なぜかタイトル名を「news」にすると400エラーになってしまいます。予約されている可能性あり。）",[1571,5424],{":src":5425,":width":4383},"'_mix\u002Fcreate_conten-768x283.png'",[13,5427,5428],{},"「続ける」をクリックすると次は項目とそのデータ型を登録します。",[1571,5430],{":src":5431,":width":4383},"'_mix\u002Fcreate_news_items-768x480.png'",[13,5433,5434],{},"タイトル→文字（text）、内容→リッチテキスト（Rich Text : Long Text)、サムネイル→画像（Media）とします。例えば「タイトル」は以下の通りです。",[1571,5436],{":src":5437,":width":4383},"'_mix\u002Fcreate_news_title-768x399.png'",[13,5439,5440,5443],{},[24,5441,5442],{},"Name","に入力された値はカラム名となるので日本語入力できません。引き続き入力する場合は「+Add another filed」で続け、項目の設定を終了する場合は「終了」を押します。ひとまず以下の様に設定します。",[1571,5445],{":src":5446,":width":4383},"'_mix\u002Fnews_ct-768x338.png'",[13,5448,5449],{},"そして「保存」を押すとこのコンテンツタイプが登録されます。ターミナルをみてみると",[17,5451,5454],{"className":5452,"code":5453,"language":150},[989],"[2020-11-02T16:43:29.201Z] info File created: \u002FUsers\u002Fjun\u002Fheadless\u002Fbackend\u002Fapi\u002Fnews-items\u002Fconfig\u002Froutes.json\n[2020-11-02T16:43:29.201Z] info File created: \u002FUsers\u002Fjun\u002Fheadless\u002Fbackend\u002Fapi\u002Fnews-items\u002Fcontrollers\u002Fnews-items.js\n[2020-11-02T16:43:29.201Z] info File created: \u002FUsers\u002Fjun\u002Fheadless\u002Fbackend\u002Fapi\u002Fnews-items\u002Fmodels\u002Fnews-items.js\n[2020-11-02T16:43:29.201Z] info File created: \u002FUsers\u002Fjun\u002Fheadless\u002Fbackend\u002Fapi\u002Fnews-items\u002Fmodels\u002Fnews-items.settings.json\n[2020-11-02T16:43:29.201Z] info File created: \u002FUsers\u002Fjun\u002Fheadless\u002Fbackend\u002Fapi\u002Fnews-items\u002Fservices\u002Fnews-items.js\n[2020-11-02T16:43:29.588Z] debug POST \u002Fcontent-type-builder\u002Fcontent-types (650 ms) 201\n[2020-11-02T16:43:29.614Z] info The server is restarting\n",[24,5455,5453],{"__ignoreMap":22},[13,5457,5458],{},"content-typeを作成するためのAPIが POSTされていることがわかります。実はstrapi内でもこの様にREST APIで呼び合ってコンテンツの編集を行っています。",[397,5460,5461],{"id":5461},"感覚的にはテーブルを作成",[13,5463,5464],{},"コンテンツタイプ作成の手順を一通りみてみるとDBでテーブルを作成したり、MVCフレームワークの「モデル」部分の実装と操作が似ています。というか同じです。コンテンツタイプでデータ項目と型を決定します。さらにstrapiではこのコンテンツタイプに応じたエンドポイントも作成してくれます。",[13,5466,5467],{},"headlessCMSはこの様に",[830,5469,5470,5473,5476,5479,5482],{},[833,5471,5472],{},"データテーブルとそのカラム・データ型の作成",[833,5474,5475],{},"エンドポイントの自動作成",[833,5477,5478],{},"エンドポイントの権限管理",[833,5480,5481],{},"自動マイグレーション",[833,5483,5484],{},"コンテンツタイプ（モデル）に応じたレコードの挿入、削除",[13,5486,5487],{},"これらを提供してくれます。",[397,5489,5490],{"id":5490},"お知らせを１件登録する",[13,5492,5493],{},"では早速レコードを１つ登録してみましょう。コレクションタイプ（画面左上）に「newsItem」が登録されているので、それをクリックします。すると以下の様な一覧画面が表示されます。",[1571,5495],{":src":5496,":width":4383},"'_mix\u002Fcreate_news_record-768x246.png'",[13,5498,5499],{},"レコードを登録するために右上の「newsItemを追加」をクリックします。すると先程コンテンツタイプ作成で定義した項目の入力欄が出現します。そこに値を入力します。",[1571,5501],{":src":5502,":width":4383},"'_mix\u002Fnews_input-768x263.png'",[13,5504,5505],{},"テキストエリア はプレーンテキスト、リッチテキストはマークダウンで入力します。画像を定義した箇所はファイルアップローダーが起動するので、ファイルをアップロードできますし、アップロードしたものを選択することもできます。",[13,5507,5508],{},"内容が入力し終わったら画面右上の「保存」をクリックします。そして保存終了後に隣の「Publish」をクリックしてこの登録したレコードが外部に公開できる様になります。",[13,5510,5511],{},"このPublishをクリックしないと、このデータをAPIで呼び出しても404が帰ってきますので注意。",[397,5513,5514],{"id":5514},"公開するためにもう一歩",[13,5516,5517],{},"現段階ではまだ先程登録したnewsItemは外部から呼び出せません。「設定」から「権限とロール」をクリックします。権限とロールでは公開するコンテンツタイプやPOST、 PUT、DELETEの権限設定を行えます。",[1571,5519],{":src":5520,":width":4383},"'_mix\u002Fpermission-768x223'",[13,5522,5523],{},"初期では「Authenticated（認証ユーザー）」「Public（匿名・全てのリクエスト）」のロールがあります。Publicをまずクリックすると、それぞれの権限設定が表示されます。",[1571,5525],{":src":5526,":width":5527,":center":1575},"'_mix\u002Froles-768x634.png'","'600px'",[13,5529,5530],{},"登録したnewsItemの設定があります。ここで findとcount、findoneにチェックを入れます。そして「保存」を押します。 チェックした３つは読み取り専用です。ここでcreateやdeleteにチェックをいれると、APIを通じて誰もがデータを操作できてしまうので気をつけてください。",[13,5532,5533],{},"逆にstrapiで登録したユーザーが外部からデータを操作する場合は Authenticatedでチェックを入れます。私は面倒なのでAuthenticatedは Select all にして、Publicは読み取りのみにしています。",[13,5535,5536],{},"ちなみにロールは３つまで無料です。３ロール以上はなぜか課金が必要です。",[397,5538,5540],{"id":5539},"apiドキュメントプラグインをインストール","APIドキュメントプラグインをインストール",[13,5542,5543],{},"フロントの構築に移る前にAPIドキュメントプラグインを入れておきます。このプラグインは最初から入っておらず、「マーケットプレイス」から無料インストール可能です。このプラグインをいれるとコンテンツタイプに応じたAPIのエンドポイント一覧ドキュメントを自動で作成してくれます。",[1571,5545],{":src":5546,":width":5419,":center":1575},"'_mix\u002Fdocument-300x206.png'",[13,5548,5549],{},"メニューにDocumentationが現れ、その画面から「Open the Documentation」でドキュメントが開きます。登録したnewsItemのAPIもあります。",[279,5551,5552],{"id":5552},"フロントから呼び出してみる",[13,5554,5555],{},"ドキュメントをみてみると",[830,5557,5558,5561,5564],{},[833,5559,5560],{},"\u002Fnews-items で一覧",[833,5562,5563],{},"\u002Fnews-items\u002Fcount で総数",[833,5565,5566],{},"\u002Fnews-items\u002F{id}　でidで紐づいたデータ",[13,5568,5569,5570,5573],{},"を取得することができます。idはstrapiのnewsItemの一覧画面で見れます。数字で表されます。さっき例で作ったのは最初なので ",[24,5571,5572],{},"\u002Fnews-items\u002F1"," で取得できます。",[397,5575,5577],{"id":5576},"talend-api-tester-でテスト","Talend API Tester でテスト",[13,5579,5580,5581,5584],{},"とりあえずAPIがきちんと呼び出せるか、内容が取れるかが確かめたい場合はTalend API Testerなどを使用してAPIをテストすることができます。以下の様に ",[24,5582,5583],{},"http:\u002F\u002Flocalhost:1337\u002Fnews-items\u002F1"," に対してAPIを投げてみると先程の登録した内容がレスポンスにJSONで戻ってきました。",[1571,5586],{":src":5587,":width":4383},"'_mix\u002Fapi_test-768x461.png'",[13,5589,5590],{},"Nuxtでの構築もこのJSONを元に作成します。",[397,5592,5594],{"id":5593},"strapi-nuxt-moduleをインストール","strapi nuxt moduleをインストール",[13,5596,5597],{},"nuxtでAPIベースの呼び出しを行う場合、呼び出し用のプラグインを自作することがあります。例えば以下の様な感じです。",[17,5599,5602],{"className":1228,"code":5600,"filename":5601,"language":1231,"meta":22,"style":22},"export default function(context,inject){\n    \u002F\u002F axios　インスタンス\n    const api = context.$axios.create({\n        timeout:5000,\n        headers:{\n            'Content-Type': 'application\u002Fjson',\n            'X-Requested-With': 'XMLHttpRequest',\n        }\n    })\n\n    \u002F\u002F APIの呼び出し先を設定\n    api.setBaseURL(context.env.apiBaseURL);\n\n \n    api.onRequest(config=>{\n        \u002F\u002F リクエスト時の処理\n    })\n\n\n    api.onResponseError(err=>{\n        switch(err.response.status){\n            \u002F\u002F ステータスごとに異なる処理\n        }\n    })\n\n    \u002F\u002F こうすると $API で上記の設定をしたaxiosインスタンスを呼び出せる。つまり通信処理を共通化できる。\n    inject('API',api);\n}\n","plugins\u002Faxios.js",[24,5603,5604,5625,5630,5657,5669,5677,5697,5717,5721,5727,5731,5736,5764,5768,5773,5791,5796,5802,5806,5810,5828,5851,5856,5860,5866,5870,5875,5898],{"__ignoreMap":22},[27,5605,5606,5608,5610,5613,5615,5618,5620,5623],{"class":29,"line":30},[27,5607,1773],{"class":1238},[27,5609,1776],{"class":1238},[27,5611,5612],{"class":41}," function",[27,5614,202],{"class":33},[27,5616,5617],{"class":1788},"context",[27,5619,231],{"class":33},[27,5621,5622],{"class":1788},"inject",[27,5624,1796],{"class":33},[27,5626,5627],{"class":29,"line":72},[27,5628,5629],{"class":243},"    \u002F\u002F axios　インスタンス\n",[27,5631,5632,5635,5638,5640,5643,5645,5648,5650,5653,5655],{"class":29,"line":137},[27,5633,5634],{"class":41},"    const",[27,5636,5637],{"class":158}," api",[27,5639,1325],{"class":33},[27,5641,5642],{"class":158}," context",[27,5644,216],{"class":33},[27,5646,5647],{"class":158},"$axios",[27,5649,216],{"class":33},[27,5651,5652],{"class":198},"create",[27,5654,202],{"class":37},[27,5656,514],{"class":33},[27,5658,5659,5662,5664,5667],{"class":29,"line":169},[27,5660,5661],{"class":37},"        timeout",[27,5663,527],{"class":33},[27,5665,5666],{"class":2673},"5000",[27,5668,538],{"class":33},[27,5670,5671,5674],{"class":29,"line":178},[27,5672,5673],{"class":37},"        headers",[27,5675,5676],{"class":33},":{\n",[27,5678,5679,5681,5684,5686,5688,5690,5693,5695],{"class":29,"line":185},[27,5680,3375],{"class":33},[27,5682,5683],{"class":37},"Content-Type",[27,5685,205],{"class":33},[27,5687,527],{"class":33},[27,5689,1248],{"class":33},[27,5691,5692],{"class":51},"application\u002Fjson",[27,5694,205],{"class":33},[27,5696,538],{"class":33},[27,5698,5699,5701,5704,5706,5708,5710,5713,5715],{"class":29,"line":195},[27,5700,3375],{"class":33},[27,5702,5703],{"class":37},"X-Requested-With",[27,5705,205],{"class":33},[27,5707,527],{"class":33},[27,5709,1248],{"class":33},[27,5711,5712],{"class":51},"XMLHttpRequest",[27,5714,205],{"class":33},[27,5716,538],{"class":33},[27,5718,5719],{"class":29,"line":240},[27,5720,3388],{"class":33},[27,5722,5723,5725],{"class":29,"line":247},[27,5724,250],{"class":33},[27,5726,253],{"class":37},[27,5728,5729],{"class":29,"line":256},[27,5730,182],{"emptyLinePlaceholder":181},[27,5732,5733],{"class":29,"line":1156},[27,5734,5735],{"class":243},"    \u002F\u002F APIの呼び出し先を設定\n",[27,5737,5738,5741,5743,5746,5748,5750,5752,5755,5757,5760,5762],{"class":29,"line":1161},[27,5739,5740],{"class":158},"    api",[27,5742,216],{"class":33},[27,5744,5745],{"class":198},"setBaseURL",[27,5747,202],{"class":37},[27,5749,5617],{"class":158},[27,5751,216],{"class":33},[27,5753,5754],{"class":158},"env",[27,5756,216],{"class":33},[27,5758,5759],{"class":158},"apiBaseURL",[27,5761,213],{"class":37},[27,5763,1255],{"class":33},[27,5765,5766],{"class":29,"line":1171},[27,5767,182],{"emptyLinePlaceholder":181},[27,5769,5770],{"class":29,"line":1181},[27,5771,5772],{"class":37}," \n",[27,5774,5775,5777,5779,5782,5784,5787,5789],{"class":29,"line":1190},[27,5776,5740],{"class":158},[27,5778,216],{"class":33},[27,5780,5781],{"class":198},"onRequest",[27,5783,202],{"class":37},[27,5785,5786],{"class":1788},"config",[27,5788,1986],{"class":41},[27,5790,514],{"class":33},[27,5792,5793],{"class":29,"line":1216},[27,5794,5795],{"class":243},"        \u002F\u002F リクエスト時の処理\n",[27,5797,5798,5800],{"class":29,"line":2420},[27,5799,250],{"class":33},[27,5801,253],{"class":37},[27,5803,5804],{"class":29,"line":2426},[27,5805,182],{"emptyLinePlaceholder":181},[27,5807,5808],{"class":29,"line":2442},[27,5809,182],{"emptyLinePlaceholder":181},[27,5811,5812,5814,5816,5819,5821,5824,5826],{"class":29,"line":2465},[27,5813,5740],{"class":158},[27,5815,216],{"class":33},[27,5817,5818],{"class":198},"onResponseError",[27,5820,202],{"class":37},[27,5822,5823],{"class":1788},"err",[27,5825,1986],{"class":41},[27,5827,514],{"class":33},[27,5829,5830,5833,5835,5837,5839,5842,5844,5847,5849],{"class":29,"line":2471},[27,5831,5832],{"class":1238},"        switch",[27,5834,202],{"class":37},[27,5836,5823],{"class":158},[27,5838,216],{"class":33},[27,5840,5841],{"class":158},"response",[27,5843,216],{"class":33},[27,5845,5846],{"class":158},"status",[27,5848,213],{"class":37},[27,5850,514],{"class":33},[27,5852,5853],{"class":29,"line":2482},[27,5854,5855],{"class":243},"            \u002F\u002F ステータスごとに異なる処理\n",[27,5857,5858],{"class":29,"line":2488},[27,5859,3388],{"class":33},[27,5861,5862,5864],{"class":29,"line":2494},[27,5863,250],{"class":33},[27,5865,253],{"class":37},[27,5867,5868],{"class":29,"line":2500},[27,5869,182],{"emptyLinePlaceholder":181},[27,5871,5872],{"class":29,"line":2506},[27,5873,5874],{"class":243},"    \u002F\u002F こうすると $API で上記の設定をしたaxiosインスタンスを呼び出せる。つまり通信処理を共通化できる。\n",[27,5876,5877,5880,5882,5884,5887,5889,5891,5894,5896],{"class":29,"line":2511},[27,5878,5879],{"class":198},"    inject",[27,5881,202],{"class":37},[27,5883,205],{"class":33},[27,5885,5886],{"class":51},"API",[27,5888,205],{"class":33},[27,5890,231],{"class":33},[27,5892,5893],{"class":158},"api",[27,5895,213],{"class":37},[27,5897,1255],{"class":33},[27,5899,5900],{"class":29,"line":2520},[27,5901,1910],{"class":33},[13,5903,5904],{},"こうすることでAPIの呼び出し処理を共通化できます。しかしstrapiには上記の様な strapi nuxt module というものがあります。そのモジュールを用いると以下の様に呼び出しが可能です。",[17,5906,5908],{"className":1228,"code":5907,"language":1231,"meta":22,"style":22},"async asyncData(){\n    await this.$strapi.findOne('newsItem',1)\n        .then(async res=>{\n            \u002F\u002F成功時の処理 res にstrapi からのデータが入っている\n        })\n        .catch(err=>{\n　　　　　　　\u002F\u002Fエラー時の処理\n            console.log(error);\n        })\n}\n",[24,5909,5910,5922,5952,5972,5977,5984,5999,6004,6023,6029],{"__ignoreMap":22},[27,5911,5912,5915,5918,5920],{"class":29,"line":30},[27,5913,5914],{"class":158},"async ",[27,5916,5917],{"class":198},"asyncData",[27,5919,1823],{"class":158},[27,5921,514],{"class":33},[27,5923,5924,5927,5929,5932,5934,5937,5939,5941,5944,5946,5948,5950],{"class":29,"line":72},[27,5925,5926],{"class":1238},"    await",[27,5928,4225],{"class":33},[27,5930,5931],{"class":158},"$strapi",[27,5933,216],{"class":33},[27,5935,5936],{"class":198},"findOne",[27,5938,202],{"class":37},[27,5940,205],{"class":33},[27,5942,5943],{"class":51},"newsItem",[27,5945,205],{"class":33},[27,5947,231],{"class":33},[27,5949,88],{"class":2673},[27,5951,253],{"class":37},[27,5953,5954,5957,5960,5962,5965,5968,5970],{"class":29,"line":137},[27,5955,5956],{"class":33},"        .",[27,5958,5959],{"class":198},"then",[27,5961,202],{"class":37},[27,5963,5964],{"class":41},"async",[27,5966,5967],{"class":1788}," res",[27,5969,1986],{"class":41},[27,5971,514],{"class":33},[27,5973,5974],{"class":29,"line":169},[27,5975,5976],{"class":243},"            \u002F\u002F成功時の処理 res にstrapi からのデータが入っている\n",[27,5978,5979,5982],{"class":29,"line":178},[27,5980,5981],{"class":33},"        }",[27,5983,253],{"class":37},[27,5985,5986,5988,5991,5993,5995,5997],{"class":29,"line":185},[27,5987,5956],{"class":33},[27,5989,5990],{"class":198},"catch",[27,5992,202],{"class":37},[27,5994,5823],{"class":1788},[27,5996,1986],{"class":41},[27,5998,514],{"class":33},[27,6000,6001],{"class":29,"line":195},[27,6002,6003],{"class":243},"　　　　　　　\u002F\u002Fエラー時の処理\n",[27,6005,6006,6009,6011,6014,6016,6019,6021],{"class":29,"line":240},[27,6007,6008],{"class":158},"            console",[27,6010,216],{"class":33},[27,6012,6013],{"class":198},"log",[27,6015,202],{"class":37},[27,6017,6018],{"class":158},"error",[27,6020,213],{"class":37},[27,6022,1255],{"class":33},[27,6024,6025,6027],{"class":29,"line":247},[27,6026,5981],{"class":33},[27,6028,253],{"class":37},[27,6030,6031],{"class":29,"line":256},[27,6032,1910],{"class":33},[13,6034,6035,6037,6038,6041,6042],{},[24,6036,5931],{}," というコンテキストが自動で追加され ",[24,6039,6040],{},"findOne('contens-type-name',id)","で呼び出すことができます。loginや認証ユーザーのAPIもあるのでnuxt strapiモジュールを入れておくと構築がしやすくなります。",[442,6043,6046],{"href":6044,"rel":6045},"https:\u002F\u002Fstrapi.nuxtjs.org\u002F",[446],"公式サイト",[13,6048,6049],{},"npm でインストールしておきます。",[17,6051,6054],{"className":6052,"code":6053,"language":150},[989],"npm install @nuxtjs\u002Fstrapi\n",[24,6055,6053],{"__ignoreMap":22},[13,6057,6058,6061],{},[24,6059,6060],{},"nuxt.config.js","で以下の様に追記します。",[17,6063,6066],{"className":6064,"code":6065,"language":150},[989],"modules: [\n  '@nuxtjs\u002Fstrapi',\n],\nstrapi: {\n  url:'http:\u002F\u002Flocalhost:1337'\n},\n",[24,6067,6065],{"__ignoreMap":22},[13,6069,6070,6071,6073,6074,6077,6078,6081],{},"こうするとモジュールが有効になり、",[24,6072,5931],{},"のコンテキストが使用可能になります。",[24,6075,6076],{},"strapi:{}","でオプションが指定できます。strapiがホストされているurlを書いておきます。開発版なので",[24,6079,6080],{},"http:\u002F\u002Flocalhost:1337","にしていますが実際の運用ではenvファイルに書いておくなりしておきましょう。",[397,6083,6085],{"id":6084},"とりあえず-初期画面に表示してみる","とりあえず 初期画面に表示してみる",[13,6087,6088],{},"nuxt.jsをインストールした時に既に存在している pages\u002Findex.vue に登録したニュースをboostrap のcardを用いて表示させてみましょう。以下の様に記述",[17,6090,6093],{"className":4054,"code":6091,"filename":6092,"language":4057,"meta":22,"style":22},"\u003Ctemplate>\n  \u003Cdiv class=\"container\">\n    \u003Cdiv>\n      \u003Cb-card\n        :title=\"title\"\n        :img-src=\"img\"\n        img-top\n        tag=\"article\"\n        style=\"max-width: 20rem;\"\n        class=\"my-5\"\n      >\n        \u003Cb-card-text>\n          {{content}}\n        \u003C\u002Fb-card-text>\n      \u003C\u002Fb-card>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default {\n  data(){\n    return{\n\n    }\n  },\n  async asyncData(context){\n    return await context.$strapi.findOne('news-items',1)\n      .then(res=>{\n        return {\n          title:res.title,\n          img:context.env.envSet.IMG_BASE_URL+res.thumbnail.url,\n          content:res.conctent\n        }\n      })\n      .catch(err=>{\n        console.error(err)\n      })\n  }\n}\n\u003C\u002Fscript>\n","pages\u002Findex.vue",[24,6094,6095,6103,6122,6130,6137,6150,6164,6169,6183,6197,6211,6216,6225,6230,6238,6248,6256,6264,6272,6276,6284,6292,6298,6304,6308,6312,6316,6330,6362,6378,6385,6400,6439,6453,6457,6463,6477,6492,6498,6502,6507],{"__ignoreMap":22},[27,6096,6097,6099,6101],{"class":29,"line":30},[27,6098,34],{"class":33},[27,6100,4066],{"class":37},[27,6102,69],{"class":33},[27,6104,6105,6107,6109,6111,6113,6115,6118,6120],{"class":29,"line":72},[27,6106,3657],{"class":33},[27,6108,427],{"class":37},[27,6110,42],{"class":41},[27,6112,45],{"class":33},[27,6114,48],{"class":33},[27,6116,6117],{"class":51},"container",[27,6119,48],{"class":33},[27,6121,69],{"class":33},[27,6123,6124,6126,6128],{"class":29,"line":137},[27,6125,75],{"class":33},[27,6127,427],{"class":37},[27,6129,69],{"class":33},[27,6131,6132,6134],{"class":29,"line":169},[27,6133,3857],{"class":33},[27,6135,6136],{"class":37},"b-card\n",[27,6138,6139,6142,6144,6146,6148],{"class":29,"line":178},[27,6140,6141],{"class":41},"        :title",[27,6143,45],{"class":33},[27,6145,48],{"class":33},[27,6147,1091],{"class":51},[27,6149,656],{"class":33},[27,6151,6152,6155,6157,6159,6162],{"class":29,"line":185},[27,6153,6154],{"class":41},"        :img-src",[27,6156,45],{"class":33},[27,6158,48],{"class":33},[27,6160,6161],{"class":51},"img",[27,6163,656],{"class":33},[27,6165,6166],{"class":29,"line":195},[27,6167,6168],{"class":41},"        img-top\n",[27,6170,6171,6174,6176,6178,6181],{"class":29,"line":240},[27,6172,6173],{"class":41},"        tag",[27,6175,45],{"class":33},[27,6177,48],{"class":33},[27,6179,6180],{"class":51},"article",[27,6182,656],{"class":33},[27,6184,6185,6188,6190,6192,6195],{"class":29,"line":247},[27,6186,6187],{"class":41},"        style",[27,6189,45],{"class":33},[27,6191,48],{"class":33},[27,6193,6194],{"class":51},"max-width: 20rem;",[27,6196,656],{"class":33},[27,6198,6199,6202,6204,6206,6209],{"class":29,"line":256},[27,6200,6201],{"class":41},"        class",[27,6203,45],{"class":33},[27,6205,48],{"class":33},[27,6207,6208],{"class":51},"my-5",[27,6210,656],{"class":33},[27,6212,6213],{"class":29,"line":1156},[27,6214,6215],{"class":33},"      >\n",[27,6217,6218,6220,6223],{"class":29,"line":1161},[27,6219,1067],{"class":33},[27,6221,6222],{"class":37},"b-card-text",[27,6224,69],{"class":33},[27,6226,6227],{"class":29,"line":1171},[27,6228,6229],{"class":158},"          {{content}}\n",[27,6231,6232,6234,6236],{"class":29,"line":1181},[27,6233,1174],{"class":33},[27,6235,6222],{"class":37},[27,6237,69],{"class":33},[27,6239,6240,6243,6246],{"class":29,"line":1190},[27,6241,6242],{"class":33},"      \u003C\u002F",[27,6244,6245],{"class":37},"b-card",[27,6247,69],{"class":33},[27,6249,6250,6252,6254],{"class":29,"line":1216},[27,6251,1107],{"class":33},[27,6253,427],{"class":37},[27,6255,69],{"class":33},[27,6257,6258,6260,6262],{"class":29,"line":2420},[27,6259,3831],{"class":33},[27,6261,427],{"class":37},[27,6263,69],{"class":33},[27,6265,6266,6268,6270],{"class":29,"line":2426},[27,6267,162],{"class":33},[27,6269,4066],{"class":37},[27,6271,69],{"class":33},[27,6273,6274],{"class":29,"line":2442},[27,6275,182],{"emptyLinePlaceholder":181},[27,6277,6278,6280,6282],{"class":29,"line":2465},[27,6279,34],{"class":33},[27,6281,190],{"class":37},[27,6283,69],{"class":33},[27,6285,6286,6288,6290],{"class":29,"line":2471},[27,6287,1773],{"class":1238},[27,6289,1776],{"class":1238},[27,6291,1328],{"class":33},[27,6293,6294,6296],{"class":29,"line":2482},[27,6295,4177],{"class":37},[27,6297,237],{"class":33},[27,6299,6300,6302],{"class":29,"line":2488},[27,6301,1993],{"class":1238},[27,6303,514],{"class":33},[27,6305,6306],{"class":29,"line":2494},[27,6307,182],{"emptyLinePlaceholder":181},[27,6309,6310],{"class":29,"line":2500},[27,6311,1436],{"class":33},[27,6313,6314],{"class":29,"line":2506},[27,6315,4202],{"class":33},[27,6317,6318,6321,6324,6326,6328],{"class":29,"line":2511},[27,6319,6320],{"class":41},"  async",[27,6322,6323],{"class":37}," asyncData",[27,6325,202],{"class":33},[27,6327,5617],{"class":1788},[27,6329,1796],{"class":33},[27,6331,6332,6334,6337,6339,6341,6343,6345,6347,6349,6351,6354,6356,6358,6360],{"class":29,"line":2520},[27,6333,1993],{"class":1238},[27,6335,6336],{"class":1238}," await",[27,6338,5642],{"class":158},[27,6340,216],{"class":33},[27,6342,5931],{"class":158},[27,6344,216],{"class":33},[27,6346,5936],{"class":198},[27,6348,202],{"class":37},[27,6350,205],{"class":33},[27,6352,6353],{"class":51},"news-items",[27,6355,205],{"class":33},[27,6357,231],{"class":33},[27,6359,88],{"class":2673},[27,6361,253],{"class":37},[27,6363,6364,6367,6369,6371,6374,6376],{"class":29,"line":2525},[27,6365,6366],{"class":33},"      .",[27,6368,5959],{"class":198},[27,6370,202],{"class":37},[27,6372,6373],{"class":1788},"res",[27,6375,1986],{"class":41},[27,6377,514],{"class":33},[27,6379,6380,6383],{"class":29,"line":4},[27,6381,6382],{"class":1238},"        return",[27,6384,1328],{"class":33},[27,6386,6387,6390,6392,6394,6396,6398],{"class":29,"line":2548},[27,6388,6389],{"class":37},"          title",[27,6391,527],{"class":33},[27,6393,6373],{"class":158},[27,6395,216],{"class":33},[27,6397,1091],{"class":158},[27,6399,538],{"class":33},[27,6401,6402,6405,6407,6409,6411,6413,6415,6418,6420,6423,6425,6427,6429,6432,6434,6437],{"class":29,"line":2561},[27,6403,6404],{"class":37},"          img",[27,6406,527],{"class":33},[27,6408,5617],{"class":158},[27,6410,216],{"class":33},[27,6412,5754],{"class":158},[27,6414,216],{"class":33},[27,6416,6417],{"class":158},"envSet",[27,6419,216],{"class":33},[27,6421,6422],{"class":158},"IMG_BASE_URL",[27,6424,1853],{"class":33},[27,6426,6373],{"class":158},[27,6428,216],{"class":33},[27,6430,6431],{"class":158},"thumbnail",[27,6433,216],{"class":33},[27,6435,6436],{"class":158},"url",[27,6438,538],{"class":33},[27,6440,6441,6444,6446,6448,6450],{"class":29,"line":2567},[27,6442,6443],{"class":37},"          content",[27,6445,527],{"class":33},[27,6447,6373],{"class":158},[27,6449,216],{"class":33},[27,6451,6452],{"class":158},"conctent\n",[27,6454,6455],{"class":29,"line":2573},[27,6456,3388],{"class":33},[27,6458,6459,6461],{"class":29,"line":2583},[27,6460,2614],{"class":33},[27,6462,253],{"class":37},[27,6464,6465,6467,6469,6471,6473,6475],{"class":29,"line":2596},[27,6466,6366],{"class":33},[27,6468,5990],{"class":198},[27,6470,202],{"class":37},[27,6472,5823],{"class":1788},[27,6474,1986],{"class":41},[27,6476,514],{"class":33},[27,6478,6479,6482,6484,6486,6488,6490],{"class":29,"line":2611},[27,6480,6481],{"class":158},"        console",[27,6483,216],{"class":33},[27,6485,6018],{"class":198},[27,6487,202],{"class":37},[27,6489,5823],{"class":158},[27,6491,253],{"class":37},[27,6493,6494,6496],{"class":29,"line":2619},[27,6495,2614],{"class":33},[27,6497,253],{"class":37},[27,6499,6500],{"class":29,"line":2625},[27,6501,4327],{"class":33},[27,6503,6505],{"class":29,"line":6504},40,[27,6506,1910],{"class":33},[27,6508,6510,6512,6514],{"class":29,"line":6509},41,[27,6511,162],{"class":33},[27,6513,190],{"class":37},[27,6515,69],{"class":33},[13,6517,6518],{},"いらないものはとりあえず省きましました。コードの解説をします。",[6520,6521,6523],"h4",{"id":6522},"asyncdataでapiを呼ぶ","asyncDataでAPIを呼ぶ",[13,6525,6526],{},"asyncDataというのはssr・ssgモードのpages配下で利用できます。これはcreatedよりも前、コンポーネントインスタンスが完成する際に呼び出せます。このasycData内の処理が終わってからコンポーネントが作成されます。",[13,6528,6529],{},"静的出力・ssrの場合はasycDataに処理を書くことでサーバーサイドでAPIの呼び出し、そしてコンポーネントのdataへのマージを行ってくれます。createdでやると、静的出力した際になんと静的htmlからAPIを呼び出してしまいます。",[13,6531,6532,6533],{},"詳しくは",[442,6534,6537],{"href":6535,"rel":6536},"https:\u002F\u002Fnuxtjs.org\u002Fdocs\u002F2.x\u002Ffeatures\u002Fdata-fetching#async-data",[446],"こちら",[6520,6539,6541],{"id":6540},"画像のurlはちょっと注意","画像のURLはちょっと注意",[13,6543,6544],{},"APIで呼び出した際に、サムネイルのURLは以下の様になっていました。",[17,6546,6548],{"className":494,"code":6547,"language":497,"meta":22,"style":22},"thumbnail:{\n...\nurl\": \"\u002Fuploads\u002Fnuxt_strapi_8a86d72c14.png\",\n...\n}\n",[24,6549,6550,6557,6561,6578,6582],{"__ignoreMap":22},[27,6551,6552,6555],{"class":29,"line":30},[27,6553,6554],{"class":158},"thumbnail:",[27,6556,514],{"class":33},[27,6558,6559],{"class":29,"line":72},[27,6560,3233],{"class":158},[27,6562,6563,6565,6567,6569,6571,6574,6576],{"class":29,"line":137},[27,6564,6436],{"class":158},[27,6566,48],{"class":33},[27,6568,511],{"class":41},[27,6570,48],{"class":33},[27,6572,6573],{"class":158},"\u002Fuploads\u002Fnuxt_strapi_8a86d72c14.png",[27,6575,48],{"class":33},[27,6577,538],{"class":41},[27,6579,6580],{"class":29,"line":169},[27,6581,3233],{"class":41},[27,6583,6584],{"class":29,"line":178},[27,6585,1910],{"class":41},[13,6587,6588,6589,6592],{},"ローカルの環境では画像にアクセスできません。（実物が",[24,6590,6591],{},"http:\u002F\u002Flocalhost:1337\u002F","配下にいるため）そのためenvを用いて",[17,6594,6597],{"className":6595,"code":6596,"language":150},[989],"img:context.env.envSet.IMG_BASE_URL+res.thumbnail.url,\n",[24,6598,6596],{"__ignoreMap":22},[13,6600,6601,6602,6605],{},"としてURLの前に",[24,6603,6604],{},"env.envSet.IMG_BASE_URL = http:\u002F\u002Flocalhost:1337","が出力される様にしています。そして表示されたページが以下の感じです。",[1571,6607],{":src":6608,":width":5419,":center":1575},"'_mix\u002Ffinal-768x414.png'",[13,6610,6611],{},"きちんとデータ・画像がとってこれていますし、bootstrapにはめ込まれていい感じです。",[6520,6613,6614],{"id":6614},"データ取得できない場合",[13,6616,6617],{},"データが取得できない場合以下のことを確認",[830,6619,6620,6623,6626,6629],{},[833,6621,6622],{},"レコードは「Publish」になっているか",[833,6624,6625],{},"権限とロールでPublicの読み取り権限にチェックを入れているか",[833,6627,6628],{},"エンドポイントを間違っていないか",[833,6630,6631],{},"strapiのサーバを落としていないか",[279,6633,6635],{"id":6634},"次回は","次回は…",[13,6637,6638],{},"今回の記事ではstrapiとNuxtのインストール・連携まで行いました。次回の記事からはブログ構成を構築するためのstrapiで「ページ」データの構築、そしてnuxtでの出力方法を書いていきます。",[324,6640,6641],{},"html pre.shiki code .s6cf3, html code.shiki .s6cf3{--shiki-default:#89DDFF;--shiki-default-font-style:italic}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .s7ZW3, html code.shiki .s7ZW3{--shiki-default:#BABED8;--shiki-default-font-style:italic}html pre.shiki code .sC9rS, html code.shiki .sC9rS{--shiki-default:#464B5D;--shiki-default-font-style:italic}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":22,"searchDepth":137,"depth":137,"links":6643},[6644,6647,6648,6649,6654,6660,6669],{"id":5222,"depth":72,"text":5223,"children":6645},[6646],{"id":5241,"depth":137,"text":5242},{"id":5282,"depth":72,"text":5282},{"id":5300,"depth":72,"text":5300},{"id":5316,"depth":72,"text":5316,"children":6650},[6651,6652,6653],{"id":5319,"depth":137,"text":5319},{"id":5347,"depth":137,"text":5347},{"id":5370,"depth":137,"text":5371},{"id":5395,"depth":72,"text":5395,"children":6655},[6656,6657,6658,6659],{"id":5461,"depth":137,"text":5461},{"id":5490,"depth":137,"text":5490},{"id":5514,"depth":137,"text":5514},{"id":5539,"depth":137,"text":5540},{"id":5552,"depth":72,"text":5552,"children":6661},[6662,6663,6664],{"id":5576,"depth":137,"text":5577},{"id":5593,"depth":137,"text":5594},{"id":6084,"depth":137,"text":6085,"children":6665},[6666,6667,6668],{"id":6522,"depth":169,"text":6523},{"id":6540,"depth":169,"text":6541},{"id":6614,"depth":169,"text":6614},{"id":6634,"depth":72,"text":6635},[862],"2020-11-03","headlessCMSのstrapi をnuxt.jsで静的書き出しを行う。",{},"\u002Fseries\u002Fheadlesscms-strapi-1",{"title":5174,"description":6672},"headlesscms-strapi","series\u002Fheadlesscms-strapi-1",[344,5170,6679],"headlesscms","_mix\u002Fnuxt_strapi-768x768.png","AUAh3SzPOmWReCngs8NQezSo25YbFgc0ddNoPwO0euE",{"id":6683,"title":6684,"body":6685,"category":10040,"createdAt":10041,"description":10042,"extension":337,"index":137,"meta":10043,"navigation":181,"path":10044,"publish":181,"seo":10045,"series":10046,"seriesTitle":10047,"stem":10048,"tag":10049,"thumbnail":10051,"updatedAt":338,"__hash__":10052},"series\u002Fseries\u002Fconcrete5vue-3.md","Concrete5にVueCLIを使ってUIを構築する。3【編集画面と一覧画面】",{"type":10,"value":6686,"toc":10017},[6687,6695,6698,6702,6709,6712,6716,6719,6725,6732,6801,6812,6819,6826,6830,6833,7193,7198,7202,7209,7286,7295,7302,7305,7308,7338,7345,7465,7471,7717,7720,7727,7741,7744,7751,7754,7757,7760,7858,7861,7864,7869,8033,8040,8043,8046,8049,8052,8055,8058,8061,8064,9390,9393,9404,9407,9410,9413,9417,9422,9554,9558,9561,9567,9594,9597,9853,9859,9862,9865,9868,9871,9874,9877,9880,9883,9886,9889,9892,9895,9898,9901,9904,10002,10005,10008,10011,10014],[13,6688,6689,6690,6694],{},"こんにちはjunです。",[442,6691,6693],{"href":6692},"\u002Fseries\u002Fconcrete5vue-2","Concrete5にVueCLIを使ってUIを構築する 2","の記事の続きを書いていきます。前回の記事ではフォームの作成・登録まで行いました。この記事では登録したデータの編集とファイルマネージャーのvueコンポーネント化を行っていきます。",[13,6696,6697],{},"編集画面は追加画面とコンポーネントを共有し、データベースからAjaxでデータを取得してコンポーネントに代入をします。そして登録したデータの操作が行える一覧画面を作成します。",[279,6699,6701],{"id":6700},"編集画面のレンダーとajax設定","編集画面のレンダーとAjax設定",[13,6703,6704,6705,6708],{},"まずは編集画面から作成していきます。編集は追加と違ってデータベースからデータを取得して、初期値として当てはめる必要があります。PHPであれば",[24,6706,6707],{},"value=\"\u003C?php echo $data?>\"","みたいに挿入することで簡単に実現できますが、vueを使うとなれば一捻り必要です。",[13,6710,6711],{},"jsでフロントを構築する場合、基本的にDBのデータはAjaxを用いて再度サーバーにデータを要求します。concrete5でもAjaxとそのエントリーを実装することができます。まずエントリーの設定からやってみましょう。",[397,6713,6715],{"id":6714},"ajaxエントリーrest-apiの作成","Ajaxエントリー(REST API)の作成",[13,6717,6718],{},"今回はシングルページのコントローラーを用いてAjax用のエントリーを実装します。Ajaxでデータを取得する際は基本的に取得用のURLを作成して、そのURLに対してAjaxを飛ばしてJSONデータをレスポンスとして受け取るのが定石です。",[17,6720,6723],{"className":6721,"code":6722,"language":150},[989],"vuetest\n├── controller.php \u002F\u002Fこれはパッケージのcontroller\n├── controllers\n│   └── single_page\n│       └── dashboard\n│           └── vuetest.php\n└── single_pages\n    └── dashboard\n        └── vuetest\n            ├── add.php   \u002F\u002F追加画面シングルページ\n            ├── edit.php  \u002F\u002F編集画面シングルページ\n            └── view.php\n",[24,6724,6722],{"__ignoreMap":22},[13,6726,6727,6728,6731],{},"では ",[24,6729,6730],{},"vuetest\u002Fcontrollers\u002Fsingle_page\u002Fdashboard\u002Fvuetest.php"," に以下のように記述します。",[17,6733,6738],{"className":6734,"code":6735,"filename":6736,"language":6737,"meta":22,"style":22},"language-php shiki shiki-themes material-theme-ocean","public function edit($id=null){\n    if($id==null) return Redirect::to('\u002Fdashboard\u002Fvuetest\u002F')->send();\n\n    if($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'){\n        $db = Database::connection();\n        $db->Execute(\"START TRANSACTION\");\n        $result = $db->fetchAll('SELECT * FROM album WHERE ID = ?',array($id));\n        $db->Execute(\"COMMIT\");\n        return Core::make('helper\u002Fajax')->sendResult($result);\n    }\n\n    $this->render('\u002Fdashboard\u002Fvuetest\u002Fedit');\n}\n","vuetest.php","php",[24,6739,6740,6745,6750,6754,6759,6764,6769,6774,6779,6784,6788,6792,6797],{"__ignoreMap":22},[27,6741,6742],{"class":29,"line":30},[27,6743,6744],{},"public function edit($id=null){\n",[27,6746,6747],{"class":29,"line":72},[27,6748,6749],{},"    if($id==null) return Redirect::to('\u002Fdashboard\u002Fvuetest\u002F')->send();\n",[27,6751,6752],{"class":29,"line":137},[27,6753,182],{"emptyLinePlaceholder":181},[27,6755,6756],{"class":29,"line":169},[27,6757,6758],{},"    if($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'){\n",[27,6760,6761],{"class":29,"line":178},[27,6762,6763],{},"        $db = Database::connection();\n",[27,6765,6766],{"class":29,"line":185},[27,6767,6768],{},"        $db->Execute(\"START TRANSACTION\");\n",[27,6770,6771],{"class":29,"line":195},[27,6772,6773],{},"        $result = $db->fetchAll('SELECT * FROM album WHERE ID = ?',array($id));\n",[27,6775,6776],{"class":29,"line":240},[27,6777,6778],{},"        $db->Execute(\"COMMIT\");\n",[27,6780,6781],{"class":29,"line":247},[27,6782,6783],{},"        return Core::make('helper\u002Fajax')->sendResult($result);\n",[27,6785,6786],{"class":29,"line":256},[27,6787,1436],{},[27,6789,6790],{"class":29,"line":1156},[27,6791,182],{"emptyLinePlaceholder":181},[27,6793,6794],{"class":29,"line":1161},[27,6795,6796],{},"    $this->render('\u002Fdashboard\u002Fvuetest\u002Fedit');\n",[27,6798,6799],{"class":29,"line":1171},[27,6800,1910],{},[13,6802,6803,6804,6807,6808,6811],{},"まずは編集画面を ",[24,6805,6806],{},"\u002Fdashboard\u002Fvuetest\u002Fedit"," というURLで表示できるようにします。このメソッドではパラメータがあるので ",[24,6809,6810],{},"\u002Fdashboard\u002Fvuetest\u002Fedit\u002F2"," のようなURLを送信できます。数字の部分はアルバムのDBでのIDとします。（アルバムID）",[13,6813,6814,6815,6818],{},"つまり",[24,6816,6817],{},"\u002Fdashboard\u002Fvuetest\u002Fedit\u002F"," にアルバムIDを加えて送信することで、指定したIDのデータを表示できるようにします。そしてリクエストがXMLHttpRequestつまり、AjaxであればデータJSONで返し、そうでなければ404を返すようにします。",[13,6820,6821,6822,6825],{},"リクエストの種別を限定することで、ブラウザなどでAjax専用の ",[24,6823,6824],{},"\u002Fdashboard\u002Fvuetest\u002FloadData"," というURLを叩いても404しか表示されません。",[427,6827,6829],{"className":6828},[430,882],"\nこのようなAPIを作成するときは、データを差し出してもいいユーザーなのかをチェックする必要があります。しかし今回のような管理画面配下のページ（dashboard）でルーティングする場合は特に気にする必要はありません。\n",[13,6831,6832],{},"\u002Fdashboard\u002F* 配下はリクエストからログインユーザーであるかをチェックしており、ログインユーザーでないリクエストの場合、ログインページのHTMLが返されます。実際にcurlで上記のURLを打ってみると",[17,6834,6836],{"className":19,"code":6835,"language":21,"meta":22,"style":22},"\u003C!DOCTYPE html>\n\u003Chtml>\n\u003Chead>\n    \u003Clink rel=\"stylesheet\" type=\"text\u002Fcss\" href=\"\u002Fconcrete\u002Fthemes\u002Fconcrete\u002Fmain.css\" \u002F>\n    \n\u003Ctitle>ログイン :: c5test\u003C\u002Ftitle>\n\n\u003Cmeta http-equiv=\"content-type\" content=\"text\u002Fhtml; charset=UTF-8\"\u002F>\n\u003Cmeta name=\"generator\" content=\"concrete5 - 8.5.4\"\u002F>\n\u003Clink rel=\"canonical\" href=\"http:\u002F\u002Flocalhost:8888\u002Flogin\">\n\u003Cscript type=\"text\u002Fjavascript\">\n    var CCM_DISPATCHER_FILENAME = \"\u002Findex.php\";\n    var CCM_CID = 174;\n    var CCM_EDIT_MODE = false;\n    var CCM_ARRANGE_MODE = false;\n    var CCM_IMAGE_PATH = \"\u002Fconcrete\u002Fimages\";\n    var CCM_TOOLS_PATH = \"\u002Findex.php\u002Ftools\u002Frequired\";\n    var CCM_APPLICATION_URL = \"http:\u002F\u002Flocalhost:8888\";\n    var CCM_REL = \"\";\n    var CCM_ACTIVE_LOCALE = \"ja_JP\";\n\u003C\u002Fscript>\n",[24,6837,6838,6848,6856,6864,6905,6909,6926,6930,6961,6991,7021,7040,7059,7073,7086,7099,7117,7135,7153,7167,7185],{"__ignoreMap":22},[27,6839,6840,6842,6844,6846],{"class":29,"line":30},[27,6841,1037],{"class":33},[27,6843,1040],{"class":37},[27,6845,1043],{"class":41},[27,6847,69],{"class":33},[27,6849,6850,6852,6854],{"class":29,"line":72},[27,6851,34],{"class":33},[27,6853,21],{"class":37},[27,6855,69],{"class":33},[27,6857,6858,6860,6862],{"class":29,"line":137},[27,6859,34],{"class":33},[27,6861,1060],{"class":37},[27,6863,69],{"class":33},[27,6865,6866,6868,6870,6872,6874,6876,6878,6880,6882,6884,6886,6889,6891,6893,6895,6897,6900,6902],{"class":29,"line":169},[27,6867,75],{"class":33},[27,6869,2851],{"class":37},[27,6871,2854],{"class":41},[27,6873,45],{"class":33},[27,6875,48],{"class":33},[27,6877,2861],{"class":51},[27,6879,48],{"class":33},[27,6881,104],{"class":41},[27,6883,45],{"class":33},[27,6885,48],{"class":33},[27,6887,6888],{"class":51},"text\u002Fcss",[27,6890,48],{"class":33},[27,6892,2866],{"class":41},[27,6894,45],{"class":33},[27,6896,48],{"class":33},[27,6898,6899],{"class":51},"\u002Fconcrete\u002Fthemes\u002Fconcrete\u002Fmain.css",[27,6901,48],{"class":33},[27,6903,6904],{"class":33}," \u002F>\n",[27,6906,6907],{"class":29,"line":178},[27,6908,2311],{"class":158},[27,6910,6911,6913,6915,6917,6920,6922,6924],{"class":29,"line":185},[27,6912,34],{"class":33},[27,6914,1091],{"class":37},[27,6916,155],{"class":33},[27,6918,6919],{"class":158},"ログイン :: c5test",[27,6921,162],{"class":33},[27,6923,1091],{"class":37},[27,6925,69],{"class":33},[27,6927,6928],{"class":29,"line":195},[27,6929,182],{"emptyLinePlaceholder":181},[27,6931,6932,6934,6936,6938,6940,6942,6945,6947,6949,6951,6953,6956,6958],{"class":29,"line":240},[27,6933,34],{"class":33},[27,6935,1070],{"class":37},[27,6937,3688],{"class":41},[27,6939,45],{"class":33},[27,6941,48],{"class":33},[27,6943,6944],{"class":51},"content-type",[27,6946,48],{"class":33},[27,6948,3700],{"class":41},[27,6950,45],{"class":33},[27,6952,48],{"class":33},[27,6954,6955],{"class":51},"text\u002Fhtml; charset=UTF-8",[27,6957,48],{"class":33},[27,6959,6960],{"class":33},"\u002F>\n",[27,6962,6963,6965,6967,6969,6971,6973,6976,6978,6980,6982,6984,6987,6989],{"class":29,"line":247},[27,6964,34],{"class":33},[27,6966,1070],{"class":37},[27,6968,116],{"class":41},[27,6970,45],{"class":33},[27,6972,48],{"class":33},[27,6974,6975],{"class":51},"generator",[27,6977,48],{"class":33},[27,6979,3700],{"class":41},[27,6981,45],{"class":33},[27,6983,48],{"class":33},[27,6985,6986],{"class":51},"concrete5 - 8.5.4",[27,6988,48],{"class":33},[27,6990,6960],{"class":33},[27,6992,6993,6995,6997,6999,7001,7003,7006,7008,7010,7012,7014,7017,7019],{"class":29,"line":256},[27,6994,34],{"class":33},[27,6996,2851],{"class":37},[27,6998,2854],{"class":41},[27,7000,45],{"class":33},[27,7002,48],{"class":33},[27,7004,7005],{"class":51},"canonical",[27,7007,48],{"class":33},[27,7009,2866],{"class":41},[27,7011,45],{"class":33},[27,7013,48],{"class":33},[27,7015,7016],{"class":51},"http:\u002F\u002Flocalhost:8888\u002Flogin",[27,7018,48],{"class":33},[27,7020,69],{"class":33},[27,7022,7023,7025,7027,7029,7031,7033,7036,7038],{"class":29,"line":1156},[27,7024,34],{"class":33},[27,7026,190],{"class":37},[27,7028,104],{"class":41},[27,7030,45],{"class":33},[27,7032,48],{"class":33},[27,7034,7035],{"class":51},"text\u002Fjavascript",[27,7037,48],{"class":33},[27,7039,69],{"class":33},[27,7041,7042,7045,7048,7050,7052,7055,7057],{"class":29,"line":1161},[27,7043,7044],{"class":41},"    var",[27,7046,7047],{"class":158}," CCM_DISPATCHER_FILENAME ",[27,7049,45],{"class":33},[27,7051,530],{"class":33},[27,7053,7054],{"class":51},"\u002Findex.php",[27,7056,48],{"class":33},[27,7058,1255],{"class":33},[27,7060,7061,7063,7066,7068,7071],{"class":29,"line":1171},[27,7062,7044],{"class":41},[27,7064,7065],{"class":158}," CCM_CID ",[27,7067,45],{"class":33},[27,7069,7070],{"class":2673}," 174",[27,7072,1255],{"class":33},[27,7074,7075,7077,7080,7082,7084],{"class":29,"line":1181},[27,7076,7044],{"class":41},[27,7078,7079],{"class":158}," CCM_EDIT_MODE ",[27,7081,45],{"class":33},[27,7083,2459],{"class":2458},[27,7085,1255],{"class":33},[27,7087,7088,7090,7093,7095,7097],{"class":29,"line":1190},[27,7089,7044],{"class":41},[27,7091,7092],{"class":158}," CCM_ARRANGE_MODE ",[27,7094,45],{"class":33},[27,7096,2459],{"class":2458},[27,7098,1255],{"class":33},[27,7100,7101,7103,7106,7108,7110,7113,7115],{"class":29,"line":1216},[27,7102,7044],{"class":41},[27,7104,7105],{"class":158}," CCM_IMAGE_PATH ",[27,7107,45],{"class":33},[27,7109,530],{"class":33},[27,7111,7112],{"class":51},"\u002Fconcrete\u002Fimages",[27,7114,48],{"class":33},[27,7116,1255],{"class":33},[27,7118,7119,7121,7124,7126,7128,7131,7133],{"class":29,"line":2420},[27,7120,7044],{"class":41},[27,7122,7123],{"class":158}," CCM_TOOLS_PATH ",[27,7125,45],{"class":33},[27,7127,530],{"class":33},[27,7129,7130],{"class":51},"\u002Findex.php\u002Ftools\u002Frequired",[27,7132,48],{"class":33},[27,7134,1255],{"class":33},[27,7136,7137,7139,7142,7144,7146,7149,7151],{"class":29,"line":2426},[27,7138,7044],{"class":41},[27,7140,7141],{"class":158}," CCM_APPLICATION_URL ",[27,7143,45],{"class":33},[27,7145,530],{"class":33},[27,7147,7148],{"class":51},"http:\u002F\u002Flocalhost:8888",[27,7150,48],{"class":33},[27,7152,1255],{"class":33},[27,7154,7155,7157,7160,7162,7165],{"class":29,"line":2442},[27,7156,7044],{"class":41},[27,7158,7159],{"class":158}," CCM_REL ",[27,7161,45],{"class":33},[27,7163,7164],{"class":33}," \"\"",[27,7166,1255],{"class":33},[27,7168,7169,7171,7174,7176,7178,7181,7183],{"class":29,"line":2465},[27,7170,7044],{"class":41},[27,7172,7173],{"class":158}," CCM_ACTIVE_LOCALE ",[27,7175,45],{"class":33},[27,7177,530],{"class":33},[27,7179,7180],{"class":51},"ja_JP",[27,7182,48],{"class":33},[27,7184,1255],{"class":33},[27,7186,7187,7189,7191],{"class":29,"line":2471},[27,7188,162],{"class":33},[27,7190,190],{"class":37},[27,7192,69],{"class":33},[427,7194,7197],{"className":7195},[430,7196],"alert-danger","\n内部の生データが外部からアクセスされるのは非常にまずいのでdashbord配下にルーティングを置き、さらにリクエストの種類をXHRだけにしましょう。\n",[397,7199,7201],{"id":7200},"コンポーネントにajaxアクセスを実装","コンポーネントにAjaxアクセスを実装",[13,7203,7204,7205,7208],{},"URLとデータの取得処理は書いたので、vue側でそこにAjaxを飛ばすようにします。前回作成した",[24,7206,7207],{},"form.vue"," にAjaxの処理を書きます。",[17,7210,7212],{"className":4054,"code":7211,"filename":7207,"language":4057,"meta":22,"style":22},"created(){\n    if(this.isEdit){\n        $.ajax({\n            url:location.href,\n            type:'GET',\n            success: (data)=> {\n                let result = JSON.parse(data)[0]; \u002F\u002F returns array\n                this.title = result.title;\n            },\n            error: (xhr, textStatus, errorThrown)=>{\n                console.error('Error! ' + textStatus + ' ' + errorThrown);\n            }\n        })\n    }\n}\n",[24,7213,7214,7219,7224,7229,7234,7239,7244,7249,7254,7258,7263,7268,7273,7278,7282],{"__ignoreMap":22},[27,7215,7216],{"class":29,"line":30},[27,7217,7218],{"class":158},"created(){\n",[27,7220,7221],{"class":29,"line":72},[27,7222,7223],{"class":158},"    if(this.isEdit){\n",[27,7225,7226],{"class":29,"line":137},[27,7227,7228],{"class":158},"        $.ajax({\n",[27,7230,7231],{"class":29,"line":169},[27,7232,7233],{"class":158},"            url:location.href,\n",[27,7235,7236],{"class":29,"line":178},[27,7237,7238],{"class":158},"            type:'GET',\n",[27,7240,7241],{"class":29,"line":185},[27,7242,7243],{"class":158},"            success: (data)=> {\n",[27,7245,7246],{"class":29,"line":195},[27,7247,7248],{"class":158},"                let result = JSON.parse(data)[0]; \u002F\u002F returns array\n",[27,7250,7251],{"class":29,"line":240},[27,7252,7253],{"class":158},"                this.title = result.title;\n",[27,7255,7256],{"class":29,"line":247},[27,7257,3370],{"class":158},[27,7259,7260],{"class":29,"line":256},[27,7261,7262],{"class":158},"            error: (xhr, textStatus, errorThrown)=>{\n",[27,7264,7265],{"class":29,"line":1156},[27,7266,7267],{"class":158},"                console.error('Error! ' + textStatus + ' ' + errorThrown);\n",[27,7269,7270],{"class":29,"line":1161},[27,7271,7272],{"class":158},"            }\n",[27,7274,7275],{"class":29,"line":1171},[27,7276,7277],{"class":158},"        })\n",[27,7279,7280],{"class":29,"line":1181},[27,7281,1436],{"class":158},[27,7283,7284],{"class":29,"line":1190},[27,7285,1910],{"class":158},[13,7287,7288,7290,7291,7294],{},[24,7289,7207],{},"は編集と新規追加を併用しているので、",[24,7292,7293],{},"isEdit","というプロパティでAjaxを飛ばすかを制御しています。現在のURLに対してAjaxを飛ばすと、先ほどのコードで書かれているようにIDに紐づいたアルバム情報をDBから取ってきてくれます。",[13,7296,7297,7298,7301],{},"データはJSONで戻ってくるので　",[24,7299,7300],{},"JSON.prase","を用いてJSで用いられるようにします。もし仮にAjaxが失敗した場合\b（400、500系のエラー）、コンソールでエラーが吐かれるようになっています。",[397,7303,7304],{"id":7304},"編集用コンポーネントのレンダリング設定",[13,7306,7307],{},"シングルページそのものはエントリーポイントだけ。",[17,7309,7312],{"className":6734,"code":7310,"filename":7311,"language":6737,"meta":22,"style":22},"\u003C?php\ndefined('C5_EXECUTE') or die('Access Denied.');\n?>\n\n\u003Cdiv id=\"edit\">\u003C\u002Fdiv>\n","edit.php",[24,7313,7314,7319,7324,7329,7333],{"__ignoreMap":22},[27,7315,7316],{"class":29,"line":30},[27,7317,7318],{},"\u003C?php\n",[27,7320,7321],{"class":29,"line":72},[27,7322,7323],{},"defined('C5_EXECUTE') or die('Access Denied.');\n",[27,7325,7326],{"class":29,"line":137},[27,7327,7328],{},"?>\n",[27,7330,7331],{"class":29,"line":169},[27,7332,182],{"emptyLinePlaceholder":181},[27,7334,7335],{"class":29,"line":178},[27,7336,7337],{},"\u003Cdiv id=\"edit\">\u003C\u002Fdiv>\n",[13,7339,7340,7341,7344],{},"そしてvue側のeditコンポーネントはpropsを変えるだけで",[24,7342,7343],{},"add.vue","とほぼ同じ",[17,7346,7349],{"className":4054,"code":7347,"filename":7348,"language":4057,"meta":22,"style":22},"\u003Ctemplate>\n    \u003CAlbumForm :isEdit=\"true\"\u002F>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport AlbumForm from '.\u002Fcomponents\u002Fform';\nexport default {\n    name:'add',\n    components:{AlbumForm}\n}\n\u003C\u002Fscript>\n","edit.vue",[24,7350,7351,7359,7379,7387,7391,7399,7417,7425,7441,7453,7457],{"__ignoreMap":22},[27,7352,7353,7355,7357],{"class":29,"line":30},[27,7354,34],{"class":33},[27,7356,4066],{"class":37},[27,7358,69],{"class":33},[27,7360,7361,7363,7366,7369,7371,7373,7375,7377],{"class":29,"line":72},[27,7362,75],{"class":33},[27,7364,7365],{"class":37},"AlbumForm",[27,7367,7368],{"class":41}," :isEdit",[27,7370,45],{"class":33},[27,7372,48],{"class":33},[27,7374,1575],{"class":51},[27,7376,48],{"class":33},[27,7378,6960],{"class":33},[27,7380,7381,7383,7385],{"class":29,"line":137},[27,7382,162],{"class":33},[27,7384,4066],{"class":37},[27,7386,69],{"class":33},[27,7388,7389],{"class":29,"line":169},[27,7390,182],{"emptyLinePlaceholder":181},[27,7392,7393,7395,7397],{"class":29,"line":178},[27,7394,34],{"class":33},[27,7396,190],{"class":37},[27,7398,69],{"class":33},[27,7400,7401,7403,7406,7408,7410,7413,7415],{"class":29,"line":185},[27,7402,1239],{"class":1238},[27,7404,7405],{"class":158}," AlbumForm ",[27,7407,1245],{"class":1238},[27,7409,1248],{"class":33},[27,7411,7412],{"class":51},".\u002Fcomponents\u002Fform",[27,7414,205],{"class":33},[27,7416,1255],{"class":33},[27,7418,7419,7421,7423],{"class":29,"line":195},[27,7420,1773],{"class":1238},[27,7422,1776],{"class":1238},[27,7424,1328],{"class":33},[27,7426,7427,7430,7432,7434,7437,7439],{"class":29,"line":240},[27,7428,7429],{"class":37},"    name",[27,7431,527],{"class":33},[27,7433,205],{"class":33},[27,7435,7436],{"class":51},"add",[27,7438,205],{"class":33},[27,7440,538],{"class":33},[27,7442,7443,7446,7449,7451],{"class":29,"line":247},[27,7444,7445],{"class":37},"    components",[27,7447,7448],{"class":33},":{",[27,7450,7365],{"class":158},[27,7452,1910],{"class":33},[27,7454,7455],{"class":29,"line":256},[27,7456,1910],{"class":33},[27,7458,7459,7461,7463],{"class":29,"line":1156},[27,7460,162],{"class":33},[27,7462,190],{"class":37},[27,7464,69],{"class":33},[13,7466,7467,7468,7470],{},"最後にレンダー用の",[24,7469,390],{},"を以下のようにしておきます。",[17,7472,7474],{"className":1228,"code":7473,"filename":390,"language":1231,"meta":22,"style":22},"import Vue from 'vue'\nimport Add from '.\u002Fadd.vue'\nimport Edit from '.\u002Fedit.vue'\n\n\nVue.config.productionTip = false\n\nfunction renderIfidExits(id,vueRoot){\n  if(document.getElementById(id) !== null){\n    return new Vue({\n      render: h => h(vueRoot),\n    }).$mount('#'+id)\n  }\n}\n\nrenderIfidExits('add',Add);\nrenderIfidExits('edit',Edit);\n",[24,7475,7476,7491,7507,7523,7527,7531,7550,7554,7573,7603,7616,7639,7665,7669,7673,7677,7697],{"__ignoreMap":22},[27,7477,7478,7480,7483,7485,7487,7489],{"class":29,"line":30},[27,7479,1239],{"class":1238},[27,7481,7482],{"class":158}," Vue ",[27,7484,1245],{"class":1238},[27,7486,1248],{"class":33},[27,7488,4057],{"class":51},[27,7490,2479],{"class":33},[27,7492,7493,7495,7498,7500,7502,7505],{"class":29,"line":72},[27,7494,1239],{"class":1238},[27,7496,7497],{"class":158}," Add ",[27,7499,1245],{"class":1238},[27,7501,1248],{"class":33},[27,7503,7504],{"class":51},".\u002Fadd.vue",[27,7506,2479],{"class":33},[27,7508,7509,7511,7514,7516,7518,7521],{"class":29,"line":137},[27,7510,1239],{"class":1238},[27,7512,7513],{"class":158}," Edit ",[27,7515,1245],{"class":1238},[27,7517,1248],{"class":33},[27,7519,7520],{"class":51},".\u002Fedit.vue",[27,7522,2479],{"class":33},[27,7524,7525],{"class":29,"line":169},[27,7526,182],{"emptyLinePlaceholder":181},[27,7528,7529],{"class":29,"line":178},[27,7530,182],{"emptyLinePlaceholder":181},[27,7532,7533,7536,7538,7540,7542,7545,7547],{"class":29,"line":185},[27,7534,7535],{"class":158},"Vue",[27,7537,216],{"class":33},[27,7539,5786],{"class":158},[27,7541,216],{"class":33},[27,7543,7544],{"class":158},"productionTip ",[27,7546,45],{"class":33},[27,7548,7549],{"class":2458}," false\n",[27,7551,7552],{"class":29,"line":195},[27,7553,182],{"emptyLinePlaceholder":181},[27,7555,7556,7558,7561,7563,7566,7568,7571],{"class":29,"line":240},[27,7557,234],{"class":41},[27,7559,7560],{"class":198}," renderIfidExits",[27,7562,202],{"class":33},[27,7564,7565],{"class":1788},"id",[27,7567,231],{"class":33},[27,7569,7570],{"class":1788},"vueRoot",[27,7572,1796],{"class":33},[27,7574,7575,7578,7580,7582,7584,7586,7588,7590,7593,7596,7599,7601],{"class":29,"line":247},[27,7576,7577],{"class":1238},"  if",[27,7579,202],{"class":37},[27,7581,4238],{"class":158},[27,7583,216],{"class":33},[27,7585,4243],{"class":198},[27,7587,202],{"class":37},[27,7589,7565],{"class":158},[27,7591,7592],{"class":37},") ",[27,7594,7595],{"class":33},"!==",[27,7597,7598],{"class":33}," null",[27,7600,213],{"class":37},[27,7602,514],{"class":33},[27,7604,7605,7607,7609,7612,7614],{"class":29,"line":256},[27,7606,1993],{"class":1238},[27,7608,4222],{"class":33},[27,7610,7611],{"class":198}," Vue",[27,7613,202],{"class":37},[27,7615,514],{"class":33},[27,7617,7618,7621,7623,7626,7629,7631,7633,7635,7637],{"class":29,"line":1156},[27,7619,7620],{"class":198},"      render",[27,7622,527],{"class":33},[27,7624,7625],{"class":1788}," h",[27,7627,7628],{"class":41}," =>",[27,7630,7625],{"class":198},[27,7632,202],{"class":37},[27,7634,7570],{"class":158},[27,7636,213],{"class":37},[27,7638,538],{"class":33},[27,7640,7641,7643,7645,7647,7650,7652,7654,7657,7659,7661,7663],{"class":29,"line":1161},[27,7642,250],{"class":33},[27,7644,213],{"class":37},[27,7646,216],{"class":33},[27,7648,7649],{"class":198},"$mount",[27,7651,202],{"class":37},[27,7653,205],{"class":33},[27,7655,7656],{"class":51},"#",[27,7658,205],{"class":33},[27,7660,1853],{"class":33},[27,7662,7565],{"class":158},[27,7664,253],{"class":37},[27,7666,7667],{"class":29,"line":1171},[27,7668,4327],{"class":33},[27,7670,7671],{"class":29,"line":1181},[27,7672,1910],{"class":33},[27,7674,7675],{"class":29,"line":1190},[27,7676,182],{"emptyLinePlaceholder":181},[27,7678,7679,7682,7684,7686,7688,7690,7692,7695],{"class":29,"line":1216},[27,7680,7681],{"class":198},"renderIfidExits",[27,7683,202],{"class":158},[27,7685,205],{"class":33},[27,7687,7436],{"class":51},[27,7689,205],{"class":33},[27,7691,231],{"class":33},[27,7693,7694],{"class":158},"Add)",[27,7696,1255],{"class":33},[27,7698,7699,7701,7703,7705,7708,7710,7712,7715],{"class":29,"line":2420},[27,7700,7681],{"class":198},[27,7702,202],{"class":158},[27,7704,205],{"class":33},[27,7706,7707],{"class":51},"edit",[27,7709,205],{"class":33},[27,7711,231],{"class":33},[27,7713,7714],{"class":158},"Edit)",[27,7716,1255],{"class":33},[13,7718,7719],{},"前回はAddコンポーネントだけでしたが、今回はEditもあります。さらにこのmain.jsで全てのコンポーネントのレンダーを制御しているので、エントリーポイント のIDが存在すればそこにコンポーネントをレンダリングする。という方法を取っています。",[13,7721,7722,7723,7726],{},"本当はコンポーネントごとにレンダー用のjs（",[24,7724,7725],{},"add.js,edit.js","みたいな）を作成するのですが、面倒だったのでこうしました。",[13,7728,7729,7730,7733,7734,7737,7738,7740],{},"そしてform.vueで編集の場合（",[24,7731,7732],{},"isEdit = true","）に",[24,7735,7736],{},"created()","でAjaxを飛ばしてデータを取得し、",[24,7739,5006],{},"に代入するようします。",[279,7742,7743],{"id":7743},"実際にレンダーしてみる",[13,7745,7746,7747,7750],{},"ビルドをして ",[24,7748,7749],{},"\u002Fdashboard\u002Fvuetest\u002Fedit\u002F{id}"," へアクセスします。IDがあっていればそのデータを取ってきてくれます。",[1571,7752],{":src":7753,":width":4383},"'_mix\u002Fsch-2020-10-04-1.14.27-768x527.png'",[1571,7755],{":src":7756,":width":4383},"'_mix\u002Fajax-768x764.png'",[13,7758,7759],{},"きちんと画面には前回入力した追加内容が表示されています。開発者ツールでNetworkでAjaxを確認してみましょう。Request Header を確認すると確かに、現在のURLにアクセスしておりさらにXMLHttpRequestで送信されています。Resonseをみてみると",[17,7761,7763],{"className":494,"code":7762,"language":497,"meta":22,"style":22},"[\n    {\n        \"id\":\"1\",\n        \"title\":\"\\u30c6\\u30b9\\u30c8\",\n        \"created\":\"2020-08-27 00:28:38\",\n        \"modified\":\"2020-08-27 00:28:38\"\n    }\n]\n",[24,7764,7765,7770,7775,7794,7813,7833,7850,7854],{"__ignoreMap":22},[27,7766,7767],{"class":29,"line":30},[27,7768,7769],{"class":33},"[\n",[27,7771,7772],{"class":29,"line":72},[27,7773,7774],{"class":33},"    {\n",[27,7776,7777,7780,7782,7784,7786,7788,7790,7792],{"class":29,"line":137},[27,7778,7779],{"class":33},"        \"",[27,7781,7565],{"class":41},[27,7783,48],{"class":33},[27,7785,527],{"class":33},[27,7787,48],{"class":33},[27,7789,88],{"class":51},[27,7791,48],{"class":33},[27,7793,538],{"class":33},[27,7795,7796,7798,7800,7802,7804,7806,7809,7811],{"class":29,"line":169},[27,7797,7779],{"class":33},[27,7799,1091],{"class":41},[27,7801,48],{"class":33},[27,7803,527],{"class":33},[27,7805,48],{"class":33},[27,7807,7808],{"class":158},"\\u30c6\\u30b9\\u30c8",[27,7810,48],{"class":33},[27,7812,538],{"class":33},[27,7814,7815,7817,7820,7822,7824,7826,7829,7831],{"class":29,"line":178},[27,7816,7779],{"class":33},[27,7818,7819],{"class":41},"created",[27,7821,48],{"class":33},[27,7823,527],{"class":33},[27,7825,48],{"class":33},[27,7827,7828],{"class":51},"2020-08-27 00:28:38",[27,7830,48],{"class":33},[27,7832,538],{"class":33},[27,7834,7835,7837,7840,7842,7844,7846,7848],{"class":29,"line":185},[27,7836,7779],{"class":33},[27,7838,7839],{"class":41},"modified",[27,7841,48],{"class":33},[27,7843,527],{"class":33},[27,7845,48],{"class":33},[27,7847,7828],{"class":51},[27,7849,656],{"class":33},[27,7851,7852],{"class":29,"line":195},[27,7853,1436],{"class":33},[27,7855,7856],{"class":29,"line":240},[27,7857,3445],{"class":33},[13,7859,7860],{},"このようにデータベースから取ってきた内容がJSONとして渡されているのが確認できます。ちなみに日本語はエンコードされているので、js側でJSON praseを使うことで元の文章に戻すことができます。",[279,7862,7863],{"id":7863},"内容を更新する",[13,7865,7866,7867,1614],{},"編集画面を表示した際の初期表示はできるようになったので、次は内容が書き換えてDBの内容を変更できるようにしましょう。しかし触るのはバックエンドの部分だけです。",[24,7868,6730],{},[17,7870,7872],{"className":6734,"code":7871,"filename":6736,"language":6737,"meta":22,"style":22},"pupublic function edit($id=null){\n\n    \u002F\u002F パラメータがない場合は一覧画面へリダイレクト\n    if($id==null) return Redirect::to('\u002Fdashboard\u002Fvuetest\u002F')->send();\n\n    \u002F\u002F Ajax エンドポイント\n    if($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'){\n        $db = Database::connection();\n        $db->Execute(\"START TRANSACTION\");\n        $result = $db->fetchAll('SELECT * FROM album ORDER BY ID DESC');\n        $db->Execute(\"COMMIT\");\n        return Core::make('helper\u002Fajax')->sendResult($result);\n    }\n\n    \u002F\u002Fpost処理(ここを追記)\n    if(Request::isPost() == true){\n        $title = $this->post('title');\n\n        if(empty($title)==false){\n            $db = Database::connection();\n            $db->executeQuery(\"START TRANSACTION\");\n            $db->executeQuery(\n                'UPDATE album SET `title`=?, `modified`=now() WHERE `ID`=?',\n                array($title,$id)\n            );\n            $db->executeQuery(\"COMMIT\");\n            Redirect::to('\u002Fdashboard\u002Fvuetest')->send();\n        }else{\n            Redirect::to('\u002Fdashboard\u002Fvuetest')->send();\n        }\n\n    }else{\n        $this->render('\u002Fdashboard\u002Fvuetest\u002Fadd');\n    }\n}\n",[24,7873,7874,7879,7883,7888,7892,7896,7901,7905,7909,7913,7918,7922,7926,7930,7934,7939,7944,7949,7953,7958,7963,7968,7973,7978,7983,7988,7993,7998,8003,8007,8011,8015,8020,8025,8029],{"__ignoreMap":22},[27,7875,7876],{"class":29,"line":30},[27,7877,7878],{},"pupublic function edit($id=null){\n",[27,7880,7881],{"class":29,"line":72},[27,7882,182],{"emptyLinePlaceholder":181},[27,7884,7885],{"class":29,"line":137},[27,7886,7887],{},"    \u002F\u002F パラメータがない場合は一覧画面へリダイレクト\n",[27,7889,7890],{"class":29,"line":169},[27,7891,6749],{},[27,7893,7894],{"class":29,"line":178},[27,7895,182],{"emptyLinePlaceholder":181},[27,7897,7898],{"class":29,"line":185},[27,7899,7900],{},"    \u002F\u002F Ajax エンドポイント\n",[27,7902,7903],{"class":29,"line":195},[27,7904,6758],{},[27,7906,7907],{"class":29,"line":240},[27,7908,6763],{},[27,7910,7911],{"class":29,"line":247},[27,7912,6768],{},[27,7914,7915],{"class":29,"line":256},[27,7916,7917],{},"        $result = $db->fetchAll('SELECT * FROM album ORDER BY ID DESC');\n",[27,7919,7920],{"class":29,"line":1156},[27,7921,6778],{},[27,7923,7924],{"class":29,"line":1161},[27,7925,6783],{},[27,7927,7928],{"class":29,"line":1171},[27,7929,1436],{},[27,7931,7932],{"class":29,"line":1181},[27,7933,182],{"emptyLinePlaceholder":181},[27,7935,7936],{"class":29,"line":1190},[27,7937,7938],{},"    \u002F\u002Fpost処理(ここを追記)\n",[27,7940,7941],{"class":29,"line":1216},[27,7942,7943],{},"    if(Request::isPost() == true){\n",[27,7945,7946],{"class":29,"line":2420},[27,7947,7948],{},"        $title = $this->post('title');\n",[27,7950,7951],{"class":29,"line":2426},[27,7952,182],{"emptyLinePlaceholder":181},[27,7954,7955],{"class":29,"line":2442},[27,7956,7957],{},"        if(empty($title)==false){\n",[27,7959,7960],{"class":29,"line":2465},[27,7961,7962],{},"            $db = Database::connection();\n",[27,7964,7965],{"class":29,"line":2471},[27,7966,7967],{},"            $db->executeQuery(\"START TRANSACTION\");\n",[27,7969,7970],{"class":29,"line":2482},[27,7971,7972],{},"            $db->executeQuery(\n",[27,7974,7975],{"class":29,"line":2488},[27,7976,7977],{},"                'UPDATE album SET `title`=?, `modified`=now() WHERE `ID`=?',\n",[27,7979,7980],{"class":29,"line":2494},[27,7981,7982],{},"                array($title,$id)\n",[27,7984,7985],{"class":29,"line":2500},[27,7986,7987],{},"            );\n",[27,7989,7990],{"class":29,"line":2506},[27,7991,7992],{},"            $db->executeQuery(\"COMMIT\");\n",[27,7994,7995],{"class":29,"line":2511},[27,7996,7997],{},"            Redirect::to('\u002Fdashboard\u002Fvuetest')->send();\n",[27,7999,8000],{"class":29,"line":2520},[27,8001,8002],{},"        }else{\n",[27,8004,8005],{"class":29,"line":2525},[27,8006,7997],{},[27,8008,8009],{"class":29,"line":4},[27,8010,3388],{},[27,8012,8013],{"class":29,"line":2548},[27,8014,182],{"emptyLinePlaceholder":181},[27,8016,8017],{"class":29,"line":2561},[27,8018,8019],{},"    }else{\n",[27,8021,8022],{"class":29,"line":2567},[27,8023,8024],{},"        $this->render('\u002Fdashboard\u002Fvuetest\u002Fadd');\n",[27,8026,8027],{"class":29,"line":2573},[27,8028,1436],{},[27,8030,8031],{"class":29,"line":2583},[27,8032,1910],{},[13,8034,8035,8036,8039],{},"新規作成の際に使用される",[24,8037,8038],{},"pupublic function edit"," を一部変更したぐらいです。postがある場合、その値のバリデーションをして変更するIDを元に更新用のSQLを走らせるだけです。",[397,8041,8042],{"id":8042},"実際に変更してみる",[1571,8044],{":src":8045,":width":4383},"'_mix\u002Fsch-2020-10-04-13.37.28-768x396.png'",[1571,8047],{":src":8048,":width":4383},"'_mix\u002Fsch-2020-10-04-13.38.13.png'",[13,8050,8051],{},"無事、タイトルと編集時間が更新されました。これでCRUDの「Update」が完成しました。",[279,8053,8054],{"id":8054},"一覧画面を作成する",[13,8056,8057],{},"追加・編集の画面は完成しました。次は全ての登録データを一覧で見れる画面を作成していきます。\u002Fdashboard\u002Fvuetest\u002F にアクセスした際に一覧が表示されるようにします。",[397,8059,8060],{"id":8060},"一覧用コンポーネントの作成",[13,8062,8063],{},"一覧コンポーネントをindex.vueとしておきます。一覧画面では以下のような構成にしています。",[17,8065,8068],{"className":4054,"code":8066,"filename":8067,"language":4057,"meta":22,"style":22},"\u003Ctemplate>\n    \u003Cdiv class=\"ccm-dashboard-content-inner\">\n        \u003Ch3>アルバム一覧\u003C\u002Fh3>\n        \u003Chr>\n\n        \u003Ctable class=\"p-package-index table table-hover\">\n            \u003Cthead>\n                \u003Ctr>\n                    \u003Cth class=\"c-dol-title\">タイトル\u003C\u002Fth>\n                    \u003Cth class=\"c-dol-publish-date\">作成日\u003C\u002Fth>\n                    \u003Cth class=\"c-dol-operation\">操作\u003C\u002Fth>\n                \u003C\u002Ftr>\n            \u003C\u002Fthead>\n            \u003Ctbody>\n                \u003Ctr v-for=\"val in list\" :key=\"val.id\" tabindex=\"0\">\n                    \u003Ctd style=\"vertical-align:middle;\">{{val.title}}\u003C\u002Ftd>\n                    \u003Ctd style=\"vertical-align:middle;\">{{val.created}}\u003C\u002Ftd>\n                    \u003Ctd style=\"vertical-align:middle;\">\n                        \u003Ca :href=\"'\u002Fdashboard\u002Fvuetest\u002Fedit\u002F'+val.id\" type=\"button\" class=\"btn btn-success btn-sm\" style=\"margin-right:10px;\">編集\u003C\u002Fa>\n                        \u003Cbutton type=\"button\" class=\"btn btn-danger btn-sm\" v-on:click=\"cofirmDelete(val)\">削除\u003C\u002Fbutton>\n                    \u003C\u002Ftd>\n                \u003C\u002Ftr>\n            \u003C\u002Ftbody>\n        \u003C\u002Ftable>\n\n        \u003Cdiv class=\"ccm-dashboard-form-actions-wrapper\">\n            \u003Cdiv class=\"ccm-dashboard-form-actions\">\n                \u003Ca href=\"\u002Fdashboard\u002Fvuetest\u002Fadd\" class=\"pull-right btn btn-primary\">\n                    新規追加\n                \u003C\u002Fa>\n            \u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default {\n    name:'index',\n    data(){\n        return {\n            list:[],\n            isProcessing:false\n        }\n    },\n    methods:{\n        cofirmDelete(obj){\n            let result = window.confirm('アルバム：「'+obj.title+'」を本当に削除しますか？この操作は取り消せません。');\n            if(result) return this.deleteAlubm(obj.id);\n        },\n        deleteAlubm(id){\n            if(this.isProcessing==false){\n                this.isProcessing = true;\n\n                $.ajax({\n                    url:'\u002Fdashboard\u002Fvuetest\u002FdeleteData\u002F',\n                    type:'POST',\n                    data:{target:id},\n                    success: ()=> {\n                        let targetIndex = this.list.findIndex((ele)=>{\n                            return ele.ID == id;\n                        });\n                        this.list.splice(targetIndex,1);\n                        this.isProcessing = false;\n                    },\n                    error: (xhr, textStatus, errorThrown)=>{\n                        console.log('Error! ' + textStatus + ' ' + errorThrown);\n                    }\n                })\n            }\n        },\n    },\n    created(){\n        $.ajax({\n            url:'\u002Fdashboard\u002Fvuetest\u002FloadData',\n            type:'GET',\n            success: (data)=> {\n                this.list = JSON.parse(data); \u002F\u002F returns array\n            },\n            error: (xhr, textStatus, errorThrown)=>{\n                console.error('Error! ' + textStatus + ' ' + errorThrown);\n            }\n        })\n    }\n}\n\u003C\u002Fscript>\n","index.vue",[24,8069,8070,8078,8097,8114,8123,8127,8147,8156,8166,8195,8223,8251,8260,8268,8277,8321,8351,8378,8396,8458,8508,8517,8525,8533,8541,8545,8564,8583,8613,8618,8626,8634,8642,8650,8658,8662,8670,8678,8693,8700,8706,8718,8729,8734,8739,8747,8760,8809,8842,8848,8860,8883,8898,8903,8918,8935,8952,8969,8984,9018,9039,9049,9075,9088,9094,9124,9162,9168,9176,9181,9186,9191,9199,9212,9228,9245,9264,9292,9297,9323,9359,9364,9371,9376,9381],{"__ignoreMap":22},[27,8071,8072,8074,8076],{"class":29,"line":30},[27,8073,34],{"class":33},[27,8075,4066],{"class":37},[27,8077,69],{"class":33},[27,8079,8080,8082,8084,8086,8088,8090,8093,8095],{"class":29,"line":72},[27,8081,75],{"class":33},[27,8083,427],{"class":37},[27,8085,42],{"class":41},[27,8087,45],{"class":33},[27,8089,48],{"class":33},[27,8091,8092],{"class":51},"ccm-dashboard-content-inner",[27,8094,48],{"class":33},[27,8096,69],{"class":33},[27,8098,8099,8101,8103,8105,8108,8110,8112],{"class":29,"line":137},[27,8100,1067],{"class":33},[27,8102,397],{"class":37},[27,8104,155],{"class":33},[27,8106,8107],{"class":158},"アルバム一覧",[27,8109,162],{"class":33},[27,8111,397],{"class":37},[27,8113,69],{"class":33},[27,8115,8116,8118,8121],{"class":29,"line":169},[27,8117,1067],{"class":33},[27,8119,8120],{"class":37},"hr",[27,8122,69],{"class":33},[27,8124,8125],{"class":29,"line":178},[27,8126,182],{"emptyLinePlaceholder":181},[27,8128,8129,8131,8134,8136,8138,8140,8143,8145],{"class":29,"line":185},[27,8130,1067],{"class":33},[27,8132,8133],{"class":37},"table",[27,8135,42],{"class":41},[27,8137,45],{"class":33},[27,8139,48],{"class":33},[27,8141,8142],{"class":51},"p-package-index table table-hover",[27,8144,48],{"class":33},[27,8146,69],{"class":33},[27,8148,8149,8151,8154],{"class":29,"line":195},[27,8150,1138],{"class":33},[27,8152,8153],{"class":37},"thead",[27,8155,69],{"class":33},[27,8157,8158,8161,8164],{"class":29,"line":240},[27,8159,8160],{"class":33},"                \u003C",[27,8162,8163],{"class":37},"tr",[27,8165,69],{"class":33},[27,8167,8168,8171,8174,8176,8178,8180,8183,8185,8187,8189,8191,8193],{"class":29,"line":247},[27,8169,8170],{"class":33},"                    \u003C",[27,8172,8173],{"class":37},"th",[27,8175,42],{"class":41},[27,8177,45],{"class":33},[27,8179,48],{"class":33},[27,8181,8182],{"class":51},"c-dol-title",[27,8184,48],{"class":33},[27,8186,155],{"class":33},[27,8188,5406],{"class":158},[27,8190,162],{"class":33},[27,8192,8173],{"class":37},[27,8194,69],{"class":33},[27,8196,8197,8199,8201,8203,8205,8207,8210,8212,8214,8217,8219,8221],{"class":29,"line":256},[27,8198,8170],{"class":33},[27,8200,8173],{"class":37},[27,8202,42],{"class":41},[27,8204,45],{"class":33},[27,8206,48],{"class":33},[27,8208,8209],{"class":51},"c-dol-publish-date",[27,8211,48],{"class":33},[27,8213,155],{"class":33},[27,8215,8216],{"class":158},"作成日",[27,8218,162],{"class":33},[27,8220,8173],{"class":37},[27,8222,69],{"class":33},[27,8224,8225,8227,8229,8231,8233,8235,8238,8240,8242,8245,8247,8249],{"class":29,"line":1156},[27,8226,8170],{"class":33},[27,8228,8173],{"class":37},[27,8230,42],{"class":41},[27,8232,45],{"class":33},[27,8234,48],{"class":33},[27,8236,8237],{"class":51},"c-dol-operation",[27,8239,48],{"class":33},[27,8241,155],{"class":33},[27,8243,8244],{"class":158},"操作",[27,8246,162],{"class":33},[27,8248,8173],{"class":37},[27,8250,69],{"class":33},[27,8252,8253,8256,8258],{"class":29,"line":1161},[27,8254,8255],{"class":33},"                \u003C\u002F",[27,8257,8163],{"class":37},[27,8259,69],{"class":33},[27,8261,8262,8264,8266],{"class":29,"line":1171},[27,8263,1164],{"class":33},[27,8265,8153],{"class":37},[27,8267,69],{"class":33},[27,8269,8270,8272,8275],{"class":29,"line":1181},[27,8271,1138],{"class":33},[27,8273,8274],{"class":37},"tbody",[27,8276,69],{"class":33},[27,8278,8279,8281,8283,8286,8288,8290,8293,8295,8298,8300,8302,8305,8307,8310,8312,8314,8317,8319],{"class":29,"line":1190},[27,8280,8160],{"class":33},[27,8282,8163],{"class":37},[27,8284,8285],{"class":41}," v-for",[27,8287,45],{"class":33},[27,8289,48],{"class":33},[27,8291,8292],{"class":51},"val in list",[27,8294,48],{"class":33},[27,8296,8297],{"class":41}," :key",[27,8299,45],{"class":33},[27,8301,48],{"class":33},[27,8303,8304],{"class":51},"val.id",[27,8306,48],{"class":33},[27,8308,8309],{"class":41}," tabindex",[27,8311,45],{"class":33},[27,8313,48],{"class":33},[27,8315,8316],{"class":51},"0",[27,8318,48],{"class":33},[27,8320,69],{"class":33},[27,8322,8323,8325,8328,8331,8333,8335,8338,8340,8342,8345,8347,8349],{"class":29,"line":1216},[27,8324,8170],{"class":33},[27,8326,8327],{"class":37},"td",[27,8329,8330],{"class":41}," style",[27,8332,45],{"class":33},[27,8334,48],{"class":33},[27,8336,8337],{"class":51},"vertical-align:middle;",[27,8339,48],{"class":33},[27,8341,155],{"class":33},[27,8343,8344],{"class":158},"{{val.title}}",[27,8346,162],{"class":33},[27,8348,8327],{"class":37},[27,8350,69],{"class":33},[27,8352,8353,8355,8357,8359,8361,8363,8365,8367,8369,8372,8374,8376],{"class":29,"line":2420},[27,8354,8170],{"class":33},[27,8356,8327],{"class":37},[27,8358,8330],{"class":41},[27,8360,45],{"class":33},[27,8362,48],{"class":33},[27,8364,8337],{"class":51},[27,8366,48],{"class":33},[27,8368,155],{"class":33},[27,8370,8371],{"class":158},"{{val.created}}",[27,8373,162],{"class":33},[27,8375,8327],{"class":37},[27,8377,69],{"class":33},[27,8379,8380,8382,8384,8386,8388,8390,8392,8394],{"class":29,"line":2426},[27,8381,8170],{"class":33},[27,8383,8327],{"class":37},[27,8385,8330],{"class":41},[27,8387,45],{"class":33},[27,8389,48],{"class":33},[27,8391,8337],{"class":51},[27,8393,48],{"class":33},[27,8395,69],{"class":33},[27,8397,8398,8401,8403,8406,8408,8410,8413,8415,8417,8419,8421,8423,8425,8427,8429,8431,8434,8436,8438,8440,8442,8445,8447,8449,8452,8454,8456],{"class":29,"line":2442},[27,8399,8400],{"class":33},"                        \u003C",[27,8402,442],{"class":37},[27,8404,8405],{"class":41}," :href",[27,8407,45],{"class":33},[27,8409,48],{"class":33},[27,8411,8412],{"class":51},"'\u002Fdashboard\u002Fvuetest\u002Fedit\u002F'+val.id",[27,8414,48],{"class":33},[27,8416,104],{"class":41},[27,8418,45],{"class":33},[27,8420,48],{"class":33},[27,8422,1704],{"class":51},[27,8424,48],{"class":33},[27,8426,42],{"class":41},[27,8428,45],{"class":33},[27,8430,48],{"class":33},[27,8432,8433],{"class":51},"btn btn-success btn-sm",[27,8435,48],{"class":33},[27,8437,8330],{"class":41},[27,8439,45],{"class":33},[27,8441,48],{"class":33},[27,8443,8444],{"class":51},"margin-right:10px;",[27,8446,48],{"class":33},[27,8448,155],{"class":33},[27,8450,8451],{"class":158},"編集",[27,8453,162],{"class":33},[27,8455,442],{"class":37},[27,8457,69],{"class":33},[27,8459,8460,8462,8464,8466,8468,8470,8472,8474,8476,8478,8480,8483,8485,8488,8490,8492,8495,8497,8499,8502,8504,8506],{"class":29,"line":2465},[27,8461,8400],{"class":33},[27,8463,1704],{"class":37},[27,8465,104],{"class":41},[27,8467,45],{"class":33},[27,8469,48],{"class":33},[27,8471,1704],{"class":51},[27,8473,48],{"class":33},[27,8475,42],{"class":41},[27,8477,45],{"class":33},[27,8479,48],{"class":33},[27,8481,8482],{"class":51},"btn btn-danger btn-sm",[27,8484,48],{"class":33},[27,8486,8487],{"class":41}," v-on:click",[27,8489,45],{"class":33},[27,8491,48],{"class":33},[27,8493,8494],{"class":51},"cofirmDelete(val)",[27,8496,48],{"class":33},[27,8498,155],{"class":33},[27,8500,8501],{"class":158},"削除",[27,8503,162],{"class":33},[27,8505,1704],{"class":37},[27,8507,69],{"class":33},[27,8509,8510,8513,8515],{"class":29,"line":2471},[27,8511,8512],{"class":33},"                    \u003C\u002F",[27,8514,8327],{"class":37},[27,8516,69],{"class":33},[27,8518,8519,8521,8523],{"class":29,"line":2482},[27,8520,8255],{"class":33},[27,8522,8163],{"class":37},[27,8524,69],{"class":33},[27,8526,8527,8529,8531],{"class":29,"line":2488},[27,8528,1164],{"class":33},[27,8530,8274],{"class":37},[27,8532,69],{"class":33},[27,8534,8535,8537,8539],{"class":29,"line":2494},[27,8536,1174],{"class":33},[27,8538,8133],{"class":37},[27,8540,69],{"class":33},[27,8542,8543],{"class":29,"line":2500},[27,8544,182],{"emptyLinePlaceholder":181},[27,8546,8547,8549,8551,8553,8555,8557,8560,8562],{"class":29,"line":2506},[27,8548,1067],{"class":33},[27,8550,427],{"class":37},[27,8552,42],{"class":41},[27,8554,45],{"class":33},[27,8556,48],{"class":33},[27,8558,8559],{"class":51},"ccm-dashboard-form-actions-wrapper",[27,8561,48],{"class":33},[27,8563,69],{"class":33},[27,8565,8566,8568,8570,8572,8574,8576,8579,8581],{"class":29,"line":2511},[27,8567,1138],{"class":33},[27,8569,427],{"class":37},[27,8571,42],{"class":41},[27,8573,45],{"class":33},[27,8575,48],{"class":33},[27,8577,8578],{"class":51},"ccm-dashboard-form-actions",[27,8580,48],{"class":33},[27,8582,69],{"class":33},[27,8584,8585,8587,8589,8591,8593,8595,8598,8600,8602,8604,8606,8609,8611],{"class":29,"line":2520},[27,8586,8160],{"class":33},[27,8588,442],{"class":37},[27,8590,2866],{"class":41},[27,8592,45],{"class":33},[27,8594,48],{"class":33},[27,8596,8597],{"class":51},"\u002Fdashboard\u002Fvuetest\u002Fadd",[27,8599,48],{"class":33},[27,8601,42],{"class":41},[27,8603,45],{"class":33},[27,8605,48],{"class":33},[27,8607,8608],{"class":51},"pull-right btn btn-primary",[27,8610,48],{"class":33},[27,8612,69],{"class":33},[27,8614,8615],{"class":29,"line":2525},[27,8616,8617],{"class":158},"                    新規追加\n",[27,8619,8620,8622,8624],{"class":29,"line":4},[27,8621,8255],{"class":33},[27,8623,442],{"class":37},[27,8625,69],{"class":33},[27,8627,8628,8630,8632],{"class":29,"line":2548},[27,8629,1164],{"class":33},[27,8631,427],{"class":37},[27,8633,69],{"class":33},[27,8635,8636,8638,8640],{"class":29,"line":2561},[27,8637,1174],{"class":33},[27,8639,427],{"class":37},[27,8641,69],{"class":33},[27,8643,8644,8646,8648],{"class":29,"line":2567},[27,8645,1107],{"class":33},[27,8647,427],{"class":37},[27,8649,69],{"class":33},[27,8651,8652,8654,8656],{"class":29,"line":2573},[27,8653,162],{"class":33},[27,8655,4066],{"class":37},[27,8657,69],{"class":33},[27,8659,8660],{"class":29,"line":2583},[27,8661,182],{"emptyLinePlaceholder":181},[27,8663,8664,8666,8668],{"class":29,"line":2596},[27,8665,34],{"class":33},[27,8667,190],{"class":37},[27,8669,69],{"class":33},[27,8671,8672,8674,8676],{"class":29,"line":2611},[27,8673,1773],{"class":1238},[27,8675,1776],{"class":1238},[27,8677,1328],{"class":33},[27,8679,8680,8682,8684,8686,8689,8691],{"class":29,"line":2619},[27,8681,7429],{"class":37},[27,8683,527],{"class":33},[27,8685,205],{"class":33},[27,8687,8688],{"class":51},"index",[27,8690,205],{"class":33},[27,8692,538],{"class":33},[27,8694,8695,8698],{"class":29,"line":2625},[27,8696,8697],{"class":37},"    data",[27,8699,237],{"class":33},[27,8701,8702,8704],{"class":29,"line":6504},[27,8703,6382],{"class":1238},[27,8705,1328],{"class":33},[27,8707,8708,8711,8713,8716],{"class":29,"line":6509},[27,8709,8710],{"class":37},"            list",[27,8712,527],{"class":33},[27,8714,8715],{"class":37},"[]",[27,8717,538],{"class":33},[27,8719,8721,8724,8726],{"class":29,"line":8720},42,[27,8722,8723],{"class":37},"            isProcessing",[27,8725,527],{"class":33},[27,8727,8728],{"class":2458},"false\n",[27,8730,8732],{"class":29,"line":8731},43,[27,8733,3388],{"class":33},[27,8735,8737],{"class":29,"line":8736},44,[27,8738,2564],{"class":33},[27,8740,8742,8745],{"class":29,"line":8741},45,[27,8743,8744],{"class":37},"    methods",[27,8746,5676],{"class":33},[27,8748,8750,8753,8755,8758],{"class":29,"line":8749},46,[27,8751,8752],{"class":37},"        cofirmDelete",[27,8754,202],{"class":33},[27,8756,8757],{"class":1788},"obj",[27,8759,1796],{"class":33},[27,8761,8763,8766,8769,8771,8774,8776,8779,8781,8783,8786,8788,8790,8792,8794,8796,8798,8800,8803,8805,8807],{"class":29,"line":8762},47,[27,8764,8765],{"class":41},"            let",[27,8767,8768],{"class":158}," result",[27,8770,1325],{"class":33},[27,8772,8773],{"class":158}," window",[27,8775,216],{"class":33},[27,8777,8778],{"class":198},"confirm",[27,8780,202],{"class":37},[27,8782,205],{"class":33},[27,8784,8785],{"class":51},"アルバム：「",[27,8787,205],{"class":33},[27,8789,1853],{"class":33},[27,8791,8757],{"class":158},[27,8793,216],{"class":33},[27,8795,1091],{"class":158},[27,8797,1853],{"class":33},[27,8799,205],{"class":33},[27,8801,8802],{"class":51},"」を本当に削除しますか？この操作は取り消せません。",[27,8804,205],{"class":33},[27,8806,213],{"class":37},[27,8808,1255],{"class":33},[27,8810,8812,8815,8817,8820,8822,8825,8827,8830,8832,8834,8836,8838,8840],{"class":29,"line":8811},48,[27,8813,8814],{"class":1238},"            if",[27,8816,202],{"class":37},[27,8818,8819],{"class":158},"result",[27,8821,7592],{"class":37},[27,8823,8824],{"class":1238},"return",[27,8826,4225],{"class":33},[27,8828,8829],{"class":198},"deleteAlubm",[27,8831,202],{"class":37},[27,8833,8757],{"class":158},[27,8835,216],{"class":33},[27,8837,7565],{"class":158},[27,8839,213],{"class":37},[27,8841,1255],{"class":33},[27,8843,8845],{"class":29,"line":8844},49,[27,8846,8847],{"class":33},"        },\n",[27,8849,8851,8854,8856,8858],{"class":29,"line":8850},50,[27,8852,8853],{"class":37},"        deleteAlubm",[27,8855,202],{"class":33},[27,8857,7565],{"class":1788},[27,8859,1796],{"class":33},[27,8861,8863,8865,8867,8870,8873,8876,8879,8881],{"class":29,"line":8862},51,[27,8864,8814],{"class":1238},[27,8866,202],{"class":37},[27,8868,8869],{"class":33},"this.",[27,8871,8872],{"class":158},"isProcessing",[27,8874,8875],{"class":33},"==",[27,8877,8878],{"class":2458},"false",[27,8880,213],{"class":37},[27,8882,514],{"class":33},[27,8884,8886,8889,8891,8893,8896],{"class":29,"line":8885},52,[27,8887,8888],{"class":33},"                this.",[27,8890,8872],{"class":158},[27,8892,1325],{"class":33},[27,8894,8895],{"class":2458}," true",[27,8897,1255],{"class":33},[27,8899,8901],{"class":29,"line":8900},53,[27,8902,182],{"emptyLinePlaceholder":181},[27,8904,8906,8909,8911,8914,8916],{"class":29,"line":8905},54,[27,8907,8908],{"class":158},"                $",[27,8910,216],{"class":33},[27,8912,8913],{"class":198},"ajax",[27,8915,202],{"class":37},[27,8917,514],{"class":33},[27,8919,8921,8924,8926,8928,8931,8933],{"class":29,"line":8920},55,[27,8922,8923],{"class":37},"                    url",[27,8925,527],{"class":33},[27,8927,205],{"class":33},[27,8929,8930],{"class":51},"\u002Fdashboard\u002Fvuetest\u002FdeleteData\u002F",[27,8932,205],{"class":33},[27,8934,538],{"class":33},[27,8936,8938,8941,8943,8945,8948,8950],{"class":29,"line":8937},56,[27,8939,8940],{"class":37},"                    type",[27,8942,527],{"class":33},[27,8944,205],{"class":33},[27,8946,8947],{"class":51},"POST",[27,8949,205],{"class":33},[27,8951,538],{"class":33},[27,8953,8955,8958,8960,8963,8965,8967],{"class":29,"line":8954},57,[27,8956,8957],{"class":37},"                    data",[27,8959,7448],{"class":33},[27,8961,8962],{"class":37},"target",[27,8964,527],{"class":33},[27,8966,7565],{"class":158},[27,8968,3397],{"class":33},[27,8970,8972,8975,8977,8980,8982],{"class":29,"line":8971},58,[27,8973,8974],{"class":198},"                    success",[27,8976,527],{"class":33},[27,8978,8979],{"class":33}," ()",[27,8981,1986],{"class":41},[27,8983,1328],{"class":33},[27,8985,8987,8990,8993,8995,8997,9000,9002,9005,9007,9009,9012,9014,9016],{"class":29,"line":8986},59,[27,8988,8989],{"class":41},"                        let",[27,8991,8992],{"class":158}," targetIndex",[27,8994,1325],{"class":33},[27,8996,4225],{"class":33},[27,8998,8999],{"class":158},"list",[27,9001,216],{"class":33},[27,9003,9004],{"class":198},"findIndex",[27,9006,202],{"class":37},[27,9008,202],{"class":33},[27,9010,9011],{"class":1788},"ele",[27,9013,213],{"class":33},[27,9015,1986],{"class":41},[27,9017,514],{"class":33},[27,9019,9021,9024,9027,9029,9032,9035,9037],{"class":29,"line":9020},60,[27,9022,9023],{"class":1238},"                            return",[27,9025,9026],{"class":158}," ele",[27,9028,216],{"class":33},[27,9030,9031],{"class":158},"ID",[27,9033,9034],{"class":33}," ==",[27,9036,93],{"class":158},[27,9038,1255],{"class":33},[27,9040,9042,9045,9047],{"class":29,"line":9041},61,[27,9043,9044],{"class":33},"                        }",[27,9046,213],{"class":37},[27,9048,1255],{"class":33},[27,9050,9052,9055,9057,9059,9062,9064,9067,9069,9071,9073],{"class":29,"line":9051},62,[27,9053,9054],{"class":33},"                        this.",[27,9056,8999],{"class":158},[27,9058,216],{"class":33},[27,9060,9061],{"class":198},"splice",[27,9063,202],{"class":37},[27,9065,9066],{"class":158},"targetIndex",[27,9068,231],{"class":33},[27,9070,88],{"class":2673},[27,9072,213],{"class":37},[27,9074,1255],{"class":33},[27,9076,9078,9080,9082,9084,9086],{"class":29,"line":9077},63,[27,9079,9054],{"class":33},[27,9081,8872],{"class":158},[27,9083,1325],{"class":33},[27,9085,2459],{"class":2458},[27,9087,1255],{"class":33},[27,9089,9091],{"class":29,"line":9090},64,[27,9092,9093],{"class":33},"                    },\n",[27,9095,9097,9100,9102,9105,9108,9110,9113,9115,9118,9120,9122],{"class":29,"line":9096},65,[27,9098,9099],{"class":198},"                    error",[27,9101,527],{"class":33},[27,9103,9104],{"class":33}," (",[27,9106,9107],{"class":1788},"xhr",[27,9109,231],{"class":33},[27,9111,9112],{"class":1788}," textStatus",[27,9114,231],{"class":33},[27,9116,9117],{"class":1788}," errorThrown",[27,9119,213],{"class":33},[27,9121,1986],{"class":41},[27,9123,514],{"class":33},[27,9125,9127,9130,9132,9134,9136,9138,9141,9143,9146,9148,9150,9152,9154,9156,9158,9160],{"class":29,"line":9126},66,[27,9128,9129],{"class":158},"                        console",[27,9131,216],{"class":33},[27,9133,6013],{"class":198},[27,9135,202],{"class":37},[27,9137,205],{"class":33},[27,9139,9140],{"class":51},"Error! ",[27,9142,205],{"class":33},[27,9144,9145],{"class":33}," +",[27,9147,9112],{"class":158},[27,9149,9145],{"class":33},[27,9151,1248],{"class":33},[27,9153,1248],{"class":33},[27,9155,9145],{"class":33},[27,9157,9117],{"class":158},[27,9159,213],{"class":37},[27,9161,1255],{"class":33},[27,9163,9165],{"class":29,"line":9164},67,[27,9166,9167],{"class":33},"                    }\n",[27,9169,9171,9174],{"class":29,"line":9170},68,[27,9172,9173],{"class":33},"                }",[27,9175,253],{"class":37},[27,9177,9179],{"class":29,"line":9178},69,[27,9180,7272],{"class":33},[27,9182,9184],{"class":29,"line":9183},70,[27,9185,8847],{"class":33},[27,9187,9189],{"class":29,"line":9188},71,[27,9190,2564],{"class":33},[27,9192,9194,9197],{"class":29,"line":9193},72,[27,9195,9196],{"class":37},"    created",[27,9198,237],{"class":33},[27,9200,9202,9204,9206,9208,9210],{"class":29,"line":9201},73,[27,9203,1830],{"class":158},[27,9205,216],{"class":33},[27,9207,8913],{"class":198},[27,9209,202],{"class":37},[27,9211,514],{"class":33},[27,9213,9215,9218,9220,9222,9224,9226],{"class":29,"line":9214},74,[27,9216,9217],{"class":37},"            url",[27,9219,527],{"class":33},[27,9221,205],{"class":33},[27,9223,6824],{"class":51},[27,9225,205],{"class":33},[27,9227,538],{"class":33},[27,9229,9231,9234,9236,9238,9241,9243],{"class":29,"line":9230},75,[27,9232,9233],{"class":37},"            type",[27,9235,527],{"class":33},[27,9237,205],{"class":33},[27,9239,9240],{"class":51},"GET",[27,9242,205],{"class":33},[27,9244,538],{"class":33},[27,9246,9248,9251,9253,9255,9258,9260,9262],{"class":29,"line":9247},76,[27,9249,9250],{"class":198},"            success",[27,9252,527],{"class":33},[27,9254,9104],{"class":33},[27,9256,9257],{"class":1788},"data",[27,9259,213],{"class":33},[27,9261,1986],{"class":41},[27,9263,1328],{"class":33},[27,9265,9267,9269,9271,9273,9276,9278,9281,9283,9285,9287,9289],{"class":29,"line":9266},77,[27,9268,8888],{"class":33},[27,9270,8999],{"class":158},[27,9272,1325],{"class":33},[27,9274,9275],{"class":158}," JSON",[27,9277,216],{"class":33},[27,9279,9280],{"class":198},"parse",[27,9282,202],{"class":37},[27,9284,9257],{"class":158},[27,9286,213],{"class":37},[27,9288,1869],{"class":33},[27,9290,9291],{"class":243}," \u002F\u002F returns array\n",[27,9293,9295],{"class":29,"line":9294},78,[27,9296,3370],{"class":33},[27,9298,9300,9303,9305,9307,9309,9311,9313,9315,9317,9319,9321],{"class":29,"line":9299},79,[27,9301,9302],{"class":198},"            error",[27,9304,527],{"class":33},[27,9306,9104],{"class":33},[27,9308,9107],{"class":1788},[27,9310,231],{"class":33},[27,9312,9112],{"class":1788},[27,9314,231],{"class":33},[27,9316,9117],{"class":1788},[27,9318,213],{"class":33},[27,9320,1986],{"class":41},[27,9322,514],{"class":33},[27,9324,9326,9329,9331,9333,9335,9337,9339,9341,9343,9345,9347,9349,9351,9353,9355,9357],{"class":29,"line":9325},80,[27,9327,9328],{"class":158},"                console",[27,9330,216],{"class":33},[27,9332,6018],{"class":198},[27,9334,202],{"class":37},[27,9336,205],{"class":33},[27,9338,9140],{"class":51},[27,9340,205],{"class":33},[27,9342,9145],{"class":33},[27,9344,9112],{"class":158},[27,9346,9145],{"class":33},[27,9348,1248],{"class":33},[27,9350,1248],{"class":33},[27,9352,9145],{"class":33},[27,9354,9117],{"class":158},[27,9356,213],{"class":37},[27,9358,1255],{"class":33},[27,9360,9362],{"class":29,"line":9361},81,[27,9363,7272],{"class":33},[27,9365,9367,9369],{"class":29,"line":9366},82,[27,9368,5981],{"class":33},[27,9370,253],{"class":37},[27,9372,9374],{"class":29,"line":9373},83,[27,9375,1436],{"class":33},[27,9377,9379],{"class":29,"line":9378},84,[27,9380,1910],{"class":33},[27,9382,9384,9386,9388],{"class":29,"line":9383},85,[27,9385,162],{"class":33},[27,9387,190],{"class":37},[27,9389,69],{"class":33},[13,9391,9392],{},"レンダリングされる際は以下のような処理で一覧画面が表示されます。",[4817,9394,9395,9398,9401],{},[833,9396,9397],{},"createdでDB上の情報を取ってくるAjaxをエンドポイントに飛ばす。",[833,9399,9400],{},"取得したデータをdata()のlistに挿入。",[833,9402,9403],{},"v-forでlist分 の行を表示する。",[13,9405,9406],{},"これをレンダーすると以下のようになります。",[1571,9408],{":src":9409,":width":4383},"'_mix\u002Fsch-2020-10-04-14.40.28-768x285.png'",[13,9411,9412],{},"右側の「操作」で、「編集」でそのアルバムデータの編集画面へ移動、「削除」ではそのデータを削除するAjaxを飛ばします。また、「新規追加」では新規追加画面へ移動します。この画面があれば一通りのデータ操作がブラウザ画面からできるようになります。",[397,9414,9416],{"id":9415},"ロード用削除用エンドポイントを作成","ロード用・削除用エンドポイントを作成",[13,9418,9419,9421],{},[24,9420,6730],{}," コントローラーでは一覧データ・対象のIDを削除するAjaxエンドポイントを作成します。",[17,9423,9425],{"className":6734,"code":9424,"filename":6736,"language":6737,"meta":22,"style":22},"public function view() {\n    $this->render('\u002Fdashboard\u002Fvuetest\u002Fview');\n}\n\n\u002F\u002F ロード用のエンドポイント\npublic function loadData() {\n    if ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'){\n        $db = Database::connection();\n        $db->Execute(\"START TRANSACTION\");\n        $result = $db->fetchAll('SELECT * FROM album ORDER BY ID DESC');\n        $db->Execute(\"COMMIT\");\n        return Core::make('helper\u002Fajax')->sendResult($result);\n    }else{\n        $this->replace('\u002Fpage_not_found');\n    }\n}\n\n\u002F\u002F 削除エンドポイント\npublic function deleteData(){\n    $targetID=$this->post('target');\n    if ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' && !is_null($targetID)){\n        $db = Database::connection();\n        $db->Execute(\"START TRANSACTION\");\n        $db->executeQuery('DELETE FROM album WHERE `ID`=?',array($targetID));\n        $db->Execute(\"COMMIT\");\n    }else{\n        $this->replace('\u002Fpage_not_found');\n    }\n}\n",[24,9426,9427,9432,9437,9441,9445,9450,9455,9460,9464,9468,9472,9476,9480,9484,9489,9493,9497,9501,9506,9511,9516,9521,9525,9529,9534,9538,9542,9546,9550],{"__ignoreMap":22},[27,9428,9429],{"class":29,"line":30},[27,9430,9431],{},"public function view() {\n",[27,9433,9434],{"class":29,"line":72},[27,9435,9436],{},"    $this->render('\u002Fdashboard\u002Fvuetest\u002Fview');\n",[27,9438,9439],{"class":29,"line":137},[27,9440,1910],{},[27,9442,9443],{"class":29,"line":169},[27,9444,182],{"emptyLinePlaceholder":181},[27,9446,9447],{"class":29,"line":178},[27,9448,9449],{},"\u002F\u002F ロード用のエンドポイント\n",[27,9451,9452],{"class":29,"line":185},[27,9453,9454],{},"public function loadData() {\n",[27,9456,9457],{"class":29,"line":195},[27,9458,9459],{},"    if ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'){\n",[27,9461,9462],{"class":29,"line":240},[27,9463,6763],{},[27,9465,9466],{"class":29,"line":247},[27,9467,6768],{},[27,9469,9470],{"class":29,"line":256},[27,9471,7917],{},[27,9473,9474],{"class":29,"line":1156},[27,9475,6778],{},[27,9477,9478],{"class":29,"line":1161},[27,9479,6783],{},[27,9481,9482],{"class":29,"line":1171},[27,9483,8019],{},[27,9485,9486],{"class":29,"line":1181},[27,9487,9488],{},"        $this->replace('\u002Fpage_not_found');\n",[27,9490,9491],{"class":29,"line":1190},[27,9492,1436],{},[27,9494,9495],{"class":29,"line":1216},[27,9496,1910],{},[27,9498,9499],{"class":29,"line":2420},[27,9500,182],{"emptyLinePlaceholder":181},[27,9502,9503],{"class":29,"line":2426},[27,9504,9505],{},"\u002F\u002F 削除エンドポイント\n",[27,9507,9508],{"class":29,"line":2442},[27,9509,9510],{},"public function deleteData(){\n",[27,9512,9513],{"class":29,"line":2465},[27,9514,9515],{},"    $targetID=$this->post('target');\n",[27,9517,9518],{"class":29,"line":2471},[27,9519,9520],{},"    if ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' && !is_null($targetID)){\n",[27,9522,9523],{"class":29,"line":2482},[27,9524,6763],{},[27,9526,9527],{"class":29,"line":2488},[27,9528,6768],{},[27,9530,9531],{"class":29,"line":2494},[27,9532,9533],{},"        $db->executeQuery('DELETE FROM album WHERE `ID`=?',array($targetID));\n",[27,9535,9536],{"class":29,"line":2500},[27,9537,6778],{},[27,9539,9540],{"class":29,"line":2506},[27,9541,8019],{},[27,9543,9544],{"class":29,"line":2511},[27,9545,9488],{},[27,9547,9548],{"class":29,"line":2520},[27,9549,1436],{},[27,9551,9552],{"class":29,"line":2525},[27,9553,1910],{},[397,9555,9557],{"id":9556},"エントリーポイント-を作成","エントリーポイント を作成",[13,9559,9560],{},"上記のコンポーネントをレンダリングするために編集・追加の時のように、一覧用ページにレンダーできるように設定をします。",[13,9562,9563,9566],{},[24,9564,9565],{},"packages\u002Fvuetest\u002Fsingle_pages\u002Fdashboard\u002Fvuetest\u002Fview.php"," に以下のようにエントリーポイントを記述します。",[17,9568,9571],{"className":6734,"code":9569,"filename":9570,"language":6737,"meta":22,"style":22},"\u003C?php\ndefined('C5_EXECUTE') or die('Access Denied.');\n?>\n\n\u003Cdiv id=\"index\">\u003C\u002Fdiv>\n","view.php",[24,9572,9573,9577,9581,9585,9589],{"__ignoreMap":22},[27,9574,9575],{"class":29,"line":30},[27,9576,7318],{},[27,9578,9579],{"class":29,"line":72},[27,9580,7323],{},[27,9582,9583],{"class":29,"line":137},[27,9584,7328],{},[27,9586,9587],{"class":29,"line":169},[27,9588,182],{"emptyLinePlaceholder":181},[27,9590,9591],{"class":29,"line":178},[27,9592,9593],{},"\u003Cdiv id=\"index\">\u003C\u002Fdiv>\n",[13,9595,9596],{},"そしてmain.jsでは",[17,9598,9600],{"className":1228,"code":9599,"filename":390,"language":1231,"meta":22,"style":22},"import Vue from 'vue'\nimport Index from '.\u002Findex.vue'\nimport Add from '.\u002Fadd.vue'\nimport Edit from '.\u002Fedit.vue'\n\n\nVue.config.productionTip = false\n\nfunction renderIfidExits(id,vueRoot){\n  if(document.getElementById(id) !== null){\n    return new Vue({\n      render: h => h(vueRoot),\n    }).$mount('#'+id)\n  }\n}\n\nrenderIfidExits('index',Index);\nrenderIfidExits('add',Add);\nrenderIfidExits('edit',Edit);\n",[24,9601,9602,9616,9632,9646,9660,9664,9668,9684,9688,9704,9730,9742,9762,9786,9790,9794,9798,9817,9835],{"__ignoreMap":22},[27,9603,9604,9606,9608,9610,9612,9614],{"class":29,"line":30},[27,9605,1239],{"class":1238},[27,9607,7482],{"class":158},[27,9609,1245],{"class":1238},[27,9611,1248],{"class":33},[27,9613,4057],{"class":51},[27,9615,2479],{"class":33},[27,9617,9618,9620,9623,9625,9627,9630],{"class":29,"line":72},[27,9619,1239],{"class":1238},[27,9621,9622],{"class":158}," Index ",[27,9624,1245],{"class":1238},[27,9626,1248],{"class":33},[27,9628,9629],{"class":51},".\u002Findex.vue",[27,9631,2479],{"class":33},[27,9633,9634,9636,9638,9640,9642,9644],{"class":29,"line":137},[27,9635,1239],{"class":1238},[27,9637,7497],{"class":158},[27,9639,1245],{"class":1238},[27,9641,1248],{"class":33},[27,9643,7504],{"class":51},[27,9645,2479],{"class":33},[27,9647,9648,9650,9652,9654,9656,9658],{"class":29,"line":169},[27,9649,1239],{"class":1238},[27,9651,7513],{"class":158},[27,9653,1245],{"class":1238},[27,9655,1248],{"class":33},[27,9657,7520],{"class":51},[27,9659,2479],{"class":33},[27,9661,9662],{"class":29,"line":178},[27,9663,182],{"emptyLinePlaceholder":181},[27,9665,9666],{"class":29,"line":185},[27,9667,182],{"emptyLinePlaceholder":181},[27,9669,9670,9672,9674,9676,9678,9680,9682],{"class":29,"line":195},[27,9671,7535],{"class":158},[27,9673,216],{"class":33},[27,9675,5786],{"class":158},[27,9677,216],{"class":33},[27,9679,7544],{"class":158},[27,9681,45],{"class":33},[27,9683,7549],{"class":2458},[27,9685,9686],{"class":29,"line":240},[27,9687,182],{"emptyLinePlaceholder":181},[27,9689,9690,9692,9694,9696,9698,9700,9702],{"class":29,"line":247},[27,9691,234],{"class":41},[27,9693,7560],{"class":198},[27,9695,202],{"class":33},[27,9697,7565],{"class":1788},[27,9699,231],{"class":33},[27,9701,7570],{"class":1788},[27,9703,1796],{"class":33},[27,9705,9706,9708,9710,9712,9714,9716,9718,9720,9722,9724,9726,9728],{"class":29,"line":256},[27,9707,7577],{"class":1238},[27,9709,202],{"class":37},[27,9711,4238],{"class":158},[27,9713,216],{"class":33},[27,9715,4243],{"class":198},[27,9717,202],{"class":37},[27,9719,7565],{"class":158},[27,9721,7592],{"class":37},[27,9723,7595],{"class":33},[27,9725,7598],{"class":33},[27,9727,213],{"class":37},[27,9729,514],{"class":33},[27,9731,9732,9734,9736,9738,9740],{"class":29,"line":1156},[27,9733,1993],{"class":1238},[27,9735,4222],{"class":33},[27,9737,7611],{"class":198},[27,9739,202],{"class":37},[27,9741,514],{"class":33},[27,9743,9744,9746,9748,9750,9752,9754,9756,9758,9760],{"class":29,"line":1161},[27,9745,7620],{"class":198},[27,9747,527],{"class":33},[27,9749,7625],{"class":1788},[27,9751,7628],{"class":41},[27,9753,7625],{"class":198},[27,9755,202],{"class":37},[27,9757,7570],{"class":158},[27,9759,213],{"class":37},[27,9761,538],{"class":33},[27,9763,9764,9766,9768,9770,9772,9774,9776,9778,9780,9782,9784],{"class":29,"line":1171},[27,9765,250],{"class":33},[27,9767,213],{"class":37},[27,9769,216],{"class":33},[27,9771,7649],{"class":198},[27,9773,202],{"class":37},[27,9775,205],{"class":33},[27,9777,7656],{"class":51},[27,9779,205],{"class":33},[27,9781,1853],{"class":33},[27,9783,7565],{"class":158},[27,9785,253],{"class":37},[27,9787,9788],{"class":29,"line":1181},[27,9789,4327],{"class":33},[27,9791,9792],{"class":29,"line":1190},[27,9793,1910],{"class":33},[27,9795,9796],{"class":29,"line":1216},[27,9797,182],{"emptyLinePlaceholder":181},[27,9799,9800,9802,9804,9806,9808,9810,9812,9815],{"class":29,"line":2420},[27,9801,7681],{"class":198},[27,9803,202],{"class":158},[27,9805,205],{"class":33},[27,9807,8688],{"class":51},[27,9809,205],{"class":33},[27,9811,231],{"class":33},[27,9813,9814],{"class":158},"Index)",[27,9816,1255],{"class":33},[27,9818,9819,9821,9823,9825,9827,9829,9831,9833],{"class":29,"line":2426},[27,9820,7681],{"class":198},[27,9822,202],{"class":158},[27,9824,205],{"class":33},[27,9826,7436],{"class":51},[27,9828,205],{"class":33},[27,9830,231],{"class":33},[27,9832,7694],{"class":158},[27,9834,1255],{"class":33},[27,9836,9837,9839,9841,9843,9845,9847,9849,9851],{"class":29,"line":2442},[27,9838,7681],{"class":198},[27,9840,202],{"class":158},[27,9842,205],{"class":33},[27,9844,7707],{"class":51},[27,9846,205],{"class":33},[27,9848,231],{"class":33},[27,9850,7714],{"class":158},[27,9852,1255],{"class":33},[13,9854,9855,9858],{},[24,9856,9857],{},"id='index'","があれば一覧のコンポーネントが出力されるようにしました。",[279,9860,9861],{"id":9861},"実際に操作してみる",[397,9863,9864],{"id":9864},"新しくデータを入れてみる",[1571,9866],{":src":9867,":width":4383},"'_mix\u002Fsch-2020-10-04-14.40.28-768x285-2.png'",[13,9869,9870],{},"右下の「新規追加」は \u002Fdashboard\u002Fvuetest\u002Fadd へリンクしています。そのためクリックすると追加画面が現れて、追加ができます。新規に「テスト２」としておきましょう。",[1571,9872],{":src":9873,":width":4383},"'_mix\u002Fsch-2020-10-04-15.11.23-768x216.png'",[13,9875,9876],{},"それで「登録」を押すとデータ挿入処理と共に一覧へリダイレクトされます。すると、",[1571,9878],{":src":9879,":width":4383},"'_mix\u002Fsch-2020-10-04-15.12.17-768x313.png'",[13,9881,9882],{},"きちんと一覧に反映されました。",[279,9884,9885],{"id":9885},"削除してみる",[13,9887,9888],{},"では削除を押すと、確認ダイアログがでてOKであれば削除APIが走って実行されます。",[1571,9890],{":src":9891,":width":1574,":center":1575},"'_mix\u002Fsch-2020-10-04-15.13.59-768x251.png'",[1571,9893],{":src":9894,":width":4383},"'_mix\u002Fsch-2020-10-04-15.22.02.png'",[13,9896,9897],{},"「変更テスト（id 1）」がDB上から削除されまた、一覧からも消えました。",[397,9899,9900],{"id":9900},"編集してみる",[13,9902,9903],{},"緑色の「編集」は編集画面のURLへリンクしています。vueでは以下のようにv-for内で動的に作成できるようにしています。",[17,9905,9907],{"className":4054,"code":9906,"language":4057,"meta":8067,"style":22},"\u003Ctr v-for=\"val in list\" :key=\"val.id\" tabindex=\"0\">\n...\n    \u003Ctd style=\"vertical-align:middle;\">\n        ...\n        \u003Ca target=\"_blank\" :href=\"'\u002Fdashboard\u002Fvuetest\u002Fedit\u002F'+val.id\">編集\u003C\u002Fa>\n        ...\n    \u003C\u002Ftd>\n...\n\u003C\u002Ftr>\n",[24,9908,9909,9962,9966,9971,9976,9981,9985,9990,9994],{"__ignoreMap":22},[27,9910,9911,9913,9915,9917,9919,9921,9924,9927,9930,9932,9935,9938,9940,9942,9944,9946,9948,9950,9952,9954,9956,9958,9960],{"class":29,"line":30},[27,9912,34],{"class":33},[27,9914,8163],{"class":37},[27,9916,8285],{"class":1238},[27,9918,45],{"class":33},[27,9920,48],{"class":33},[27,9922,9923],{"class":158},"val ",[27,9925,9926],{"class":33},"in",[27,9928,9929],{"class":158}," list",[27,9931,48],{"class":33},[27,9933,9934],{"class":33}," :",[27,9936,9937],{"class":41},"key",[27,9939,45],{"class":33},[27,9941,48],{"class":33},[27,9943,1820],{"class":158},[27,9945,216],{"class":33},[27,9947,7565],{"class":158},[27,9949,48],{"class":33},[27,9951,8309],{"class":41},[27,9953,45],{"class":33},[27,9955,48],{"class":33},[27,9957,8316],{"class":51},[27,9959,48],{"class":33},[27,9961,69],{"class":33},[27,9963,9964],{"class":29,"line":72},[27,9965,3233],{"class":158},[27,9967,9968],{"class":29,"line":137},[27,9969,9970],{"class":158},"    \u003Ctd style=\"vertical-align:middle;\">\n",[27,9972,9973],{"class":29,"line":169},[27,9974,9975],{"class":158},"        ...\n",[27,9977,9978],{"class":29,"line":178},[27,9979,9980],{"class":158},"        \u003Ca target=\"_blank\" :href=\"'\u002Fdashboard\u002Fvuetest\u002Fedit\u002F'+val.id\">編集\u003C\u002Fa>\n",[27,9982,9983],{"class":29,"line":185},[27,9984,9975],{"class":158},[27,9986,9987],{"class":29,"line":195},[27,9988,9989],{"class":158},"    \u003C\u002Ftd>\n",[27,9991,9992],{"class":29,"line":240},[27,9993,3233],{"class":158},[27,9995,9996,9998,10000],{"class":29,"line":247},[27,9997,162],{"class":33},[27,9999,8163],{"class":37},[27,10001,69],{"class":33},[13,10003,10004],{},"実際にリンクしてみると、選択したデータの編集画面が表示されました。",[1571,10006],{":src":10007,":width":1574,":center":1575},"'_mix\u002Fsch-2020-10-04-15.29.21-768x342.png'",[279,10009,10010],{"id":6634},"次回は..",[13,10012,10013],{},"今回の記事では編集画面と一覧画面が完成しました。これで「作成」「読み取り」「更新」「削除」の実装ができました。次の記事では「タイトル」だけでなく「画像データ」「リッチテキスト」といったデータを使えるようにします。",[324,10015,10016],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}html pre.shiki code .sbqyR, html code.shiki .sbqyR{--shiki-default:#FF9CAC}html pre.shiki code .s6cf3, html code.shiki .s6cf3{--shiki-default:#89DDFF;--shiki-default-font-style:italic}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .s7ZW3, html code.shiki .s7ZW3{--shiki-default:#BABED8;--shiki-default-font-style:italic}html pre.shiki code .sC9rS, html code.shiki .sC9rS{--shiki-default:#464B5D;--shiki-default-font-style:italic}",{"title":22,"searchDepth":137,"depth":137,"links":10018},[10019,10024,10025,10028,10033,10036,10039],{"id":6700,"depth":72,"text":6701,"children":10020},[10021,10022,10023],{"id":6714,"depth":137,"text":6715},{"id":7200,"depth":137,"text":7201},{"id":7304,"depth":137,"text":7304},{"id":7743,"depth":72,"text":7743},{"id":7863,"depth":72,"text":7863,"children":10026},[10027],{"id":8042,"depth":137,"text":8042},{"id":8054,"depth":72,"text":8054,"children":10029},[10030,10031,10032],{"id":8060,"depth":137,"text":8060},{"id":9415,"depth":137,"text":9416},{"id":9556,"depth":137,"text":9557},{"id":9861,"depth":72,"text":9861,"children":10034},[10035],{"id":9864,"depth":137,"text":9864},{"id":9885,"depth":72,"text":9885,"children":10037},[10038],{"id":9900,"depth":137,"text":9900},{"id":6634,"depth":72,"text":10010},[862],"2020-10-04","oncrete5にVueCLIを使ってUIを構築する。編集画面と一覧画面。",{},"\u002Fseries\u002Fconcrete5vue-3",{"title":6684,"description":10042},"concrete5vue","Concrete5にVueCLIを使ってUIを構築する。","series\u002Fconcrete5vue-3",[10050,344,4057],"concrete5","_mix\u002Fvuewithconcrete.png","b60sq7M9Lg8p5F-WfOF_VM7wNdrKWtYXHxXbLU-P1O4",{"id":10054,"title":10055,"body":10056,"category":12505,"createdAt":12506,"description":12507,"extension":337,"index":72,"meta":12508,"navigation":181,"path":6692,"publish":181,"seo":12509,"series":10046,"seriesTitle":10047,"stem":12510,"tag":12511,"thumbnail":10051,"updatedAt":338,"__hash__":12512},"series\u002Fseries\u002Fconcrete5vue-2.md","Concrete5にVueCLIを使ってUIを構築する。2【コンポーネント作成・データ登録編】",{"type":10,"value":10057,"toc":12484},[10058,10064,10067,10070,10073,10076,10090,10094,10097,10100,10104,10107,10318,10321,10325,10329,10332,10337,10343,10424,10427,10450,10456,10459,10481,10488,10490,10493,10499,10502,10505,10764,10767,10879,10887,10994,11000,11003,11014,11017,11020,11023,11027,11039,11450,11453,11456,12075,12090,12111,12114,12117,12122,12126,12132,12138,12147,12153,12159,12162,12165,12370,12383,12401,12405,12408,12427,12435,12438,12441,12444,12450,12453,12456,12459,12462,12464,12467,12478,12481],[13,10059,6689,10060,10063],{},[442,10061,10062],{"href":6692},"Concrete5にVueCLIを使ってUIを構築する。1【環境構築編】","の記事の続きを書いていきます。",[13,10065,10066],{},"今回の記事では",[13,10068,10069],{},"どんなフォームを作成するのか。\nテキストインプットを用いたフォームの簡易実装\nデータの保存のバックエンド実装\nを行いたいと思います。ファイルマネージャー、リッチテキストエディタをvueでレンダリングする方法は追加・編集・一覧化が終わった後に書きたいと思います。まずは簡単なフォーム（テキスト）とデータの保存をvueとともに実装していきましょう。",[279,10071,10072],{"id":10072},"最終的に作成するフォーム",[13,10074,10075],{},"「アルバム管理」たるものを作成しようと思います。インスタグラム的な物で、機能は以下の通りです。",[830,10077,10078,10081,10084,10087],{},[833,10079,10080],{},"1つのアルバムに複数枚の画像を自由に登録できる。数は基本的に無制限、最低１枚",[833,10082,10083],{},"画像と一緒にその画像に関する情報をリッチテキストエディタで編集できる。",[833,10085,10086],{},"画像は順番の並び替え、追加、削除が可能。",[833,10088,10089],{},"ページ内ではブロックを通じてアルバムの写真を出力できる。",[397,10091,10093],{"id":10092},"ワイヤーフレーム超簡素","ワイヤーフレーム（超簡素）",[1571,10095],{":src":10096,":width":4383},"'_mix\u002Fvueconcrete2-1.jpeg'",[1571,10098],{":src":10099,":width":4383},"'_mix\u002Fslide-2.jpeg'",[279,10101,10103],{"id":10102},"db構造","DB構造",[13,10105,10106],{},"db.xmlを用いて以下のようなテーブルを作成しておきます。",[17,10108,10113],{"className":10109,"code":10110,"filename":10111,"language":10112,"meta":22,"style":22},"language-xml shiki shiki-themes material-theme-ocean","\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\u003Cschema\n  xmlns=\"http:\u002F\u002Fwww.concrete5.org\u002Fdoctrine-xml\u002F0.5\"\n  xmlns:xsi=\"http:\u002F\u002Fwww.w3.org\u002F2001\u002FXMLSchema-instance\"\n  xsi:schemaLocation=\"http:\u002F\u002Fwww.concrete5.org\u002Fdoctrine-xml\u002F0.5 http:\u002F\u002Fconcrete5.github.io\u002Fdoctrine-xml\u002Fdoctrine-xml-0.5.xsd\">\n\n\u003Ctable name=\"album\">\n    \u003Cfield name=\"id\" type=\"integer\">\n        \u003Cautoincrement\u002F>\n        \u003Ckey\u002F>\n    \u003C\u002Ffield>\n    \u003Cfield name=\"title\" type=\"string\" size=\"255\"\u002F>\n    \u003Cfield name=\"created\" type=\"datetime\">\n      \u003Cdefault value=\"1000-01-01 00:00:00\"\u002F>\n      \u003Cnotnull\u002F>\n    \u003C\u002Ffield>\n    \u003Cfield name=\"modified\" type=\"timestamp\">\n      \u003Cdeftimestamp\u002F>\n      \u003Cnotnull\u002F>\n    \u003C\u002Ffield>\n\u003C\u002Ftable>\n\n\u003Ctable name=\"albumPics\">\n    \u003Cfield name=\"id\" type=\"integer\">\n        \u003Cautoincrement\u002F>\n        \u003Ckey\u002F>\n    \u003C\u002Ffield>\n    \u003Cfield name=\"albumID\" type=\"integer\">\n        \u003Cunsigned\u002F>\n    \u003C\u002Ffield>\n    \u003Cfield name=\"fID\" type=\"integer\">\n        \u003Cunsigned\u002F>\n    \u003C\u002Ffield>\n    \u003Cfield name=\"html\" type=\"text\" size=\"65535\"\u002F>\n    \u003Cfield name=\"created\" type=\"datetime\">\n      \u003Cdefault value=\"1000-01-01 00:00:00\"\u002F>\n      \u003Cnotnull\u002F>\n    \u003C\u002Ffield>\n    \u003Cfield name=\"modified\" type=\"timestamp\">\n      \u003Cdeftimestamp\u002F>\n      \u003Cnotnull\u002F>\n    \u003C\u002Ffield>\n\u003C\u002Ftable>\n\n\u003C\u002Fschema>\n","db.xml","xml",[24,10114,10115,10120,10125,10130,10135,10140,10144,10149,10154,10159,10164,10169,10174,10179,10184,10189,10193,10198,10203,10207,10211,10216,10220,10225,10229,10233,10237,10241,10246,10251,10255,10260,10264,10268,10273,10277,10281,10285,10289,10293,10297,10301,10305,10309,10313],{"__ignoreMap":22},[27,10116,10117],{"class":29,"line":30},[27,10118,10119],{},"\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",[27,10121,10122],{"class":29,"line":72},[27,10123,10124],{},"\u003Cschema\n",[27,10126,10127],{"class":29,"line":137},[27,10128,10129],{},"  xmlns=\"http:\u002F\u002Fwww.concrete5.org\u002Fdoctrine-xml\u002F0.5\"\n",[27,10131,10132],{"class":29,"line":169},[27,10133,10134],{},"  xmlns:xsi=\"http:\u002F\u002Fwww.w3.org\u002F2001\u002FXMLSchema-instance\"\n",[27,10136,10137],{"class":29,"line":178},[27,10138,10139],{},"  xsi:schemaLocation=\"http:\u002F\u002Fwww.concrete5.org\u002Fdoctrine-xml\u002F0.5 http:\u002F\u002Fconcrete5.github.io\u002Fdoctrine-xml\u002Fdoctrine-xml-0.5.xsd\">\n",[27,10141,10142],{"class":29,"line":185},[27,10143,182],{"emptyLinePlaceholder":181},[27,10145,10146],{"class":29,"line":195},[27,10147,10148],{},"\u003Ctable name=\"album\">\n",[27,10150,10151],{"class":29,"line":240},[27,10152,10153],{},"    \u003Cfield name=\"id\" type=\"integer\">\n",[27,10155,10156],{"class":29,"line":247},[27,10157,10158],{},"        \u003Cautoincrement\u002F>\n",[27,10160,10161],{"class":29,"line":256},[27,10162,10163],{},"        \u003Ckey\u002F>\n",[27,10165,10166],{"class":29,"line":1156},[27,10167,10168],{},"    \u003C\u002Ffield>\n",[27,10170,10171],{"class":29,"line":1161},[27,10172,10173],{},"    \u003Cfield name=\"title\" type=\"string\" size=\"255\"\u002F>\n",[27,10175,10176],{"class":29,"line":1171},[27,10177,10178],{},"    \u003Cfield name=\"created\" type=\"datetime\">\n",[27,10180,10181],{"class":29,"line":1181},[27,10182,10183],{},"      \u003Cdefault value=\"1000-01-01 00:00:00\"\u002F>\n",[27,10185,10186],{"class":29,"line":1190},[27,10187,10188],{},"      \u003Cnotnull\u002F>\n",[27,10190,10191],{"class":29,"line":1216},[27,10192,10168],{},[27,10194,10195],{"class":29,"line":2420},[27,10196,10197],{},"    \u003Cfield name=\"modified\" type=\"timestamp\">\n",[27,10199,10200],{"class":29,"line":2426},[27,10201,10202],{},"      \u003Cdeftimestamp\u002F>\n",[27,10204,10205],{"class":29,"line":2442},[27,10206,10188],{},[27,10208,10209],{"class":29,"line":2465},[27,10210,10168],{},[27,10212,10213],{"class":29,"line":2471},[27,10214,10215],{},"\u003C\u002Ftable>\n",[27,10217,10218],{"class":29,"line":2482},[27,10219,182],{"emptyLinePlaceholder":181},[27,10221,10222],{"class":29,"line":2488},[27,10223,10224],{},"\u003Ctable name=\"albumPics\">\n",[27,10226,10227],{"class":29,"line":2494},[27,10228,10153],{},[27,10230,10231],{"class":29,"line":2500},[27,10232,10158],{},[27,10234,10235],{"class":29,"line":2506},[27,10236,10163],{},[27,10238,10239],{"class":29,"line":2511},[27,10240,10168],{},[27,10242,10243],{"class":29,"line":2520},[27,10244,10245],{},"    \u003Cfield name=\"albumID\" type=\"integer\">\n",[27,10247,10248],{"class":29,"line":2525},[27,10249,10250],{},"        \u003Cunsigned\u002F>\n",[27,10252,10253],{"class":29,"line":4},[27,10254,10168],{},[27,10256,10257],{"class":29,"line":2548},[27,10258,10259],{},"    \u003Cfield name=\"fID\" type=\"integer\">\n",[27,10261,10262],{"class":29,"line":2561},[27,10263,10250],{},[27,10265,10266],{"class":29,"line":2567},[27,10267,10168],{},[27,10269,10270],{"class":29,"line":2573},[27,10271,10272],{},"    \u003Cfield name=\"html\" type=\"text\" size=\"65535\"\u002F>\n",[27,10274,10275],{"class":29,"line":2583},[27,10276,10178],{},[27,10278,10279],{"class":29,"line":2596},[27,10280,10183],{},[27,10282,10283],{"class":29,"line":2611},[27,10284,10188],{},[27,10286,10287],{"class":29,"line":2619},[27,10288,10168],{},[27,10290,10291],{"class":29,"line":2625},[27,10292,10197],{},[27,10294,10295],{"class":29,"line":6504},[27,10296,10202],{},[27,10298,10299],{"class":29,"line":6509},[27,10300,10188],{},[27,10302,10303],{"class":29,"line":8720},[27,10304,10168],{},[27,10306,10307],{"class":29,"line":8731},[27,10308,10215],{},[27,10310,10311],{"class":29,"line":8736},[27,10312,182],{"emptyLinePlaceholder":181},[27,10314,10315],{"class":29,"line":8741},[27,10316,10317],{},"\u003C\u002Fschema>\n",[13,10319,10320],{},"1アルバムに対して不定数の画像が登録されるので上記の様なDBになっています。",[279,10322,10324],{"id":10323},"パッケージのビューphpとvueのsrc構成","パッケージのビューPHPとvueのSrc構成",[397,10326,10328],{"id":10327},"パッケージ側phpの下準備","パッケージ側（PHP）の下準備",[13,10330,10331],{},"それではアルバムののフォームを作っていきましょう。前回インストールした「vuetest」にてフォーム用のシングルページを作成します。",[17,10333,10335],{"className":10334,"code":6722,"language":150},[989],[24,10336,6722],{"__ignoreMap":22},[13,10338,10339,10340,10342],{},"そしてシングルページのコントローラーである",[24,10341,6736],{},"で以下の様に入力",[17,10344,10346],{"className":6734,"code":10345,"filename":6736,"language":6737,"meta":22,"style":22},"class Vuetest extends DashboardPageController\n{\n    public $packageHandle = 'vuetest';\n\n   \u002F\u002Fvuetest のシングルページでvueのjsファイルを読み込む様にする。\n　　public function on_start() {\n        $this->requireAsset('package-vue-production');\n    }\n\n    public function view() {\n    }\n\n    \u002F\u002F \u002Fdashboard\u002Fvuetest\u002F に \u002Fadd というURLを追加できる\n    public function add(){\n        $this->render('\u002Fdashboard\u002Fvuetest\u002Fadd');\n    }\n}\n",[24,10347,10348,10353,10357,10362,10366,10371,10376,10381,10385,10389,10394,10398,10402,10407,10412,10416,10420],{"__ignoreMap":22},[27,10349,10350],{"class":29,"line":30},[27,10351,10352],{},"class Vuetest extends DashboardPageController\n",[27,10354,10355],{"class":29,"line":72},[27,10356,514],{},[27,10358,10359],{"class":29,"line":137},[27,10360,10361],{},"    public $packageHandle = 'vuetest';\n",[27,10363,10364],{"class":29,"line":169},[27,10365,182],{"emptyLinePlaceholder":181},[27,10367,10368],{"class":29,"line":178},[27,10369,10370],{},"   \u002F\u002Fvuetest のシングルページでvueのjsファイルを読み込む様にする。\n",[27,10372,10373],{"class":29,"line":185},[27,10374,10375],{},"　　public function on_start() {\n",[27,10377,10378],{"class":29,"line":195},[27,10379,10380],{},"        $this->requireAsset('package-vue-production');\n",[27,10382,10383],{"class":29,"line":240},[27,10384,1436],{},[27,10386,10387],{"class":29,"line":247},[27,10388,182],{"emptyLinePlaceholder":181},[27,10390,10391],{"class":29,"line":256},[27,10392,10393],{},"    public function view() {\n",[27,10395,10396],{"class":29,"line":1156},[27,10397,1436],{},[27,10399,10400],{"class":29,"line":1161},[27,10401,182],{"emptyLinePlaceholder":181},[27,10403,10404],{"class":29,"line":1171},[27,10405,10406],{},"    \u002F\u002F \u002Fdashboard\u002Fvuetest\u002F に \u002Fadd というURLを追加できる\n",[27,10408,10409],{"class":29,"line":1181},[27,10410,10411],{},"    public function add(){\n",[27,10413,10414],{"class":29,"line":1190},[27,10415,8024],{},[27,10417,10418],{"class":29,"line":1216},[27,10419,1436],{},[27,10421,10422],{"class":29,"line":2420},[27,10423,1910],{},[13,10425,10426],{},"すると\u002Fdashboard\u002Fvuetest\u002Fadd というURLを叩くとadd.phpが表示されます。",[17,10428,10431],{"className":6734,"code":10429,"filename":10430,"language":6737,"meta":22,"style":22},"\u003C?php\ndefined('C5_EXECUTE') or die('Access Denied.');\n?>\n\u003Cp>add用のページだよ\u003C\u002Fp>\n","add.php",[24,10432,10433,10437,10441,10445],{"__ignoreMap":22},[27,10434,10435],{"class":29,"line":30},[27,10436,7318],{},[27,10438,10439],{"class":29,"line":72},[27,10440,7323],{},[27,10442,10443],{"class":29,"line":137},[27,10444,7328],{},[27,10446,10447],{"class":29,"line":169},[27,10448,10449],{},"\u003Cp>add用のページだよ\u003C\u002Fp>\n",[13,10451,10452,10453],{},"↓\n",[1571,10454],{":src":10455,":width":5527,":center":1575},"'_mix\u002Fscsh-2020-08-26-21.34.56-768x436.png'",[13,10457,10458],{},"ページの表示が確認できたら、add.phpに以下の様に変更しておきます。",[17,10460,10462],{"className":6734,"code":10461,"filename":10430,"language":6737,"meta":22,"style":22},"\u003C?php\ndefined('C5_EXECUTE') or die('Access Denied.');\n?>\n\u003Cdiv id=\"add\">\u003C\u002Fdiv>\n",[24,10463,10464,10468,10472,10476],{"__ignoreMap":22},[27,10465,10466],{"class":29,"line":30},[27,10467,7318],{},[27,10469,10470],{"class":29,"line":72},[27,10471,7323],{},[27,10473,10474],{"class":29,"line":137},[27,10475,7328],{},[27,10477,10478],{"class":29,"line":169},[27,10479,10480],{},"\u003Cdiv id=\"add\">\u003C\u002Fdiv>\n",[13,10482,10483,10484,10487],{},"この",[24,10485,10486],{},"\u003Cdiv id=”app>\u003C\u002Fdiv>","としたところがvueコンポーネントを流し込むエントリーになります。フォームのビューファイルであるadd.phpはこれだけでおしまいです。",[279,10489,3586],{"id":3586},[13,10491,10492],{},"ではvue側も作っていきましょう。src配下は以下の様に構成します。",[17,10494,10497],{"className":10495,"code":10496,"language":150},[989],"├── src\n   ├── add.vue\n   ├── components\n   │   └── form.vue\n   ├── edit.vue\n   ├── index.vue\n   └── main.js\n",[24,10498,10496],{"__ignoreMap":22},[13,10500,10501],{},"index.vue、add.vue、edit.vueそれぞれが一覧・追加・編集ページのUIを構築します。しかしedit.vueとadd.vueは入力項目が同じで、初期値があるかないかの違いだけです。そのため共通のコンポーネントform.vueを作成します。",[13,10503,10504],{},"form.vueはひとまず以下の様に設定しておきます。",[17,10506,10508],{"className":4054,"code":10507,"filename":7207,"language":4057,"meta":22,"style":22},"\u003Ctemplate>\n    \u003Cdiv class=\"ccm-dashboard-content-inner\">\n        \u003Cp>vueレンダリングテスト\u003C\u002Fp>\n        \n        \u003Cdiv class=\"ccm-dashboard-form-actions-wrapper\">\n            \u003Cdiv class=\"ccm-dashboard-form-actions\">\n                \u003Cbutton class=\"pull-left btn btn-primary\">\n                    一覧へ戻る\n                \u003C\u002Fbutton>\n                \u003Ca href=\"#\" class=\"pull-right btn btn-primary\">\n                    登録\n                \u003C\u002Fa>\n            \u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default {\n    name:'albumForm',\n    props:['isEdit']\n}\n\u003C\u002Fscript>\n",[24,10509,10510,10518,10536,10553,10558,10576,10594,10613,10618,10626,10654,10659,10667,10675,10683,10691,10699,10703,10711,10719,10734,10752,10756],{"__ignoreMap":22},[27,10511,10512,10514,10516],{"class":29,"line":30},[27,10513,34],{"class":33},[27,10515,4066],{"class":37},[27,10517,69],{"class":33},[27,10519,10520,10522,10524,10526,10528,10530,10532,10534],{"class":29,"line":72},[27,10521,75],{"class":33},[27,10523,427],{"class":37},[27,10525,42],{"class":41},[27,10527,45],{"class":33},[27,10529,48],{"class":33},[27,10531,8092],{"class":51},[27,10533,48],{"class":33},[27,10535,69],{"class":33},[27,10537,10538,10540,10542,10544,10547,10549,10551],{"class":29,"line":137},[27,10539,1067],{"class":33},[27,10541,13],{"class":37},[27,10543,155],{"class":33},[27,10545,10546],{"class":158},"vueレンダリングテスト",[27,10548,162],{"class":33},[27,10550,13],{"class":37},[27,10552,69],{"class":33},[27,10554,10555],{"class":29,"line":169},[27,10556,10557],{"class":158},"        \n",[27,10559,10560,10562,10564,10566,10568,10570,10572,10574],{"class":29,"line":178},[27,10561,1067],{"class":33},[27,10563,427],{"class":37},[27,10565,42],{"class":41},[27,10567,45],{"class":33},[27,10569,48],{"class":33},[27,10571,8559],{"class":51},[27,10573,48],{"class":33},[27,10575,69],{"class":33},[27,10577,10578,10580,10582,10584,10586,10588,10590,10592],{"class":29,"line":185},[27,10579,1138],{"class":33},[27,10581,427],{"class":37},[27,10583,42],{"class":41},[27,10585,45],{"class":33},[27,10587,48],{"class":33},[27,10589,8578],{"class":51},[27,10591,48],{"class":33},[27,10593,69],{"class":33},[27,10595,10596,10598,10600,10602,10604,10606,10609,10611],{"class":29,"line":195},[27,10597,8160],{"class":33},[27,10599,1704],{"class":37},[27,10601,42],{"class":41},[27,10603,45],{"class":33},[27,10605,48],{"class":33},[27,10607,10608],{"class":51},"pull-left btn btn-primary",[27,10610,48],{"class":33},[27,10612,69],{"class":33},[27,10614,10615],{"class":29,"line":240},[27,10616,10617],{"class":158},"                    一覧へ戻る\n",[27,10619,10620,10622,10624],{"class":29,"line":247},[27,10621,8255],{"class":33},[27,10623,1704],{"class":37},[27,10625,69],{"class":33},[27,10627,10628,10630,10632,10634,10636,10638,10640,10642,10644,10646,10648,10650,10652],{"class":29,"line":256},[27,10629,8160],{"class":33},[27,10631,442],{"class":37},[27,10633,2866],{"class":41},[27,10635,45],{"class":33},[27,10637,48],{"class":33},[27,10639,7656],{"class":51},[27,10641,48],{"class":33},[27,10643,42],{"class":41},[27,10645,45],{"class":33},[27,10647,48],{"class":33},[27,10649,8608],{"class":51},[27,10651,48],{"class":33},[27,10653,69],{"class":33},[27,10655,10656],{"class":29,"line":1156},[27,10657,10658],{"class":158},"                    登録\n",[27,10660,10661,10663,10665],{"class":29,"line":1161},[27,10662,8255],{"class":33},[27,10664,442],{"class":37},[27,10666,69],{"class":33},[27,10668,10669,10671,10673],{"class":29,"line":1171},[27,10670,1164],{"class":33},[27,10672,427],{"class":37},[27,10674,69],{"class":33},[27,10676,10677,10679,10681],{"class":29,"line":1181},[27,10678,1174],{"class":33},[27,10680,427],{"class":37},[27,10682,69],{"class":33},[27,10684,10685,10687,10689],{"class":29,"line":1190},[27,10686,1107],{"class":33},[27,10688,427],{"class":37},[27,10690,69],{"class":33},[27,10692,10693,10695,10697],{"class":29,"line":1216},[27,10694,162],{"class":33},[27,10696,4066],{"class":37},[27,10698,69],{"class":33},[27,10700,10701],{"class":29,"line":2420},[27,10702,182],{"emptyLinePlaceholder":181},[27,10704,10705,10707,10709],{"class":29,"line":2426},[27,10706,34],{"class":33},[27,10708,190],{"class":37},[27,10710,69],{"class":33},[27,10712,10713,10715,10717],{"class":29,"line":2442},[27,10714,1773],{"class":1238},[27,10716,1776],{"class":1238},[27,10718,1328],{"class":33},[27,10720,10721,10723,10725,10727,10730,10732],{"class":29,"line":2465},[27,10722,7429],{"class":37},[27,10724,527],{"class":33},[27,10726,205],{"class":33},[27,10728,10729],{"class":51},"albumForm",[27,10731,205],{"class":33},[27,10733,538],{"class":33},[27,10735,10736,10739,10741,10744,10746,10748,10750],{"class":29,"line":2471},[27,10737,10738],{"class":37},"    props",[27,10740,527],{"class":33},[27,10742,10743],{"class":158},"[",[27,10745,205],{"class":33},[27,10747,7293],{"class":51},[27,10749,205],{"class":33},[27,10751,3445],{"class":158},[27,10753,10754],{"class":29,"line":2482},[27,10755,1910],{"class":33},[27,10757,10758,10760,10762],{"class":29,"line":2488},[27,10759,162],{"class":33},[27,10761,190],{"class":37},[27,10763,69],{"class":33},[13,10765,10766],{},"そしてadd.vueでこのform.vueを読み込んで",[17,10768,10771],{"className":4054,"code":10769,"filename":10770,"language":4057,"meta":22,"style":22},"\u003Ctemplate>\n    \u003CAlbumForm :isEdit=\"false\"\u002F>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport AlbumForm from '.\u002Fcomponents\u002Fform';\nexport default {\n    name:'add',\n    components:{AlbumForm}\n}\n\u003C\u002Fscript>\n","app.vue",[24,10772,10773,10781,10799,10807,10811,10819,10835,10843,10857,10867,10871],{"__ignoreMap":22},[27,10774,10775,10777,10779],{"class":29,"line":30},[27,10776,34],{"class":33},[27,10778,4066],{"class":37},[27,10780,69],{"class":33},[27,10782,10783,10785,10787,10789,10791,10793,10795,10797],{"class":29,"line":72},[27,10784,75],{"class":33},[27,10786,7365],{"class":37},[27,10788,7368],{"class":41},[27,10790,45],{"class":33},[27,10792,48],{"class":33},[27,10794,8878],{"class":51},[27,10796,48],{"class":33},[27,10798,6960],{"class":33},[27,10800,10801,10803,10805],{"class":29,"line":137},[27,10802,162],{"class":33},[27,10804,4066],{"class":37},[27,10806,69],{"class":33},[27,10808,10809],{"class":29,"line":169},[27,10810,182],{"emptyLinePlaceholder":181},[27,10812,10813,10815,10817],{"class":29,"line":178},[27,10814,34],{"class":33},[27,10816,190],{"class":37},[27,10818,69],{"class":33},[27,10820,10821,10823,10825,10827,10829,10831,10833],{"class":29,"line":185},[27,10822,1239],{"class":1238},[27,10824,7405],{"class":158},[27,10826,1245],{"class":1238},[27,10828,1248],{"class":33},[27,10830,7412],{"class":51},[27,10832,205],{"class":33},[27,10834,1255],{"class":33},[27,10836,10837,10839,10841],{"class":29,"line":195},[27,10838,1773],{"class":1238},[27,10840,1776],{"class":1238},[27,10842,1328],{"class":33},[27,10844,10845,10847,10849,10851,10853,10855],{"class":29,"line":240},[27,10846,7429],{"class":37},[27,10848,527],{"class":33},[27,10850,205],{"class":33},[27,10852,7436],{"class":51},[27,10854,205],{"class":33},[27,10856,538],{"class":33},[27,10858,10859,10861,10863,10865],{"class":29,"line":247},[27,10860,7445],{"class":37},[27,10862,7448],{"class":33},[27,10864,7365],{"class":158},[27,10866,1910],{"class":33},[27,10868,10869],{"class":29,"line":256},[27,10870,1910],{"class":33},[27,10872,10873,10875,10877],{"class":29,"line":1156},[27,10874,162],{"class":33},[27,10876,190],{"class":37},[27,10878,69],{"class":33},[13,10880,10881,3466,10883,10886],{},[24,10882,390],{},[24,10884,10885],{},"id=\"app\"","の要素にマウントする様にします。",[17,10888,10890],{"className":1228,"code":10889,"filename":390,"language":1231,"meta":22,"style":22},"import Vue from 'vue'\nimport Add from '.\u002Fadd.vue'\n\nVue.config.productionTip = false\n\nnew Vue({\n  render: h => h(Add),\n}).$mount('#add')\n",[24,10891,10892,10906,10920,10924,10940,10944,10955,10973],{"__ignoreMap":22},[27,10893,10894,10896,10898,10900,10902,10904],{"class":29,"line":30},[27,10895,1239],{"class":1238},[27,10897,7482],{"class":158},[27,10899,1245],{"class":1238},[27,10901,1248],{"class":33},[27,10903,4057],{"class":51},[27,10905,2479],{"class":33},[27,10907,10908,10910,10912,10914,10916,10918],{"class":29,"line":72},[27,10909,1239],{"class":1238},[27,10911,7497],{"class":158},[27,10913,1245],{"class":1238},[27,10915,1248],{"class":33},[27,10917,7504],{"class":51},[27,10919,2479],{"class":33},[27,10921,10922],{"class":29,"line":137},[27,10923,182],{"emptyLinePlaceholder":181},[27,10925,10926,10928,10930,10932,10934,10936,10938],{"class":29,"line":169},[27,10927,7535],{"class":158},[27,10929,216],{"class":33},[27,10931,5786],{"class":158},[27,10933,216],{"class":33},[27,10935,7544],{"class":158},[27,10937,45],{"class":33},[27,10939,7549],{"class":2458},[27,10941,10942],{"class":29,"line":178},[27,10943,182],{"emptyLinePlaceholder":181},[27,10945,10946,10949,10951,10953],{"class":29,"line":185},[27,10947,10948],{"class":33},"new",[27,10950,7611],{"class":198},[27,10952,202],{"class":158},[27,10954,514],{"class":33},[27,10956,10957,10960,10962,10964,10966,10968,10971],{"class":29,"line":195},[27,10958,10959],{"class":198},"  render",[27,10961,527],{"class":33},[27,10963,7625],{"class":1788},[27,10965,7628],{"class":41},[27,10967,7625],{"class":198},[27,10969,10970],{"class":158},"(Add)",[27,10972,538],{"class":33},[27,10974,10975,10977,10979,10981,10983,10985,10987,10990,10992],{"class":29,"line":240},[27,10976,661],{"class":33},[27,10978,213],{"class":158},[27,10980,216],{"class":33},[27,10982,7649],{"class":198},[27,10984,202],{"class":158},[27,10986,205],{"class":33},[27,10988,10989],{"class":51},"#add",[27,10991,205],{"class":33},[27,10993,253],{"class":158},[13,10995,10996,10997,10999],{},"こうしてビルドをしてみましょう。そして",[24,10998,8597],{},"へ移動してみると",[1571,11001],{":src":11002,":width":4383},"'_mix\u002Fsch-2020-08-26-22.48.54-768x529.png'",[13,11004,11005,11006,1454,11008,11010,11011,11013],{},"しっかりと",[24,11007,10885],{},[24,11009,7207],{},"がレンダリングされました。ではこの",[24,11012,7207],{},"にガシガシとフォームUIを作っていきましょう！",[279,11015,11016],{"id":11016},"まずはタイトルを入力するだけのものを作成",[13,11018,11019],{},"最終的なフォームにはリッチテキストエディタとファイルセレクターなど、concrete5で用いられているフォームをvueで実装したいと思います。しかし、その２つはなかなか曲者で今回の記事で説明すると長くなるので次回に話します。",[13,11021,11022],{},"まずはvueとPHP(concrete5)でどう連携させてDBにデータを挿入するかの全体的な流れについて説明するとともに、タイトル部分（プレーンテキスト）を追加できる様にしていきましょう。",[397,11024,11026],{"id":11025},"postでバックに値を送る","POSTでバックに値を送る",[13,11028,11029,11030,302,11032,11035,11036,11038],{},"vueで構築したUIで入力された値はどうやってバックに渡すか？簡単です。フォームを作る時の様に",[24,11031,78],{},[24,11033,11034],{},"\u003Cform method=\"post\">\u003C\u002Fform>","で囲んであげて、送信用のsubmitボタンを作るだけです。今回のパッケージではPOSTの値をAjaxとかで送るのでなく、普通にsubmitでサーバーに送信する様にしました。以下の様に",[24,11037,7207],{},"を変えます。",[17,11040,11042],{"className":4054,"code":11041,"filename":7207,"language":4057,"meta":22,"style":22},"\u003Ctemplate>\n    \u003Cdiv class=\"ccm-dashboard-content-inner\">\n        \u003Ch3>アルバム新規追加\u003C\u002Fh3>\n        \u003Chr>\n        \u003Cform method=\"post\">\n            \u003Clabel for=\"title\" class=\"control-label\">アルバムタイトル\u003C\u002Flabel>\n            \u003Cinput type=\"text\" name=\"title\" class=\"form-control ccm-input-text\" v-model=\"title\">\n\n            \u003Cdiv class=\"ccm-dashboard-form-actions-wrapper\">\n                \u003Cdiv class=\"ccm-dashboard-form-actions\">\n                    \u003Cbutton class=\"pull-left btn btn-primary\">\n                        一覧へ戻る\n                    \u003C\u002Fbutton>\n                    \u003Cinput value=\"登録\" type=\"submit\" class=\"pull-right btn btn-primary\">\n                \u003C\u002Fdiv>\n            \u003C\u002Fdiv>\n        \u003C\u002Fform>\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default {\n    name:'albumForm',\n    props:['isEdit'],\n    data(){\n        return{\n            title:''\n        }\n    }\n}\n\u003C\u002Fscript>\n",[24,11043,11044,11052,11070,11087,11095,11116,11154,11204,11208,11226,11244,11262,11267,11275,11315,11323,11331,11339,11347,11355,11359,11367,11375,11389,11408,11414,11420,11430,11434,11438,11442],{"__ignoreMap":22},[27,11045,11046,11048,11050],{"class":29,"line":30},[27,11047,34],{"class":33},[27,11049,4066],{"class":37},[27,11051,69],{"class":33},[27,11053,11054,11056,11058,11060,11062,11064,11066,11068],{"class":29,"line":72},[27,11055,75],{"class":33},[27,11057,427],{"class":37},[27,11059,42],{"class":41},[27,11061,45],{"class":33},[27,11063,48],{"class":33},[27,11065,8092],{"class":51},[27,11067,48],{"class":33},[27,11069,69],{"class":33},[27,11071,11072,11074,11076,11078,11081,11083,11085],{"class":29,"line":137},[27,11073,1067],{"class":33},[27,11075,397],{"class":37},[27,11077,155],{"class":33},[27,11079,11080],{"class":158},"アルバム新規追加",[27,11082,162],{"class":33},[27,11084,397],{"class":37},[27,11086,69],{"class":33},[27,11088,11089,11091,11093],{"class":29,"line":169},[27,11090,1067],{"class":33},[27,11092,8120],{"class":37},[27,11094,69],{"class":33},[27,11096,11097,11099,11102,11105,11107,11109,11112,11114],{"class":29,"line":178},[27,11098,1067],{"class":33},[27,11100,11101],{"class":37},"form",[27,11103,11104],{"class":41}," method",[27,11106,45],{"class":33},[27,11108,48],{"class":33},[27,11110,11111],{"class":51},"post",[27,11113,48],{"class":33},[27,11115,69],{"class":33},[27,11117,11118,11120,11122,11124,11126,11128,11130,11132,11134,11136,11138,11141,11143,11145,11148,11150,11152],{"class":29,"line":185},[27,11119,1138],{"class":33},[27,11121,38],{"class":37},[27,11123,57],{"class":41},[27,11125,45],{"class":33},[27,11127,48],{"class":33},[27,11129,1091],{"class":51},[27,11131,48],{"class":33},[27,11133,42],{"class":41},[27,11135,45],{"class":33},[27,11137,48],{"class":33},[27,11139,11140],{"class":51},"control-label",[27,11142,48],{"class":33},[27,11144,155],{"class":33},[27,11146,11147],{"class":158},"アルバムタイトル",[27,11149,162],{"class":33},[27,11151,38],{"class":37},[27,11153,69],{"class":33},[27,11155,11156,11158,11160,11162,11164,11166,11168,11170,11172,11174,11176,11178,11180,11182,11184,11186,11189,11191,11194,11196,11198,11200,11202],{"class":29,"line":195},[27,11157,1138],{"class":33},[27,11159,78],{"class":37},[27,11161,104],{"class":41},[27,11163,45],{"class":33},[27,11165,48],{"class":33},[27,11167,150],{"class":51},[27,11169,48],{"class":33},[27,11171,116],{"class":41},[27,11173,45],{"class":33},[27,11175,48],{"class":33},[27,11177,1091],{"class":51},[27,11179,48],{"class":33},[27,11181,42],{"class":41},[27,11183,45],{"class":33},[27,11185,48],{"class":33},[27,11187,11188],{"class":51},"form-control ccm-input-text",[27,11190,48],{"class":33},[27,11192,11193],{"class":41}," v-model",[27,11195,45],{"class":33},[27,11197,48],{"class":33},[27,11199,1091],{"class":51},[27,11201,48],{"class":33},[27,11203,69],{"class":33},[27,11205,11206],{"class":29,"line":240},[27,11207,182],{"emptyLinePlaceholder":181},[27,11209,11210,11212,11214,11216,11218,11220,11222,11224],{"class":29,"line":247},[27,11211,1138],{"class":33},[27,11213,427],{"class":37},[27,11215,42],{"class":41},[27,11217,45],{"class":33},[27,11219,48],{"class":33},[27,11221,8559],{"class":51},[27,11223,48],{"class":33},[27,11225,69],{"class":33},[27,11227,11228,11230,11232,11234,11236,11238,11240,11242],{"class":29,"line":256},[27,11229,8160],{"class":33},[27,11231,427],{"class":37},[27,11233,42],{"class":41},[27,11235,45],{"class":33},[27,11237,48],{"class":33},[27,11239,8578],{"class":51},[27,11241,48],{"class":33},[27,11243,69],{"class":33},[27,11245,11246,11248,11250,11252,11254,11256,11258,11260],{"class":29,"line":1156},[27,11247,8170],{"class":33},[27,11249,1704],{"class":37},[27,11251,42],{"class":41},[27,11253,45],{"class":33},[27,11255,48],{"class":33},[27,11257,10608],{"class":51},[27,11259,48],{"class":33},[27,11261,69],{"class":33},[27,11263,11264],{"class":29,"line":1161},[27,11265,11266],{"class":158},"                        一覧へ戻る\n",[27,11268,11269,11271,11273],{"class":29,"line":1171},[27,11270,8512],{"class":33},[27,11272,1704],{"class":37},[27,11274,69],{"class":33},[27,11276,11277,11279,11281,11283,11285,11287,11290,11292,11294,11296,11298,11301,11303,11305,11307,11309,11311,11313],{"class":29,"line":1181},[27,11278,8170],{"class":33},[27,11280,78],{"class":37},[27,11282,127],{"class":41},[27,11284,45],{"class":33},[27,11286,48],{"class":33},[27,11288,11289],{"class":51},"登録",[27,11291,48],{"class":33},[27,11293,104],{"class":41},[27,11295,45],{"class":33},[27,11297,48],{"class":33},[27,11299,11300],{"class":51},"submit",[27,11302,48],{"class":33},[27,11304,42],{"class":41},[27,11306,45],{"class":33},[27,11308,48],{"class":33},[27,11310,8608],{"class":51},[27,11312,48],{"class":33},[27,11314,69],{"class":33},[27,11316,11317,11319,11321],{"class":29,"line":1190},[27,11318,8255],{"class":33},[27,11320,427],{"class":37},[27,11322,69],{"class":33},[27,11324,11325,11327,11329],{"class":29,"line":1216},[27,11326,1164],{"class":33},[27,11328,427],{"class":37},[27,11330,69],{"class":33},[27,11332,11333,11335,11337],{"class":29,"line":2420},[27,11334,1174],{"class":33},[27,11336,11101],{"class":37},[27,11338,69],{"class":33},[27,11340,11341,11343,11345],{"class":29,"line":2426},[27,11342,1107],{"class":33},[27,11344,427],{"class":37},[27,11346,69],{"class":33},[27,11348,11349,11351,11353],{"class":29,"line":2442},[27,11350,162],{"class":33},[27,11352,4066],{"class":37},[27,11354,69],{"class":33},[27,11356,11357],{"class":29,"line":2465},[27,11358,182],{"emptyLinePlaceholder":181},[27,11360,11361,11363,11365],{"class":29,"line":2471},[27,11362,34],{"class":33},[27,11364,190],{"class":37},[27,11366,69],{"class":33},[27,11368,11369,11371,11373],{"class":29,"line":2482},[27,11370,1773],{"class":1238},[27,11372,1776],{"class":1238},[27,11374,1328],{"class":33},[27,11376,11377,11379,11381,11383,11385,11387],{"class":29,"line":2488},[27,11378,7429],{"class":37},[27,11380,527],{"class":33},[27,11382,205],{"class":33},[27,11384,10729],{"class":51},[27,11386,205],{"class":33},[27,11388,538],{"class":33},[27,11390,11391,11393,11395,11397,11399,11401,11403,11406],{"class":29,"line":2494},[27,11392,10738],{"class":37},[27,11394,527],{"class":33},[27,11396,10743],{"class":158},[27,11398,205],{"class":33},[27,11400,7293],{"class":51},[27,11402,205],{"class":33},[27,11404,11405],{"class":158},"]",[27,11407,538],{"class":33},[27,11409,11410,11412],{"class":29,"line":2500},[27,11411,8697],{"class":37},[27,11413,237],{"class":33},[27,11415,11416,11418],{"class":29,"line":2506},[27,11417,6382],{"class":1238},[27,11419,514],{"class":33},[27,11421,11422,11425,11427],{"class":29,"line":2511},[27,11423,11424],{"class":37},"            title",[27,11426,527],{"class":33},[27,11428,11429],{"class":33},"''\n",[27,11431,11432],{"class":29,"line":2520},[27,11433,3388],{"class":33},[27,11435,11436],{"class":29,"line":2525},[27,11437,1436],{"class":33},[27,11439,11440],{"class":29,"line":4},[27,11441,1910],{"class":33},[27,11443,11444,11446,11448],{"class":29,"line":2548},[27,11445,162],{"class":33},[27,11447,190],{"class":37},[27,11449,69],{"class":33},[397,11451,11452],{"id":11452},"フロントバリデーションを実装",[13,11454,11455],{},"フォームから入力された値をバリデーションチェックします。しかし今回はせっかくvueを使っているのでフロントでバリデーションをしてあげましょう。例えばtitleが空でないかをチェックする場合以下の様にします。",[17,11457,11459],{"className":4054,"code":11458,"filename":7207,"language":4057,"meta":22,"style":22},"\u003Ctemplate>\n    \u003Cdiv class=\"ccm-dashboard-content-inner\">\n        \u003Ch3>アルバム新規追加\u003C\u002Fh3>\n        \u003Chr>\n        \u003Cform method=\"post\" @submit=\"checkForm\">\n            \u003Clabel for=\"title\" class=\"control-label\">アルバムタイトル\u003C\u002Flabel>\n            \u003Cinput type=\"text\" name=\"title\" class=\"form-control ccm-input-text\" v-model=\"title\">\n            \u003Cp class=\"text-danger\" v-if=\"errTitle.length>0\">{{errTitle}}\u003C\u002Fp>\n\n            \u003Cdiv class=\"ccm-dashboard-form-actions-wrapper\">\n                \u003Cdiv class=\"ccm-dashboard-form-actions\">\n                    \u003Cbutton class=\"pull-left btn btn-primary\">\n                        一覧へ戻る\n                    \u003C\u002Fbutton>\n                    \u003Cinput value=\"登録\" type=\"submit\" class=\"pull-right btn btn-primary\">\n                \u003C\u002Fdiv>\n            \u003C\u002Fdiv>\n        \u003C\u002Fform>\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\u003Cscript>\nexport default {\n    name:'albumForm',\n    props:['isEdit'],\n    data(){\n        return{\n            title:'',\n            errTitle:''\n        }\n    },\n    methods:{\n        checkForm($event){\n            if(this.title.length >0){\n                return true;\n            }else{\n                $event.preventDefault();\n                ConcreteAlert.error({\n                    title:'入力項目に誤りがあります。',\n                    message:'アルバムタイトルが入力されていません。',\n                    delay:5000\n                })\n                this.errTitle = \"タイトルを入力してください。\"\n            }\n        }\n    }\n}\n\u003C\u002Fscript>\n",[24,11460,11461,11469,11487,11503,11511,11541,11577,11625,11665,11669,11687,11705,11723,11727,11735,11773,11781,11789,11797,11805,11813,11821,11829,11843,11861,11867,11873,11883,11892,11896,11900,11906,11918,11941,11950,11960,11974,11987,12003,12019,12029,12035,12051,12055,12059,12063,12067],{"__ignoreMap":22},[27,11462,11463,11465,11467],{"class":29,"line":30},[27,11464,34],{"class":33},[27,11466,4066],{"class":37},[27,11468,69],{"class":33},[27,11470,11471,11473,11475,11477,11479,11481,11483,11485],{"class":29,"line":72},[27,11472,75],{"class":33},[27,11474,427],{"class":37},[27,11476,42],{"class":41},[27,11478,45],{"class":33},[27,11480,48],{"class":33},[27,11482,8092],{"class":51},[27,11484,48],{"class":33},[27,11486,69],{"class":33},[27,11488,11489,11491,11493,11495,11497,11499,11501],{"class":29,"line":137},[27,11490,1067],{"class":33},[27,11492,397],{"class":37},[27,11494,155],{"class":33},[27,11496,11080],{"class":158},[27,11498,162],{"class":33},[27,11500,397],{"class":37},[27,11502,69],{"class":33},[27,11504,11505,11507,11509],{"class":29,"line":169},[27,11506,1067],{"class":33},[27,11508,8120],{"class":37},[27,11510,69],{"class":33},[27,11512,11513,11515,11517,11519,11521,11523,11525,11527,11530,11532,11534,11537,11539],{"class":29,"line":178},[27,11514,1067],{"class":33},[27,11516,11101],{"class":37},[27,11518,11104],{"class":41},[27,11520,45],{"class":33},[27,11522,48],{"class":33},[27,11524,11111],{"class":51},[27,11526,48],{"class":33},[27,11528,11529],{"class":41}," @submit",[27,11531,45],{"class":33},[27,11533,48],{"class":33},[27,11535,11536],{"class":51},"checkForm",[27,11538,48],{"class":33},[27,11540,69],{"class":33},[27,11542,11543,11545,11547,11549,11551,11553,11555,11557,11559,11561,11563,11565,11567,11569,11571,11573,11575],{"class":29,"line":185},[27,11544,1138],{"class":33},[27,11546,38],{"class":37},[27,11548,57],{"class":41},[27,11550,45],{"class":33},[27,11552,48],{"class":33},[27,11554,1091],{"class":51},[27,11556,48],{"class":33},[27,11558,42],{"class":41},[27,11560,45],{"class":33},[27,11562,48],{"class":33},[27,11564,11140],{"class":51},[27,11566,48],{"class":33},[27,11568,155],{"class":33},[27,11570,11147],{"class":158},[27,11572,162],{"class":33},[27,11574,38],{"class":37},[27,11576,69],{"class":33},[27,11578,11579,11581,11583,11585,11587,11589,11591,11593,11595,11597,11599,11601,11603,11605,11607,11609,11611,11613,11615,11617,11619,11621,11623],{"class":29,"line":195},[27,11580,1138],{"class":33},[27,11582,78],{"class":37},[27,11584,104],{"class":41},[27,11586,45],{"class":33},[27,11588,48],{"class":33},[27,11590,150],{"class":51},[27,11592,48],{"class":33},[27,11594,116],{"class":41},[27,11596,45],{"class":33},[27,11598,48],{"class":33},[27,11600,1091],{"class":51},[27,11602,48],{"class":33},[27,11604,42],{"class":41},[27,11606,45],{"class":33},[27,11608,48],{"class":33},[27,11610,11188],{"class":51},[27,11612,48],{"class":33},[27,11614,11193],{"class":41},[27,11616,45],{"class":33},[27,11618,48],{"class":33},[27,11620,1091],{"class":51},[27,11622,48],{"class":33},[27,11624,69],{"class":33},[27,11626,11627,11629,11631,11633,11635,11637,11640,11642,11645,11647,11649,11652,11654,11656,11659,11661,11663],{"class":29,"line":240},[27,11628,1138],{"class":33},[27,11630,13],{"class":37},[27,11632,42],{"class":41},[27,11634,45],{"class":33},[27,11636,48],{"class":33},[27,11638,11639],{"class":51},"text-danger",[27,11641,48],{"class":33},[27,11643,11644],{"class":41}," v-if",[27,11646,45],{"class":33},[27,11648,48],{"class":33},[27,11650,11651],{"class":51},"errTitle.length>0",[27,11653,48],{"class":33},[27,11655,155],{"class":33},[27,11657,11658],{"class":158},"{{errTitle}}",[27,11660,162],{"class":33},[27,11662,13],{"class":37},[27,11664,69],{"class":33},[27,11666,11667],{"class":29,"line":247},[27,11668,182],{"emptyLinePlaceholder":181},[27,11670,11671,11673,11675,11677,11679,11681,11683,11685],{"class":29,"line":256},[27,11672,1138],{"class":33},[27,11674,427],{"class":37},[27,11676,42],{"class":41},[27,11678,45],{"class":33},[27,11680,48],{"class":33},[27,11682,8559],{"class":51},[27,11684,48],{"class":33},[27,11686,69],{"class":33},[27,11688,11689,11691,11693,11695,11697,11699,11701,11703],{"class":29,"line":1156},[27,11690,8160],{"class":33},[27,11692,427],{"class":37},[27,11694,42],{"class":41},[27,11696,45],{"class":33},[27,11698,48],{"class":33},[27,11700,8578],{"class":51},[27,11702,48],{"class":33},[27,11704,69],{"class":33},[27,11706,11707,11709,11711,11713,11715,11717,11719,11721],{"class":29,"line":1161},[27,11708,8170],{"class":33},[27,11710,1704],{"class":37},[27,11712,42],{"class":41},[27,11714,45],{"class":33},[27,11716,48],{"class":33},[27,11718,10608],{"class":51},[27,11720,48],{"class":33},[27,11722,69],{"class":33},[27,11724,11725],{"class":29,"line":1171},[27,11726,11266],{"class":158},[27,11728,11729,11731,11733],{"class":29,"line":1181},[27,11730,8512],{"class":33},[27,11732,1704],{"class":37},[27,11734,69],{"class":33},[27,11736,11737,11739,11741,11743,11745,11747,11749,11751,11753,11755,11757,11759,11761,11763,11765,11767,11769,11771],{"class":29,"line":1190},[27,11738,8170],{"class":33},[27,11740,78],{"class":37},[27,11742,127],{"class":41},[27,11744,45],{"class":33},[27,11746,48],{"class":33},[27,11748,11289],{"class":51},[27,11750,48],{"class":33},[27,11752,104],{"class":41},[27,11754,45],{"class":33},[27,11756,48],{"class":33},[27,11758,11300],{"class":51},[27,11760,48],{"class":33},[27,11762,42],{"class":41},[27,11764,45],{"class":33},[27,11766,48],{"class":33},[27,11768,8608],{"class":51},[27,11770,48],{"class":33},[27,11772,69],{"class":33},[27,11774,11775,11777,11779],{"class":29,"line":1216},[27,11776,8255],{"class":33},[27,11778,427],{"class":37},[27,11780,69],{"class":33},[27,11782,11783,11785,11787],{"class":29,"line":2420},[27,11784,1164],{"class":33},[27,11786,427],{"class":37},[27,11788,69],{"class":33},[27,11790,11791,11793,11795],{"class":29,"line":2426},[27,11792,1174],{"class":33},[27,11794,11101],{"class":37},[27,11796,69],{"class":33},[27,11798,11799,11801,11803],{"class":29,"line":2442},[27,11800,1107],{"class":33},[27,11802,427],{"class":37},[27,11804,69],{"class":33},[27,11806,11807,11809,11811],{"class":29,"line":2465},[27,11808,162],{"class":33},[27,11810,4066],{"class":37},[27,11812,69],{"class":33},[27,11814,11815,11817,11819],{"class":29,"line":2471},[27,11816,34],{"class":33},[27,11818,190],{"class":37},[27,11820,69],{"class":33},[27,11822,11823,11825,11827],{"class":29,"line":2482},[27,11824,1773],{"class":1238},[27,11826,1776],{"class":1238},[27,11828,1328],{"class":33},[27,11830,11831,11833,11835,11837,11839,11841],{"class":29,"line":2488},[27,11832,7429],{"class":37},[27,11834,527],{"class":33},[27,11836,205],{"class":33},[27,11838,10729],{"class":51},[27,11840,205],{"class":33},[27,11842,538],{"class":33},[27,11844,11845,11847,11849,11851,11853,11855,11857,11859],{"class":29,"line":2494},[27,11846,10738],{"class":37},[27,11848,527],{"class":33},[27,11850,10743],{"class":158},[27,11852,205],{"class":33},[27,11854,7293],{"class":51},[27,11856,205],{"class":33},[27,11858,11405],{"class":158},[27,11860,538],{"class":33},[27,11862,11863,11865],{"class":29,"line":2500},[27,11864,8697],{"class":37},[27,11866,237],{"class":33},[27,11868,11869,11871],{"class":29,"line":2506},[27,11870,6382],{"class":1238},[27,11872,514],{"class":33},[27,11874,11875,11877,11879,11881],{"class":29,"line":2511},[27,11876,11424],{"class":37},[27,11878,527],{"class":33},[27,11880,1897],{"class":33},[27,11882,538],{"class":33},[27,11884,11885,11888,11890],{"class":29,"line":2520},[27,11886,11887],{"class":37},"            errTitle",[27,11889,527],{"class":33},[27,11891,11429],{"class":33},[27,11893,11894],{"class":29,"line":2525},[27,11895,3388],{"class":33},[27,11897,11898],{"class":29,"line":4},[27,11899,2564],{"class":33},[27,11901,11902,11904],{"class":29,"line":2548},[27,11903,8744],{"class":37},[27,11905,5676],{"class":33},[27,11907,11908,11911,11913,11916],{"class":29,"line":2561},[27,11909,11910],{"class":37},"        checkForm",[27,11912,202],{"class":33},[27,11914,11915],{"class":1788},"$event",[27,11917,1796],{"class":33},[27,11919,11920,11922,11924,11926,11928,11930,11933,11935,11937,11939],{"class":29,"line":2567},[27,11921,8814],{"class":1238},[27,11923,202],{"class":37},[27,11925,8869],{"class":33},[27,11927,1091],{"class":158},[27,11929,216],{"class":33},[27,11931,11932],{"class":158},"length",[27,11934,1718],{"class":33},[27,11936,8316],{"class":2673},[27,11938,213],{"class":37},[27,11940,514],{"class":33},[27,11942,11943,11946,11948],{"class":29,"line":2573},[27,11944,11945],{"class":1238},"                return",[27,11947,8895],{"class":2458},[27,11949,1255],{"class":33},[27,11951,11952,11955,11958],{"class":29,"line":2583},[27,11953,11954],{"class":33},"            }",[27,11956,11957],{"class":1238},"else",[27,11959,514],{"class":33},[27,11961,11962,11965,11967,11970,11972],{"class":29,"line":2596},[27,11963,11964],{"class":158},"                $event",[27,11966,216],{"class":33},[27,11968,11969],{"class":198},"preventDefault",[27,11971,1823],{"class":37},[27,11973,1255],{"class":33},[27,11975,11976,11979,11981,11983,11985],{"class":29,"line":2611},[27,11977,11978],{"class":158},"                ConcreteAlert",[27,11980,216],{"class":33},[27,11982,6018],{"class":198},[27,11984,202],{"class":37},[27,11986,514],{"class":33},[27,11988,11989,11992,11994,11996,11999,12001],{"class":29,"line":2619},[27,11990,11991],{"class":37},"                    title",[27,11993,527],{"class":33},[27,11995,205],{"class":33},[27,11997,11998],{"class":51},"入力項目に誤りがあります。",[27,12000,205],{"class":33},[27,12002,538],{"class":33},[27,12004,12005,12008,12010,12012,12015,12017],{"class":29,"line":2625},[27,12006,12007],{"class":37},"                    message",[27,12009,527],{"class":33},[27,12011,205],{"class":33},[27,12013,12014],{"class":51},"アルバムタイトルが入力されていません。",[27,12016,205],{"class":33},[27,12018,538],{"class":33},[27,12020,12021,12024,12026],{"class":29,"line":6504},[27,12022,12023],{"class":37},"                    delay",[27,12025,527],{"class":33},[27,12027,12028],{"class":2673},"5000\n",[27,12030,12031,12033],{"class":29,"line":6509},[27,12032,9173],{"class":33},[27,12034,253],{"class":37},[27,12036,12037,12039,12042,12044,12046,12049],{"class":29,"line":8720},[27,12038,8888],{"class":33},[27,12040,12041],{"class":158},"errTitle",[27,12043,1325],{"class":33},[27,12045,530],{"class":33},[27,12047,12048],{"class":51},"タイトルを入力してください。",[27,12050,656],{"class":33},[27,12052,12053],{"class":29,"line":8731},[27,12054,7272],{"class":33},[27,12056,12057],{"class":29,"line":8736},[27,12058,3388],{"class":33},[27,12060,12061],{"class":29,"line":8741},[27,12062,1436],{"class":33},[27,12064,12065],{"class":29,"line":8749},[27,12066,1910],{"class":33},[27,12068,12069,12071,12073],{"class":29,"line":8762},[27,12070,162],{"class":33},[27,12072,190],{"class":37},[27,12074,69],{"class":33},[13,12076,12077,12079,12080,12083,12084,12086,12087,12089],{},[24,12078,11101],{},"の箇所に",[24,12081,12082],{},"@submit=\"checkForm\"","というイベントを作成。これはこのformがsubmitされた際に",[24,12085,11536],{},"というメソッドを発火させるという意味です。そして",[24,12088,11536],{},"ではtitleの値が空かどうかを判断しています。",[13,12091,12092,12093,12096,12097,12099,12100,12103,12104,12107,12108,12110],{},"もし空でない場合は",[24,12094,12095],{},"return true","となって",[24,12098,11300],{},"が通って、サーバーへ値が送信されます。からの場合は",[24,12101,12102],{},"$event.preventDefault();","が実行されてsubmitされません。そして",[24,12105,12106],{},"ConcreteAlert.error","という8.4系から使用できるconcrete5のフラッシュメッセージのjsを出しています。（",[24,12109,12106],{},"は特に何も読み込まないでも使える）",[13,12112,12113],{},"空で「登録」を押すと以下の様になります。",[1571,12115],{":src":12116,":width":4383},"'_mix\u002Fsch-2020-08-27-0.07.18-768x531.png'",[13,12118,12119,12121],{},[24,12120,12102],{},"によってサーバーにデータは送信されず、ユーザーに対してエラーを表示できました。",[6520,12123,12125],{"id":12124},"eslintがある時のchips","ESLintがある時のchips",[13,12127,12128,12131],{},[24,12129,12130],{},"ConcreteAlert"," はconcrete5が用意してくれた便利なフラッシュメッセージです。しかし、ESLint付きのvueCLI内で使用ようとすると、ビルド時にこの様に怒られます。",[17,12133,12136],{"className":12134,"code":12135,"language":150},[989],"ERROR  Failed to compile with 1 errors                                                                                                       23:50:16\n\n error  in .\u002Fsrc\u002Fcomponents\u002Fform.vue\n\nModule Error (from .\u002Fnode_modules\u002Feslint-loader\u002Findex.js):\n\n\u002FApplications\u002FMAMP\u002Fhtdocs\u002Fc5test\u002Fpackages\u002Fvuetest\u002Fjs\u002Fpackageui\u002Fsrc\u002Fcomponents\u002Fform.vue\n  40:17  error  'ConcreteAlert' is not defined  no-undef\n\n✖ 1 problem (1 error, 0 warnings)\n",[24,12137,12135],{"__ignoreMap":22},[13,12139,12140,12141,12143,12144,12146],{},"そうです。vueプロジェクト内には",[24,12142,12130],{}," を定義したjsファイルがない、というかconcreteが用意したjsを読み込めないのでこの様に怒られます。これだとビルドできないので",[24,12145,465],{},"のeslintの設定に以下の記述をします。",[17,12148,12151],{"className":12149,"code":12150,"language":150},[989]," \"eslintConfig\": {\n　　\"globals\":{\n      \"ConcreteAlert\": true,\n    }\n },\n",[24,12152,12150],{"__ignoreMap":22},[13,12154,12155,12156,12158],{},"こうするとESLintは「ConcreteAlertってのはグローバルな奴なんだな〜。」と認識してくれて、実際にvueプロジェクト外にある",[24,12157,12130],{},"に対して怒らなくなります。",[279,12160,12161],{"id":12161},"バックエンド実装",[13,12163,12164],{},"vueを用いてまずはアルバムのタイトルだけを入力できるフォームを作りました。そしてこのタイトルをDBに挿入するまで行います。と言ってもシングルページコントローラーを以下の様に記述します。",[17,12166,12168],{"className":6734,"code":12167,"filename":6736,"language":6737,"meta":22,"style":22},"\u003C?php\nnamespace Concrete\\Package\\Vuetest\\Controller\\SinglePage\\Dashboard;\ndefined('C5_EXECUTE') or die('Access Denied.');\nuse \\Concrete\\Core\\Page\\Controller\\DashboardPageController;\nuse Concrete\\Core\\Routing\\Redirect;\nuse Concrete\\Core\\Http\\Request;\n\n\nuse Core;\nuse Database;\n\nclass Vuetest extends DashboardPageController\n{\n    public $packageHandle = 'vuetest';\n\n    public function on_start()\n    {\n        $this->requireAsset('package-vue-production');\n    }\n\n    public function view() {\n    }\n\n    public function add(){\n        if(Request::isPost() == true){\n            $title = $this->post('title');\n\n            if(empty($title)==false){\n                $db = Database::connection();\n                $db->executeQuery(\"START TRANSACTION\");\n                $db->executeQuery(\n                    'INSERT album SET `title`=?, `created`=now(), `modified`=now()',\n                    array($title)\n                );\n                $db->executeQuery(\"COMMIT\");\n                Redirect::to('\u002Fdashboard\u002Fvuetest')->send();\n            }else{\n                Redirect::to('\u002Fdashboard\u002Fvuetest')->send();\n            }\n\n        }else{\n            $this->render('\u002Fdashboard\u002Fvuetest\u002Fadd');\n        }\n    }\n}\n",[24,12169,12170,12174,12179,12183,12188,12193,12198,12202,12206,12211,12216,12220,12224,12228,12232,12236,12241,12245,12249,12253,12257,12261,12265,12269,12273,12278,12283,12287,12292,12297,12302,12307,12312,12317,12322,12327,12332,12337,12341,12345,12349,12353,12358,12362,12366],{"__ignoreMap":22},[27,12171,12172],{"class":29,"line":30},[27,12173,7318],{},[27,12175,12176],{"class":29,"line":72},[27,12177,12178],{},"namespace Concrete\\Package\\Vuetest\\Controller\\SinglePage\\Dashboard;\n",[27,12180,12181],{"class":29,"line":137},[27,12182,7323],{},[27,12184,12185],{"class":29,"line":169},[27,12186,12187],{},"use \\Concrete\\Core\\Page\\Controller\\DashboardPageController;\n",[27,12189,12190],{"class":29,"line":178},[27,12191,12192],{},"use Concrete\\Core\\Routing\\Redirect;\n",[27,12194,12195],{"class":29,"line":185},[27,12196,12197],{},"use Concrete\\Core\\Http\\Request;\n",[27,12199,12200],{"class":29,"line":195},[27,12201,182],{"emptyLinePlaceholder":181},[27,12203,12204],{"class":29,"line":240},[27,12205,182],{"emptyLinePlaceholder":181},[27,12207,12208],{"class":29,"line":247},[27,12209,12210],{},"use Core;\n",[27,12212,12213],{"class":29,"line":256},[27,12214,12215],{},"use Database;\n",[27,12217,12218],{"class":29,"line":1156},[27,12219,182],{"emptyLinePlaceholder":181},[27,12221,12222],{"class":29,"line":1161},[27,12223,10352],{},[27,12225,12226],{"class":29,"line":1171},[27,12227,514],{},[27,12229,12230],{"class":29,"line":1181},[27,12231,10361],{},[27,12233,12234],{"class":29,"line":1190},[27,12235,182],{"emptyLinePlaceholder":181},[27,12237,12238],{"class":29,"line":1216},[27,12239,12240],{},"    public function on_start()\n",[27,12242,12243],{"class":29,"line":2420},[27,12244,7774],{},[27,12246,12247],{"class":29,"line":2426},[27,12248,10380],{},[27,12250,12251],{"class":29,"line":2442},[27,12252,1436],{},[27,12254,12255],{"class":29,"line":2465},[27,12256,182],{"emptyLinePlaceholder":181},[27,12258,12259],{"class":29,"line":2471},[27,12260,10393],{},[27,12262,12263],{"class":29,"line":2482},[27,12264,1436],{},[27,12266,12267],{"class":29,"line":2488},[27,12268,182],{"emptyLinePlaceholder":181},[27,12270,12271],{"class":29,"line":2494},[27,12272,10411],{},[27,12274,12275],{"class":29,"line":2500},[27,12276,12277],{},"        if(Request::isPost() == true){\n",[27,12279,12280],{"class":29,"line":2506},[27,12281,12282],{},"            $title = $this->post('title');\n",[27,12284,12285],{"class":29,"line":2511},[27,12286,182],{"emptyLinePlaceholder":181},[27,12288,12289],{"class":29,"line":2520},[27,12290,12291],{},"            if(empty($title)==false){\n",[27,12293,12294],{"class":29,"line":2525},[27,12295,12296],{},"                $db = Database::connection();\n",[27,12298,12299],{"class":29,"line":4},[27,12300,12301],{},"                $db->executeQuery(\"START TRANSACTION\");\n",[27,12303,12304],{"class":29,"line":2548},[27,12305,12306],{},"                $db->executeQuery(\n",[27,12308,12309],{"class":29,"line":2561},[27,12310,12311],{},"                    'INSERT album SET `title`=?, `created`=now(), `modified`=now()',\n",[27,12313,12314],{"class":29,"line":2567},[27,12315,12316],{},"                    array($title)\n",[27,12318,12319],{"class":29,"line":2573},[27,12320,12321],{},"                );\n",[27,12323,12324],{"class":29,"line":2583},[27,12325,12326],{},"                $db->executeQuery(\"COMMIT\");\n",[27,12328,12329],{"class":29,"line":2596},[27,12330,12331],{},"                Redirect::to('\u002Fdashboard\u002Fvuetest')->send();\n",[27,12333,12334],{"class":29,"line":2611},[27,12335,12336],{},"            }else{\n",[27,12338,12339],{"class":29,"line":2619},[27,12340,12331],{},[27,12342,12343],{"class":29,"line":2625},[27,12344,7272],{},[27,12346,12347],{"class":29,"line":6504},[27,12348,182],{"emptyLinePlaceholder":181},[27,12350,12351],{"class":29,"line":6509},[27,12352,8002],{},[27,12354,12355],{"class":29,"line":8720},[27,12356,12357],{},"            $this->render('\u002Fdashboard\u002Fvuetest\u002Fadd');\n",[27,12359,12360],{"class":29,"line":8731},[27,12361,3388],{},[27,12363,12364],{"class":29,"line":8736},[27,12365,1436],{},[27,12367,12368],{"class":29,"line":8741},[27,12369,1910],{},[13,12371,12372,12374,12375,12378,12379,12382],{},[24,12373,8597],{}," でpostを送ると",[24,12376,12377],{},"add()","にて処理が行われます。",[24,12380,12381],{},"Request::isPost()","というメソッドを用いてリクエストがpostかどうかをチェックします。postであれば値をDBへ挿入するスクリプトを実行し、そうでなければ新規追加の画面を表示します。",[13,12384,12385,12388,12389,12392,12393,12396,12397,12400],{},[24,12386,12387],{},"DashboardPageController","配下では",[24,12390,12391],{},"$this->post('name')"," というメソッドで対応するname属性のinputの値を取得することができます！先ほどのフォームではタイトルの値を",[24,12394,12395],{},"name=\"title\"","としていたので",[24,12398,12399],{},"$title = $this->post('title');","で取得します",[397,12402,12404],{"id":12403},"chips-必ずバックエンドでもバリデーションを実装する","chips 必ずバックエンドでもバリデーションを実装する",[13,12406,12407],{},"よくみると下記の様にタイトルの値を検査しています。",[17,12409,12411],{"className":6734,"code":12410,"filename":6736,"language":6737,"meta":22,"style":22},"if(empty($title)==false){\n ...\n}\n",[24,12412,12413,12418,12423],{"__ignoreMap":22},[27,12414,12415],{"class":29,"line":30},[27,12416,12417],{},"if(empty($title)==false){\n",[27,12419,12420],{"class":29,"line":72},[27,12421,12422],{}," ...\n",[27,12424,12425],{"class":29,"line":137},[27,12426,1910],{},[427,12428,12430,12431],{"className":12429},[430,7196],"\n「フロントエンド でタイトルの値をバリデーションしたから別にやらなくても良くない？」というのは厳禁です。postで来た値は必ずバックエンドで同様にバリデーションをかけます。",[12432,12433,12434],"b",{},"なぜならブラウザのconsoleでフロントでの値は偽造することもでき、フロントエンドのバリデーションを不正にスルーできるからです。",[13,12436,12437],{},"vueで行っているバリデーションはあくまでユーザー補助、UX的な物でありセキュリティの観点からは言えばガバガバです。エンドユーザーが偽造ができないバックエンドであれば確実にバリデーションをすることができます。",[13,12439,12440],{},"dashbord配下は基本的にサイト管理者が触る物なので、不正な値を入れようとする人はいないと思いますが、フロントからpostされた値は基本的に信用しないスタイルを貫いた方が無難です。",[279,12442,12443],{"id":12443},"データを入れてみる",[13,12445,12446,12447,12449],{},"では早速使ってみましょう。",[24,12448,8597],{}," にアクセスするとタイトル入力フォームが出てきました。仮に「テスト」と入力。そして「登録」を押します。",[1571,12451],{":src":12452,":width":4383},"'_mix\u002Fsch-2020-08-27-0.48.59-768x203.png'",[13,12454,12455],{},"一覧のページにリダイレクトされました。ちゃんと挿入されたか、phpmyadminでみてみましょう。",[1571,12457],{":src":12458,":width":1574,":center":1575},"'_mix\u002Fsch-2020-08-27-0.51.36-768x340.png'",[13,12460,12461],{},"いましたね。titleが「テスト」となっているので、正しくデータが入力されました。",[279,12463,6635],{"id":6634},[13,12465,12466],{},"以上がvueとバックエンド部分の一通りの実装でした。",[830,12468,12469,12472,12475],{},[833,12470,12471],{},"シングルページにvueのエントリーポイントを作る",[833,12473,12474],{},"エントリポイントにレンダリングされる様にコンポーネントを設定",[833,12476,12477],{},"jsをシングルページに読み込む",[13,12479,12480],{},"¥以上を意識すればconcrete5のシングルページに自由にvueを用いてUIを構築できます。あとはvueの使い方とバックエンドの設計を頑張るだけです。そして次回は編集画面と一覧画面の作成をしていきます。",[324,12482,12483],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .s6cf3, html code.shiki .s6cf3{--shiki-default:#89DDFF;--shiki-default-font-style:italic}html pre.shiki code .sbqyR, html code.shiki .sbqyR{--shiki-default:#FF9CAC}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .s7ZW3, html code.shiki .s7ZW3{--shiki-default:#BABED8;--shiki-default-font-style:italic}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}",{"title":22,"searchDepth":137,"depth":137,"links":12485},[12486,12489,12490,12493,12494,12500,12503,12504],{"id":10072,"depth":72,"text":10072,"children":12487},[12488],{"id":10092,"depth":137,"text":10093},{"id":10102,"depth":72,"text":10103},{"id":10323,"depth":72,"text":10324,"children":12491},[12492],{"id":10327,"depth":137,"text":10328},{"id":3586,"depth":72,"text":3586},{"id":11016,"depth":72,"text":11016,"children":12495},[12496,12497],{"id":11025,"depth":137,"text":11026},{"id":11452,"depth":137,"text":11452,"children":12498},[12499],{"id":12124,"depth":169,"text":12125},{"id":12161,"depth":72,"text":12161,"children":12501},[12502],{"id":12403,"depth":137,"text":12404},{"id":12443,"depth":72,"text":12443},{"id":6634,"depth":72,"text":6635},[862],"2020-08-27","oncrete5にVueCLIを使ってUIを構築する。データの登録。",{},{"title":10055,"description":12507},"series\u002Fconcrete5vue-2",[10050,344,4057],"s28kXBmZtYl1vPtmgjcP0QiTjdZEL9Zwpvzi-377-0o",{"id":12514,"title":10062,"body":12515,"category":13171,"createdAt":13172,"description":10047,"extension":337,"index":30,"meta":13173,"navigation":181,"path":13174,"publish":181,"seo":13175,"series":10046,"seriesTitle":10047,"stem":13176,"tag":13177,"thumbnail":10051,"updatedAt":338,"__hash__":13178},"series\u002Fseries\u002Fconcrete5vue-1.md",{"type":10,"value":12516,"toc":13158},[12517,12520,12523,12527,12530,12592,12595,12598,12601,12604,12615,12618,12622,12625,12628,12631,12634,12637,12643,12646,12649,12655,12658,12664,12670,12690,12693,12696,12699,12702,12705,12718,12824,12830,12840,12847,13003,13018,13029,13035,13097,13103,13106,13109,13114,13140,13143,13149,13152,13155],[13,12518,12519],{},"こんにちはjunです。今回はconcrete5というCMSでVueを使ったパッケージのUIを作成していこうと思います。今回の記事ではコンポーネントを作成する前の、concrete5にvueプロジェクトを作成してCMSに結びつける環境構築まで行います。",[13,12521,12522],{},"またConcrete5とそのカスタマイズ方法についてある程度熟知している人向け、基準としてはカスタムパッケージを作成したことがある人向けの記事となります。対象とするConcrete5のバージョンは8.4以降となります。",[279,12524,12526],{"id":12525},"なぜvue","なぜVue？",[13,12528,12529],{},"なぜVueを使うのか。それはPHPとjqueryによるUI構築が非常に面倒になったからです。Concrete5にはフォーム（テキストエリアとかCMSからのファイル選択フォーム）をPHPで出力してくれる以下の様なヘルパーが存在します。",[17,12531,12533],{"className":6734,"code":12532,"language":6737,"meta":22,"style":22},"\u002F\u002F ヘルパーを読み込む\n$form = Core::make('helper\u002Fform');\n\n\u002F\u002Fテキストinput\necho $form->text($name, $default_value);\n\n\u002F\u002Fテキストエリア\necho $form-> textarea($name, $default_value);\n\n\u002F\u002F ファイルマネージャーヘルパー\n$file_selector = Core::make('helper\u002Fconcrete\u002Ffile_manager');\necho $file_selector->file('label', 'name_attr', 'Select Photo', $default_fileObj);\n",[24,12534,12535,12540,12545,12549,12554,12559,12563,12568,12573,12577,12582,12587],{"__ignoreMap":22},[27,12536,12537],{"class":29,"line":30},[27,12538,12539],{},"\u002F\u002F ヘルパーを読み込む\n",[27,12541,12542],{"class":29,"line":72},[27,12543,12544],{},"$form = Core::make('helper\u002Fform');\n",[27,12546,12547],{"class":29,"line":137},[27,12548,182],{"emptyLinePlaceholder":181},[27,12550,12551],{"class":29,"line":169},[27,12552,12553],{},"\u002F\u002Fテキストinput\n",[27,12555,12556],{"class":29,"line":178},[27,12557,12558],{},"echo $form->text($name, $default_value);\n",[27,12560,12561],{"class":29,"line":185},[27,12562,182],{"emptyLinePlaceholder":181},[27,12564,12565],{"class":29,"line":195},[27,12566,12567],{},"\u002F\u002Fテキストエリア\n",[27,12569,12570],{"class":29,"line":240},[27,12571,12572],{},"echo $form-> textarea($name, $default_value);\n",[27,12574,12575],{"class":29,"line":247},[27,12576,182],{"emptyLinePlaceholder":181},[27,12578,12579],{"class":29,"line":256},[27,12580,12581],{},"\u002F\u002F ファイルマネージャーヘルパー\n",[27,12583,12584],{"class":29,"line":1156},[27,12585,12586],{},"$file_selector = Core::make('helper\u002Fconcrete\u002Ffile_manager');\n",[27,12588,12589],{"class":29,"line":1161},[27,12590,12591],{},"echo $file_selector->file('label', 'name_attr', 'Select Photo', $default_fileObj);\n",[13,12593,12594],{},"上記のコードをページ上で実行すれば以下の様にフォームが現れます。",[1571,12596],{":src":12597,":width":4383},"'_mix\u002Fformhelpertest-768x272.png'",[13,12599,12600],{},"上から順番にテキストインプット、テキストエリア、そしてconcrete５純正のファイルマネージャーが出現します。name属性が与えられ、postを通じてデータを送ることができます。",[13,12602,12603],{},"簡単なブロックやパッケージUIであれば問題ないのですが、",[830,12605,12606,12609,12612],{},[833,12607,12608],{},"フロント側のバリデーションを実装する",[833,12610,12611],{},"決まったフォームを複数生成する",[833,12613,12614],{},"フォーム同士が入力値で依存させる場合（入力値でフォームのパターンが変わる）",[13,12616,12617],{},"上記の際にPHPとjqueryだけでは非常に苦労して工数もかかります。",[397,12619,12621],{"id":12620},"vueを使えばリッチなuiが作成可能","Vueを使えばリッチなUIが作成可能",[13,12623,12624],{},"今流行りのリッチなUI、フォームはほとんどがVue、React、Backbone,jsなどHTMLテンプレート、jsによる状態管理を行うことで簡単に実装できます。工数を抑えつつも、これらのライブラリを用いることでリッチなUIを作成することもできます。",[13,12626,12627],{},"今回の記事ではVue CLIを用いて作成したUIをconcrete5のパッケージ上で表示させ、入力内容の追加・更新・削除まで行える様に実装していきます。原理がわかれば意外と簡単です。",[279,12629,12630],{"id":12630},"パッケージの制作準備",[397,12632,12633],{"id":12633},"パッケージの専用のディレクトリを作成",[13,12635,12636],{},"それではVueで構築したUIを持つパッケージを作成していきましょう。pckageディレクトリ配下にvuetestというカスタムパッケージディレクトリを作ります。またディレクトリ構成は以下の通りにします。",[17,12638,12641],{"className":12639,"code":12640,"language":150},[989],"documentroot $ cd .\u002Fpackage\npackage $ mkdir vuetest && touch ...\npackage $ tree vuetest\n.\u002F\n└── vuetest\n    ├── controller.php\n    ├── controllers\n    │   └── single_page\n    │       └── dashboard\n    │           └── vuetest.php\n    ├── db.xml\n    ├── icon.png\n    ├── js\n    │\n    └── single_pages\n        └── dashboard\n            └── vuetest\n                └── view.php\n",[24,12642,12640],{"__ignoreMap":22},[397,12644,12645],{"id":12645},"vueプロジェクトを作成",[13,12647,12648],{},"パッケージに必要なコントローラーやUIを表示するダッシュボード上のシングルページとそのコントローラーも用意します。そしてjsディレクトリを作成してその中にvue cliを用いてプロジェクトを作成します。",[17,12650,12653],{"className":12651,"code":12652,"language":150},[989],"package $ cd vuetest\u002Fjs\njs $ vue create packageui\n\nVue CLI v4.4.6\n? Please pick a preset: default (babel, eslint) #default\n\nVue CLI v4.4.6\n✨  Creating project in documentroot\u002Fvuetest\u002Fjs\u002Fpackageui.\n🗃  Initializing git repository...\n⚙️  Installing CLI plugins. This might take a while... \n\n🎉  Successfully created project packageui.\n\njs $ cd packageui && tree -L 1\n.\n├── README.md\n├── babel.config.js\n├── node_modules\n├── package-lock.json\n├── package.json\n├── public\n",[24,12654,12652],{"__ignoreMap":22},[13,12656,12657],{},"無事にvueのプロジェクトが作成されました。concrete5との都合によりvue.config.jsファイルを作成してwebpackの設定をいじります。",[17,12659,12662],{"className":12660,"code":12661,"language":150},[989],"packageui $ touch vue.config.js\n",[24,12663,12661],{"__ignoreMap":22},[17,12665,12668],{"className":12666,"code":12667,"language":150},[989],"module.exports = {\n    configureWebpack: {\n      output: {\n        filename: '[name].js',\n        chunkFilename: '[name].js'\n      }\n    },\n  }\n",[24,12669,12667],{"__ignoreMap":22},[13,12671,12672,12673,231,12675,12678,12679,12682,12683,12686,12687,12689],{},"これは ",[24,12674,480],{},[24,12676,12677],{},"npm run build --mode development","で",[24,12680,12681],{},".vue","ファイルを",[24,12684,12685],{},".js","ファイルにコンパイルして",[24,12688,1408],{},"配下に配置される時に名前がいつも同じになる様に設定します。",[13,12691,12692],{},"buildをするとビルドされたファイルの名前にはハッシュされた英数字がつくのですが、これがビルドの度にころころ変わります。Concrete5でjsファイルをロードする場合は設定した名前が一致しないと読み込めませんので、ビルドの際に設定を変えなくて済む様に上記の設定をします。",[6520,12694,12695],{"id":12695},"もし複数のパッケージで共通のvueファイルを用いる場合",[13,12697,12698],{},"今回は vuetestというパッケージとそのディレクトリ配下に専用のvueプロジェクトを作成しますす。しかし複数のパッケージでvueファイルを使用したい場合、applicationディレクトリにjsディレクトリを作成し、vueプロジェクトを作成してください。",[397,12700,12701],{"id":12701},"コンパイルしたjsファイルをconcrete5と結びつける",[13,12703,12704],{},"vue cliでビルドするとdist配下にコンパイルされたjsファイルが生成されます。そのファイルとレンダー先のconcrete5上のページ（シングルページ）で読み込める様に、このjsファイルをCMSが読み込める様に設定します。",[13,12706,12707,12708,12710,12711,12713,12714,12717],{},"まずとりあえず以下の",[24,12709,390],{},"をビルドしてみます。レンダリング先に ",[24,12712,10885],{}," という要素があれば",[24,12715,12716],{},"Appコンポーネント","がレンダーされます。",[17,12719,12721],{"className":1228,"code":12720,"filename":1230,"language":1231,"meta":22,"style":22},"import Vue from 'vue'\nimport App from '.\u002FApp.vue'\n\nVue.config.productionTip = false\n\nnew Vue({\n  render: h => h(App),\n}).$mount('#app')\n",[24,12722,12723,12737,12753,12757,12773,12777,12787,12804],{"__ignoreMap":22},[27,12724,12725,12727,12729,12731,12733,12735],{"class":29,"line":30},[27,12726,1239],{"class":1238},[27,12728,7482],{"class":158},[27,12730,1245],{"class":1238},[27,12732,1248],{"class":33},[27,12734,4057],{"class":51},[27,12736,2479],{"class":33},[27,12738,12739,12741,12744,12746,12748,12751],{"class":29,"line":72},[27,12740,1239],{"class":1238},[27,12742,12743],{"class":158}," App ",[27,12745,1245],{"class":1238},[27,12747,1248],{"class":33},[27,12749,12750],{"class":51},".\u002FApp.vue",[27,12752,2479],{"class":33},[27,12754,12755],{"class":29,"line":137},[27,12756,182],{"emptyLinePlaceholder":181},[27,12758,12759,12761,12763,12765,12767,12769,12771],{"class":29,"line":169},[27,12760,7535],{"class":158},[27,12762,216],{"class":33},[27,12764,5786],{"class":158},[27,12766,216],{"class":33},[27,12768,7544],{"class":158},[27,12770,45],{"class":33},[27,12772,7549],{"class":2458},[27,12774,12775],{"class":29,"line":178},[27,12776,182],{"emptyLinePlaceholder":181},[27,12778,12779,12781,12783,12785],{"class":29,"line":185},[27,12780,10948],{"class":33},[27,12782,7611],{"class":198},[27,12784,202],{"class":158},[27,12786,514],{"class":33},[27,12788,12789,12791,12793,12795,12797,12799,12802],{"class":29,"line":195},[27,12790,10959],{"class":198},[27,12792,527],{"class":33},[27,12794,7625],{"class":1788},[27,12796,7628],{"class":41},[27,12798,7625],{"class":198},[27,12800,12801],{"class":158},"(App)",[27,12803,538],{"class":33},[27,12805,12806,12808,12810,12812,12814,12816,12818,12820,12822],{"class":29,"line":240},[27,12807,661],{"class":33},[27,12809,213],{"class":158},[27,12811,216],{"class":33},[27,12813,7649],{"class":198},[27,12815,202],{"class":158},[27,12817,205],{"class":33},[27,12819,1271],{"class":51},[27,12821,205],{"class":33},[27,12823,253],{"class":158},[17,12825,12828],{"className":12826,"code":12827,"language":150},[989],"packageui ＄ npm run build\n.\n├── README.md\n├── babel.config.js\n├── dist\n│   ├── app.js\n│   ├── app.js.map\n│   ├── chunk-vendors.js\n│   ├── chunk-vendors.js.map\n│   ├── css\n│   ├── favicon.ico\n│   ├── img\n│   └── index.html\n├\n",[24,12829,12827],{"__ignoreMap":22},[13,12831,12832,12833,1007,12836,12839],{},"ビルド成功。いらないものもありますが、",[24,12834,12835],{},"app.js",[24,12837,12838],{},"chunk-vendors.js","が読み込まれる様にすればOKです。",[13,12841,12842,12843,12846],{},"パッケージ専用のjsファイルで作成する場合はパッケージのインストールコントローラ（",[24,12844,12845],{},"package\u002Fvuetest\u002Fcontroller.php","）に以下の様な記述をします。",[17,12848,12851],{"className":6734,"code":12849,"filename":12850,"language":6737,"meta":22,"style":22},"\u003C?php\nnamespace Concrete\\Package\\Vuetest;\ndefined('C5_EXECUTE') or die('Access Denied.');\nuse \\Concrete\\Core\\Asset\\AssetList;\nuse \\Concrete\\Core\\Asset\\Asset;\n\nclass Controller extends \\Concrete\\Core\\Package\\Package {\n    protected $pkgHandle = 'vuetest';\n    protected $appVersionRequired = '5.7.4';\n    protected $pkgVersion = '1.0.0';\n\n    public function on_start()\n    {\n        $al = AssetList::getInstance();\n        $al->register(\n            'javascript', 'package-vue-build', 'js\u002Fpackageui\u002Fdist\u002Fapp.js',\n            array('version' => '1.0.0', 'position' => Asset::ASSET_POSITION_FOOTER, 'combine' => true),\n            $this->pkgHandle\n        );\n        \n        $al->register(\n            'javascript', 'package-vue-chunk', 'js\u002Fpackageui\u002Fdist\u002Fchunk-vendors.js',\n            array('version' => '1.0.0', 'position' => Asset::ASSET_POSITION_FOOTER, 'combine' => true),\n            $this->pkgHandle\n        );\n        \n        $al->registerGroup('package-vue-production', array(\n            array('javascript', 'package-vue-build'),\n            array('javascript', 'package-vue-chunk'),\n        )); \n    }\n...\n}\n","controller.php",[24,12852,12853,12857,12862,12866,12871,12876,12880,12885,12890,12895,12900,12904,12908,12912,12917,12922,12927,12932,12937,12942,12946,12950,12955,12959,12963,12967,12971,12976,12981,12986,12991,12995,12999],{"__ignoreMap":22},[27,12854,12855],{"class":29,"line":30},[27,12856,7318],{},[27,12858,12859],{"class":29,"line":72},[27,12860,12861],{},"namespace Concrete\\Package\\Vuetest;\n",[27,12863,12864],{"class":29,"line":137},[27,12865,7323],{},[27,12867,12868],{"class":29,"line":169},[27,12869,12870],{},"use \\Concrete\\Core\\Asset\\AssetList;\n",[27,12872,12873],{"class":29,"line":178},[27,12874,12875],{},"use \\Concrete\\Core\\Asset\\Asset;\n",[27,12877,12878],{"class":29,"line":185},[27,12879,182],{"emptyLinePlaceholder":181},[27,12881,12882],{"class":29,"line":195},[27,12883,12884],{},"class Controller extends \\Concrete\\Core\\Package\\Package {\n",[27,12886,12887],{"class":29,"line":240},[27,12888,12889],{},"    protected $pkgHandle = 'vuetest';\n",[27,12891,12892],{"class":29,"line":247},[27,12893,12894],{},"    protected $appVersionRequired = '5.7.4';\n",[27,12896,12897],{"class":29,"line":256},[27,12898,12899],{},"    protected $pkgVersion = '1.0.0';\n",[27,12901,12902],{"class":29,"line":1156},[27,12903,182],{"emptyLinePlaceholder":181},[27,12905,12906],{"class":29,"line":1161},[27,12907,12240],{},[27,12909,12910],{"class":29,"line":1171},[27,12911,7774],{},[27,12913,12914],{"class":29,"line":1181},[27,12915,12916],{},"        $al = AssetList::getInstance();\n",[27,12918,12919],{"class":29,"line":1190},[27,12920,12921],{},"        $al->register(\n",[27,12923,12924],{"class":29,"line":1216},[27,12925,12926],{},"            'javascript', 'package-vue-build', 'js\u002Fpackageui\u002Fdist\u002Fapp.js',\n",[27,12928,12929],{"class":29,"line":2420},[27,12930,12931],{},"            array('version' => '1.0.0', 'position' => Asset::ASSET_POSITION_FOOTER, 'combine' => true),\n",[27,12933,12934],{"class":29,"line":2426},[27,12935,12936],{},"            $this->pkgHandle\n",[27,12938,12939],{"class":29,"line":2442},[27,12940,12941],{},"        );\n",[27,12943,12944],{"class":29,"line":2465},[27,12945,10557],{},[27,12947,12948],{"class":29,"line":2471},[27,12949,12921],{},[27,12951,12952],{"class":29,"line":2482},[27,12953,12954],{},"            'javascript', 'package-vue-chunk', 'js\u002Fpackageui\u002Fdist\u002Fchunk-vendors.js',\n",[27,12956,12957],{"class":29,"line":2488},[27,12958,12931],{},[27,12960,12961],{"class":29,"line":2494},[27,12962,12936],{},[27,12964,12965],{"class":29,"line":2500},[27,12966,12941],{},[27,12968,12969],{"class":29,"line":2506},[27,12970,10557],{},[27,12972,12973],{"class":29,"line":2511},[27,12974,12975],{},"        $al->registerGroup('package-vue-production', array(\n",[27,12977,12978],{"class":29,"line":2520},[27,12979,12980],{},"            array('javascript', 'package-vue-build'),\n",[27,12982,12983],{"class":29,"line":2525},[27,12984,12985],{},"            array('javascript', 'package-vue-chunk'),\n",[27,12987,12988],{"class":29,"line":4},[27,12989,12990],{},"        )); \n",[27,12992,12993],{"class":29,"line":2548},[27,12994,1436],{},[27,12996,12997],{"class":29,"line":2561},[27,12998,3233],{},[27,13000,13001],{"class":29,"line":2567},[27,13002,1910],{},[13,13004,13005,13006,13009,13010,13013,13014,13017],{},"vuetestパッケージのコントローラーで",[24,13007,13008],{},"$al->register","を用いて、パスで指定したjsファイルを登録します。２つあるのでを",[24,13011,13012],{},"$al->registerGroup","用いてのp",[24,13015,13016],{},"ackage-vue-production","名前で２つのjsファイルを読み込む様にグルーピングします。",[13,13019,13020,13021,13024,13025,13028],{},"concrete5ではこの様なjs\u002Fcssのアセット登録システムがあり、登録をすれば適当にアセットが読み込むことができる様になります。",[290,13022,13023],{},"パッケージのコントローラーでは登録をした"," ので、次はjsファイルが必要な",[290,13026,13027],{},"ページのコントローラーで呼び出し"," を行います。",[13,13030,13031,13034],{},[24,13032,13033],{},"vuetest\u002Fsingle_pages\u002Fdashboard\u002Fvuetest\u002Fview.php"," に以下の様に記述します。",[17,13036,13038],{"className":6734,"code":13037,"language":6737,"meta":22,"style":22},"\u003C?php\nnamespace Concrete\\Package\\Vuetest\\Controller\\SinglePage\\Dashboard;\ndefined('C5_EXECUTE') or die('Access Denied.');\nuse \\Concrete\\Core\\Page\\Controller\\DashboardPageController;\n\nclass Vuetest extends DashboardPageController\n{\n    public $packageHandle = 'vuetest';\n\n    public function view() {\n        $this->requireAsset('package-vue-production');\n        $this->set('success', 'My success message');\n    }\n}\n",[24,13039,13040,13044,13048,13052,13056,13060,13064,13068,13072,13076,13080,13084,13089,13093],{"__ignoreMap":22},[27,13041,13042],{"class":29,"line":30},[27,13043,7318],{},[27,13045,13046],{"class":29,"line":72},[27,13047,12178],{},[27,13049,13050],{"class":29,"line":137},[27,13051,7323],{},[27,13053,13054],{"class":29,"line":169},[27,13055,12187],{},[27,13057,13058],{"class":29,"line":178},[27,13059,182],{"emptyLinePlaceholder":181},[27,13061,13062],{"class":29,"line":185},[27,13063,10352],{},[27,13065,13066],{"class":29,"line":195},[27,13067,514],{},[27,13069,13070],{"class":29,"line":240},[27,13071,10361],{},[27,13073,13074],{"class":29,"line":247},[27,13075,182],{"emptyLinePlaceholder":181},[27,13077,13078],{"class":29,"line":256},[27,13079,10393],{},[27,13081,13082],{"class":29,"line":1156},[27,13083,10380],{},[27,13085,13086],{"class":29,"line":1161},[27,13087,13088],{},"        $this->set('success', 'My success message');\n",[27,13090,13091],{"class":29,"line":1171},[27,13092,1436],{},[27,13094,13095],{"class":29,"line":1181},[27,13096,1910],{},[13,13098,10483,13099,13102],{},[24,13100,13101],{},"$this->requireAsset('package-vue-production');","で「先ほど登録したjsアセットをこのページで読み込め！」と命令しています。設定した後、実際に該当ページで探してみましょう。",[1571,13104],{":src":13105,":width":4383},"'_mix\u002Fsc-2020-08-01-19.52.09-768x133.png'",[13,13107,13108],{},"いました。\u002Fpackages\u002Fvuetest\u002Fjs\u002Fdist\u002F** と指定したパスにて読み込まれています。それではid=\"app\"を持つ適当なdivを作成して再度みてみましょう。",[13,13110,13111,13113],{},[24,13112,13033],{}," にて",[17,13115,13117],{"className":6734,"code":13116,"filename":9570,"language":6737,"meta":22,"style":22},"\u003C?php\ndefined('C5_EXECUTE') or die('Access Denied.');\n?>\n\n\u003Cdiv id=\"app\">\u003C\u002Fdiv>\n",[24,13118,13119,13123,13127,13131,13135],{"__ignoreMap":22},[27,13120,13121],{"class":29,"line":30},[27,13122,7318],{},[27,13124,13125],{"class":29,"line":72},[27,13126,7323],{},[27,13128,13129],{"class":29,"line":137},[27,13130,7328],{},[27,13132,13133],{"class":29,"line":169},[27,13134,182],{"emptyLinePlaceholder":181},[27,13136,13137],{"class":29,"line":178},[27,13138,13139],{},"\u003Cdiv id=\"app\">\u003C\u002Fdiv>\n",[1571,13141],{":src":13142,":width":1574,":center":1575},"'_mix\u002Fsh-2020-08-01-19.56.01-768x812.png'",[13,13144,13145,13146,13148],{},"パスの関係上レイアウトが崩れ、画像が読み込めていませんがvue側で記述した内容が無事にレンダリングされています。これでvueとconcrete5のセッティングは完了です。vueプロジェクトでコンポーネントを作成しながら、適切な",[24,13147,7565],{},"を用いてページ上にレンダーします。",[279,13150,13151],{"id":13151},"次はフォームコンポーネントの作成",[13,13153,13154],{},"以上がconcrete5のパッケージ上にvueプロジェクトを作成して、concrete5に結びつける方法でした。これでvueを使え、レンダーされる環境は整ったので次は情報を登録するための登録フォームと編集画面の作成を行っていきます。",[324,13156,13157],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s6cf3, html code.shiki .s6cf3{--shiki-default:#89DDFF;--shiki-default-font-style:italic}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .sbqyR, html code.shiki .sbqyR{--shiki-default:#FF9CAC}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .s7ZW3, html code.shiki .s7ZW3{--shiki-default:#BABED8;--shiki-default-font-style:italic}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}",{"title":22,"searchDepth":137,"depth":137,"links":13159},[13160,13163,13170],{"id":12525,"depth":72,"text":12526,"children":13161},[13162],{"id":12620,"depth":137,"text":12621},{"id":12630,"depth":72,"text":12630,"children":13164},[13165,13166,13169],{"id":12633,"depth":137,"text":12633},{"id":12645,"depth":137,"text":12645,"children":13167},[13168],{"id":12695,"depth":169,"text":12695},{"id":12701,"depth":137,"text":12701},{"id":13151,"depth":72,"text":13151},[862],"2020-08-25",{},"\u002Fseries\u002Fconcrete5vue-1",{"title":10062,"description":10047},"series\u002Fconcrete5vue-1",[10050,344,4057],"SFdt3_VrU2GVgYjfNjC32zyOKCtr79IKO3992JqFQJo",{"id":13180,"title":13181,"body":13182,"category":13508,"createdAt":13509,"description":13181,"extension":337,"index":338,"meta":13510,"navigation":181,"path":13511,"publish":181,"seo":13512,"series":338,"seriesTitle":338,"stem":13513,"tag":13514,"thumbnail":338,"updatedAt":338,"__hash__":13515},"articles\u002Farticles\u002Fjquery-anker.md","jqueryを用いてページの特定箇所までアニメーションスクロールさせる方法",{"type":10,"value":13183,"toc":13502},[13184,13187,13190,13193,13459,13470,13473,13476,13480,13483,13486,13489,13493,13496,13499],[13,13185,13186],{},"こんにちはjunです。どちらかというとメモ的な内容になります。このサイトの「目次」にも実装されているような、見出し名をクリックするとそのコンテンツが書いてある場所まで飛ぶ動きを実装させます。",[279,13188,13189],{"id":13189},"自動スクロールの原理",[13,13191,13192],{},"この自動スクロールの原理について解説します。",[17,13194,13196],{"className":1228,"code":13195,"language":1231,"meta":22,"style":22},"$('a[href^=#]').on('click',function() {\n    \u002F\u002F スクロールの速度\n    var speed = 400; \u002F\u002F ミリ秒\n\n    \u002F\u002F アンカーの値取得（aタグのhref属性）\n    var href= $(this).attr(\"href\");\n\n    \u002F\u002F 移動先の要素を取得\n    var target = $(href == \"#\" || href == \"\" ? 'html' : href);\n\n    \u002F\u002F 移動先の高さを取得\n    var position = target.offset().top;\n\n    \u002F\u002F スムーススクロール\n    $('body,html').animate({scrollTop:position}, speed, 'swing');\n});\n",[24,13197,13198,13233,13238,13255,13259,13264,13299,13303,13308,13357,13361,13366,13391,13395,13400,13451],{"__ignoreMap":22},[27,13199,13200,13202,13204,13206,13209,13211,13213,13215,13217,13219,13221,13223,13225,13227,13229,13231],{"class":29,"line":30},[27,13201,1264],{"class":198},[27,13203,202],{"class":158},[27,13205,205],{"class":33},[27,13207,13208],{"class":51},"a[href^=#]",[27,13210,205],{"class":33},[27,13212,213],{"class":158},[27,13214,216],{"class":33},[27,13216,219],{"class":198},[27,13218,202],{"class":158},[27,13220,205],{"class":33},[27,13222,226],{"class":51},[27,13224,205],{"class":33},[27,13226,231],{"class":33},[27,13228,234],{"class":41},[27,13230,1823],{"class":33},[27,13232,1328],{"class":33},[27,13234,13235],{"class":29,"line":72},[27,13236,13237],{"class":243},"    \u002F\u002F スクロールの速度\n",[27,13239,13240,13242,13245,13247,13250,13252],{"class":29,"line":137},[27,13241,7044],{"class":41},[27,13243,13244],{"class":158}," speed",[27,13246,1325],{"class":33},[27,13248,13249],{"class":2673}," 400",[27,13251,1869],{"class":33},[27,13253,13254],{"class":243}," \u002F\u002F ミリ秒\n",[27,13256,13257],{"class":29,"line":169},[27,13258,182],{"emptyLinePlaceholder":181},[27,13260,13261],{"class":29,"line":178},[27,13262,13263],{"class":243},"    \u002F\u002F アンカーの値取得（aタグのhref属性）\n",[27,13265,13266,13268,13270,13272,13274,13276,13279,13281,13283,13286,13288,13290,13293,13295,13297],{"class":29,"line":185},[27,13267,7044],{"class":41},[27,13269,2866],{"class":158},[27,13271,45],{"class":33},[27,13273,1809],{"class":198},[27,13275,202],{"class":37},[27,13277,13278],{"class":33},"this",[27,13280,213],{"class":37},[27,13282,216],{"class":33},[27,13284,13285],{"class":198},"attr",[27,13287,202],{"class":37},[27,13289,48],{"class":33},[27,13291,13292],{"class":51},"href",[27,13294,48],{"class":33},[27,13296,213],{"class":37},[27,13298,1255],{"class":33},[27,13300,13301],{"class":29,"line":195},[27,13302,182],{"emptyLinePlaceholder":181},[27,13304,13305],{"class":29,"line":240},[27,13306,13307],{"class":243},"    \u002F\u002F 移動先の要素を取得\n",[27,13309,13310,13312,13315,13317,13319,13321,13323,13325,13327,13329,13331,13334,13336,13338,13340,13343,13345,13347,13349,13351,13353,13355],{"class":29,"line":247},[27,13311,7044],{"class":41},[27,13313,13314],{"class":158}," target",[27,13316,1325],{"class":33},[27,13318,1809],{"class":198},[27,13320,202],{"class":37},[27,13322,13292],{"class":158},[27,13324,9034],{"class":33},[27,13326,530],{"class":33},[27,13328,7656],{"class":51},[27,13330,48],{"class":33},[27,13332,13333],{"class":33}," ||",[27,13335,2866],{"class":158},[27,13337,9034],{"class":33},[27,13339,7164],{"class":33},[27,13341,13342],{"class":33}," ?",[27,13344,1248],{"class":33},[27,13346,21],{"class":51},[27,13348,205],{"class":33},[27,13350,9934],{"class":33},[27,13352,2866],{"class":158},[27,13354,213],{"class":37},[27,13356,1255],{"class":33},[27,13358,13359],{"class":29,"line":256},[27,13360,182],{"emptyLinePlaceholder":181},[27,13362,13363],{"class":29,"line":1156},[27,13364,13365],{"class":243},"    \u002F\u002F 移動先の高さを取得\n",[27,13367,13368,13370,13373,13375,13377,13379,13382,13384,13386,13389],{"class":29,"line":1161},[27,13369,7044],{"class":41},[27,13371,13372],{"class":158}," position",[27,13374,1325],{"class":33},[27,13376,13314],{"class":158},[27,13378,216],{"class":33},[27,13380,13381],{"class":198},"offset",[27,13383,1823],{"class":37},[27,13385,216],{"class":33},[27,13387,13388],{"class":158},"top",[27,13390,1255],{"class":33},[27,13392,13393],{"class":29,"line":1171},[27,13394,182],{"emptyLinePlaceholder":181},[27,13396,13397],{"class":29,"line":1181},[27,13398,13399],{"class":243},"    \u002F\u002F スムーススクロール\n",[27,13401,13402,13404,13406,13408,13411,13413,13415,13417,13420,13422,13425,13428,13430,13433,13436,13438,13440,13442,13445,13447,13449],{"class":29,"line":1190},[27,13403,199],{"class":198},[27,13405,202],{"class":37},[27,13407,205],{"class":33},[27,13409,13410],{"class":51},"body,html",[27,13412,205],{"class":33},[27,13414,213],{"class":37},[27,13416,216],{"class":33},[27,13418,13419],{"class":198},"animate",[27,13421,202],{"class":37},[27,13423,13424],{"class":33},"{",[27,13426,13427],{"class":37},"scrollTop",[27,13429,527],{"class":33},[27,13431,13432],{"class":158},"position",[27,13434,13435],{"class":33},"},",[27,13437,13244],{"class":158},[27,13439,231],{"class":33},[27,13441,1248],{"class":33},[27,13443,13444],{"class":51},"swing",[27,13446,205],{"class":33},[27,13448,213],{"class":37},[27,13450,1255],{"class":33},[27,13452,13453,13455,13457],{"class":29,"line":1216},[27,13454,661],{"class":33},[27,13456,213],{"class":158},[27,13458,1255],{"class":33},[13,13460,13461,13462,13465,13466,13469],{},"aタグのhref属性の文字列",[24,13463,13464],{},"[href=”#hoge”]","と移動先の要素にID",[24,13467,13468],{},"\u003Cdiv id=”hoge”>\u003C\u002Fdiv>","とさせることでこのスクロールができます。",[13,13471,13472],{},"しかし、原理的に必要なものは",[13,13474,13475],{},"どの場所に飛ばすかの情報を持ったトリガー（今回のaタグ）\n自身の場所を示すページ内で一意のIDをもつクラスまたはID\nがあれば可能です。しかし後述する理由よりできたらページスイングはaタグで行い、IDで区別する方法が中規模以上のサイトでは便利です。",[279,13477,13479],{"id":13478},"url-そのページ内のdom-idを参照している","URL + # =そのページ内のDOM IDを参照している",[13,13481,13482],{},"今回のスクロールではトリガーとなるaタグをクリックすると、urlに「#hoge」というのが付きます。実はurlに#がつくと、",[13,13484,13485],{},"そのurlつまり、そのページ内におけるDOMと#以降の文字列に合致する要素に飛ばしてくれます。実はJSがなくても自動スクロースができます。ただしアニメーションもなく画面が一瞬で切り替わるので、味気っぽなさはあります。",[13,13487,13488],{},"js(jquery)ではアニメーションを実装したいので書いただけです。",[397,13490,13492],{"id":13491},"つきで行うメリット","#つきで行うメリット",[13,13494,13495],{},"#を用いて移動先の要素を取得してアニメーションを実装すると、「url+#」でアクセスしたときにもアニメーションスクロールが効くように改変が可能になります。",[13,13497,13498],{},"今回はその実装はありませんが、Webにそのわっている機能を下地にスクリプトを書いた方が、独自のスクリプトより拡張性が高くなります。",[324,13500,13501],{},"html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .sC9rS, html code.shiki .sC9rS{--shiki-default:#464B5D;--shiki-default-font-style:italic}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":22,"searchDepth":137,"depth":137,"links":13503},[13504,13505],{"id":13189,"depth":72,"text":13189},{"id":13478,"depth":72,"text":13479,"children":13506},[13507],{"id":13491,"depth":137,"text":13492},[334],"2020-05-01",{},"\u002Farticles\u002Fjquery-anker",{"title":13181,"description":13181},"articles\u002Fjquery-anker",[344,345],"m8IR5HZJkkmOljGoU2QAnguTcrNVhjIf3JLMRS3OI-s",1780987143714]