- 投稿日:2021-03-08T23:30:43+09:00
配列・オブジュエクトの変更をリアクティブに検出するには
数字や文字列などは簡単に検出できるが、配列・オブジェクトは一工夫必要
TL;DR
Vue.set()
を利用して変更させるやってみる
直接変更させた場合
<template> <div class="center"> <div class="row"> <div v-if="bool">ON</div> <div v-else>OFF</div> <button @click="bool = !bool">真偽値</button> </div> <div class="row"> <div v-if="array[0]">ON</div> <div v-else>OFF</div> <button @click="array[0] = !array[0]">配列</button> </div> </div> </template> <script> export default { layout: 'plain', data() { return { array: [false], bool: false } } } </script> <style> .center { width: 100px; margin: 0 auto; } .row { display: flex; justify-content: space-between; margin-top: 10px; } </style>単体のboolは値の変更に合わせて表示も切り替わるが、配列はそうもいかない(配列の値自体は変わっている)
配列の値をVue.set()で変更させる
中身を直接変更するのではなく、
Vue.set(Array, index, value)
を利用してみる。
ここの例では、エイリアスであるthis.$set()
にしています。詳しいドキュメントはこちら。
<template> <div class="center"> <div class="row"> <div v-if="bool">ON</div> <div v-else>OFF</div> <button @click="bool = !bool">真偽値</button> </div> <div class="row"> <div v-if="array[0]">ON</div> <div v-else>OFF</div> <!-- 配列の値の変更の仕方を変える --> <button @click="changeValue()">配列</button> </div> </div> </template> <script> export default { layout: 'plain', data() { return { array: [false], bool: false } }, methods: { changeValue() { this.$set(this.array, 0, !this.array[0]) } } } </script> <style> /* 省略 */ </style>いいかんじ!
まとめ
Vueでは配列の中身や長さ、オブジェクトのプロパティの追加や削除の検出ができないため、それを可能にするためにVue側でメソッドが用意されていたんですね。
意外とこれに気づかずに実装を進めて、後々になって「何でここだけ動いてないん...?」となったのは自分の他にもいるんじゃないでしょうか...
備考
- 投稿日:2021-03-08T23:17:34+09:00
[Electron, Vue.js] IpcRenderer.on()でthis.countに代入しようとしたら失敗した
動機
ElectronのipcRenderer.on()内でthis.countに受け取った値を代入したかったのにうまく行かなかったので
解決はしたが原因がわかりません…目次
うまくいかない例
mounted() { ipcRenderer.on("setCount", (_, arg) => { this.count = arg; }); },うまくいく例
なかなかグロテスクなコードになりました…
thisをコールバックの外に出してみたら何故かうまく行ったasync mounted() { this.count = await ((): Promise<string> => new Promise((resolve) => { ipcRenderer.on("setCount", (_, arg) => { resolve(arg); }); }))(); },関数を分けるなら
methods: { async waitIpcMessage(): Promise<string> { return new Promise((resolve) => { ipcRenderer.on("setCount", (_, arg) => { resolve(arg); }); }); } }, async mounted() { this.count = await this.waitIpcMessage(); }最後に
なぜこれならうまくいくのか知っている方は教えてください...
thisが外に出てるからうまくいくっぽい?よくわかりません話は逸れますが非同期周りの書き方ってかなり複雑ですよね
- 投稿日:2021-03-08T23:15:03+09:00
Vue.js始めました。
*学習初日の個人的なアウトプット用です。
Vue.jsとは、UIを構築するためのプログレッシブフレームワークであり、モダンなツールやサポートライブラリと組み合わせることで、洗練されたSPAを開発することもできる。
大きな特徴の一つとして、双方向データバインディングができるという点が挙げられる。
データバインディングとは data と UI を結び付けるという意味で、双方向というのは data を更新すれば UI が更新されて、 UI を更新すれば data が更新されるという意味である。入門のためここでは名前と簡単なリストを表示させる。
index.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>Vue practice</title> <link rel="stylesheet" href="css/styles.css"> </head> <body> <div id="app"> <!--二重波括弧 {{}} で表現し UI を反映させる。そのままJavaScriptの式を書くこともできる。 --> <p>Hello {{ name.toUpperCase() }}!</p> <!-- v-model を使うことで input タグに入力された文字を上の表示にリアルタイムで反映できる。--> <p><input type="text" v-model="name"></p> <h1>My Lists</h1> <ul> <li>{{ lists[0] }}</li> <li>{{ lists[1] }}</li> <li>{{ lists[2] }}</li> <!-- 上のリストは v-for を使って配列の中身を取り出すことが可能 --> <li v-for="list in lists">{{ list }}</li> </ul> </div> <!-- Vue.jsを読み込む --> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script src="js/main.js"></script> </body> </html>
- v-for や v-model など v- から始まる特殊な属性をディレクティブと呼ぶ。
js/main.js(function() { 'use strict'; // vm は view model の略 var vm = new Vue({ el: '#app', // el は elements の略 data: { name: 'tanaka', lists: [ 'list 1', 'list 2', 'list 3' ] } }); })();ブラウザで確認すると結果以下のような表示になる。
以上です。
- 投稿日:2021-03-08T20:23:42+09:00
Vue.js始めてみました①
この度Vue.jsを学び始めました。
その勉強内容をまとめます。Vue.jsとは
Evan Youさんという方が手軽に小規模なアプリケーションを作れることを目的に作られたフレームワーク。
SPA(シングルページアプリケーション)なので1つのページの変化があった部分だけ表示を変える。
その為、読み込みが少なくスムーズで切り替えが素早い。個人的にはEvanさんは日本のアニメが好きなのでVue.jsの各バージョンのコードネームが日本のアニメからきていることに親近感を覚えました。
Dragon Ball
とかHunter×Hunter
とか。可愛いな。インストール
上記に記載されている通り
- CDN
- ダウンロード
- Vue-cli
の3つの方法があります。
一番手軽なのがCDNだと思います。
1行コードをheadに組み込むだけで使えるようになります。(開発バージョンと本番バージョンがあリます)
本日時点では以下ですが、公式サイトから必ず確認してください。開発バージョン<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>開発バージョンと本番バージョンの違いは、開発バージョンだと警告文を出力してくれるなどあるみたいです。
使ってみる
以下のようにheadにCDNを組み込みます。
<head> <meta charset="UTF-8"> <title>Sample</title> <link rel="stylesheet" href="style.css" > // 以下を追加 <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script> </head>以下のようにHTML要素を作ります。(説明だけなのでテキトーです)
<div id="app"> <p>{{ text }}</p> </div>以下のようにVueインスタンスを作成します。
script要素で作成します。<script> new Vue({ el: '#app', data: { text:'Hello!World' } }) </script>上記のように記述すると
data
の内容がHTML要素のtext
に反映され、Hello!World
とブラウザ上に表示されます。Vueインスタンスの中身
new Vue({ el:どのHTML要素とつなげるか data:どのデータを入れるか methods:処理内容 computed:どのデータを使って計算するか watch:データの監視 })上記のように作成します。
一旦ここまでです。
- 投稿日:2021-03-08T17:28:51+09:00
axiosでREST APIの通信を行う時にSafariではエラーが起きたときの対処法
皆さんこんにちは!
Windowsで開発している僕にとってはSafari特有のエラー程苦しむものは無いです。
ただ、この時代スマートフォンの使用率が圧倒的に高い10代、20代のほとんどはiPhoneを使っていると思います。
僕の周りでもiPhoneの使用率が圧倒的に高いです。
なので、iPhoneのエラーも無視するわけにはいかない。。。
今日はそんなこんなでaxiosでLaravelで作成したバックエンドとの通信でWindows環境で開発しているので、エラー内容は確認できなかったのですが、おそらくCORSエラーが原因でAPI通信を行うことができませんでした。
結論から言いますと、SafariではChromeのように上手くキャッシュの処理を行うことができないそうです。
なので、このキャッシュの問題を解決してあげることでSafariでもAPI通信を行うことができます!
それでは、説明を見ていきましょう!
はじめに
今回は、Vueでの説明となります。
もし、Reactなどを使っている場合は適時変更して下さい。
また、バックエンドではLaravelでCORSドメインの設定などを行います。
もし、Laravel以外でAPIを作成している場合も適時変更してください。
axiosでCookie設定
axiosのデフォルトでは、
withCredentials
というCookieを使えるようにするためのプロパティの設定がOFFになっています。なので、これをONにしてあげましょう!
main.js// axiosのプロトタイプ宣言 Vue.prototype.$axios = axios // Cookieの使用を可とする axios.defaults.withCredentials = trueこれでaxiosがデフォルトでCookieを使用することができます。
CORSドメイン設定
次に、axiosでCookieの情報を受け取るためにバックエンドで適切なヘッダーを追加してあげましょう!
> php artisan make::middleware ApiCorsHttp/Middleware/ApiCors.php<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; class ApiCors { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle(Request $request, Closure $next) { // すべてのレスポンスに CORS 用のヘッダーを追加する必要はないので URL から判断する $paths = explode('/', $request->getPathInfo()); if ($paths[1] === 'api') { return $next($request) ->header('Access-Control-Allow-Origin', config('cors.allowed_origins')) ->header('Cache-Control', 'no-cache max-age=3600') ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS') ->header('Access-Control-Allow-Credentials', 'true') ->header('Access-Control-Allow-Headers', 'X-XSRF-TOKEN, Authorization, content-type, Transfer-Encoding, Accept, Accept-Encoding, Accept-Language'); } return $next($request); } }
$paths = explode('/', $request->getPathInfo());
とif ($paths[1] === 'api')
で/api/apis
``のようなAPI通信を行う時だけこのミドルウェアを適用するようにしています。Access-Control-Allow-Origin
Access-Control-Allow-Origin
ではCORSドメインを許可するドメインを登録しています。例えば、
http://localhost:3000
からのリクエストを許可したい場合はここにhttp://localhost:3000
と入力してください。これを
*
のようにすると全てのドメインからリクエストが可能なので、データ抜き放題となるので注意して下さい。Access-Control-Allow-Methods
Access-Control-Allow-Methods
では、使用できるメソッドを書きます。ほとんどの場合は上記のようにしてOKです。Access-Control-Allow-Credentials
Access-Control-Allow-Credentials
をtrue
にすることで、先ほどaxiosで設定したCookie情報のリクエストを受け取れるようにしています。Access-Control-Allow-Headers
最後に、
Access-Control-Allow-Headers
を説明します。これが重要です!
色々あるのですが2つだけ説明します。
X-XSRF-TOKEN
はaxiosで発行したトークン情報です。PHPで言うと、form
のCSRF
のようなものです。これを受け取るためにこのヘッダーを追加します。次に、
content-type
です。これは、GETやHEADの場合は必要ないのですが、以下のようなリクエストを行う場合
content-type
はapplicatio/json
となります。this.$axios .post(process.env.VUE_APP_LARAVEL_SITE_URL + '/api/participants', { name: 'akki' })そして、
content-type
のデフォルトではapplication/json
は対応していません。なので、このような
application/json
の形を受け取るのを許可するためにcontent-type
を追加します。ちなみに場合によっては、
content-type
でエラーが出ます。その時はContent-Type
と設定して見て下さい。ミドルウェアの適用
最後に作成したこのミドルウェアを適用させましょう!
App/Http/Middleware/Karnel.php
を開いてください。Karnel.php<?php namespace App\Http; use Illuminate\Foundation\Http\Kernel as HttpKernel; class Kernel extends HttpKernel { /** * The application's global HTTP middleware stack. * * These middleware are run during every request to your application. * * @var array */ protected $middleware = [ // \App\Http\Middleware\TrustHosts::class, // CORSドメインの設定を追加 \App\Http\Middleware\ApiCors::class, \App\Http\Middleware\TrustProxies::class, \Fruitcake\Cors\HandleCors::class, \App\Http\Middleware\PreventRequestsDuringMaintenance::class, \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, \App\Http\Middleware\TrimStrings::class, \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, ]; // 省略 }必ず$middlewareに追加してください!!
なぜかと言うと、$middlewareに追加しないと
OPTIONS
リクエストに今作成したミドルウェアを適用することができません!絶対に$middlewareに追加!!!
いかがだったでしょうか?
このエラーで3日程時間を費やされたので、本当に苦労しました。
ぜひ皆さんのお力になれたらなと思います。
以上、「axiosでREST APIの通信を行う時にSafariではエラーが起きたときの対処法」でした!
また、何か間違っていることがあればご指摘頂けると幸いです。
他にも初心者さん向けに記事を投稿しているので、時間があれば他の記事も見て下さい!!
あと、最近「ココナラ」で環境構築のお手伝いをするサービスを始めました。
気になる方はぜひ一度ご相談ください!
Thank you for reading
- 投稿日:2021-03-08T13:27:05+09:00
フロントエンドVue.js バックエンドFlaskの開発で躓いたこと色々
GoogleのOAuthをする場合
元々はFlaskのみで実装していたプロジェクトだったのだが、途中からフロントエンドをvue.jsに引き継いだため、OAuth部分をFlask側のコードで実行しようとしていた。
これだとCORSエラーが出てしまう。
CORSエラーへの対応について調べてみると、Flask,Vue.jsともにCORS対応方法についての文献がでてくる。
しかしこれをやっても超えられない。何故か。色々と探してみたのだが、そもそもそういう使い方は出来ないっぽい。探しすぎてソースを見失ってしまったが、つまりそういうことだったんだと思う。
素直にVue.js側でvue-google-oauth2を使った実装を行う事によってVue.js→Google→Vue.jsという通信によってtokenが取得できるのでそれを使う。認証されないGoogleAPIキー
これはそのままこれ
https://qiita.com/kenken1981/items/9d738687c5cfb453be19どうも、Google認証の「OAuth 2.0 クライアント ID」は、一度あるポートで使うと、その後で違うポートで利用することはできないらしい。
ということで、別途フロントエンド用のキーを作って、vue.js側のポートを指定することで解決。
認証されないGoogleAPIキー2
承認済みの JavaScript 生成元
については、127.0.0.1 は使えない。
https://qiita.com/kenken1981/items/b6cb3e536668a3cef520Google認証は、「127.0.0.1」というIPアドレスは認めておらず、「actual urls」というらしい?いわゆるaaa.jpやbbb.comといった形式でないとエラーを返す模様。
Vue.js側で環境変数が読み込まれない
Vue.js側は yarn build することでdistフォルダに静的ファイルを吐き出す形で利用しているのだが、どうもVue.js側で環境変数を読み込んでくれない。
.envに書いてあろうが読み込まれない。何故か。buildする時に、その設定が必要だった。
詳細についてはここにある通り。
https://qiita.com/go6887/items/2e254d31b5a4af42f813vue.js が
/frontend
内に入っていて、/frontoend でビルドコマンドを打つ形にしてありましたが
/frontend/.env.development
というファイルを作って、中に変数を記述。package.json には
"build": "cross-env NODE_ENV=development vue-cli-service build --mode development --dest ../dist",みたいな形に書いておく事によって環境変数が読み込まれた。
変更履歴
2021/3/8 初稿up
- 投稿日:2021-03-08T11:40:51+09:00
Vue.jsコンポーネントの記述順序(options order)の表
本家スタイルガイド - コンポーネント/インスタンス オプション順序の転記。スクロールしないで読めるように。
分類 オプション 1. 副作用 el 2. グローバルな認識 name, parent 3. コンポーネントの種類 functional 4. テンプレートの修飾子 delimiters, comments 5. テンプレートの依存関係 components, directives, filters 6. 合成 extends, mixins 7. インタフェース inheritAttrs, model, props/propsData 8. ローカルの状態 data, computed 9. イベント watch
beforeCreate, created, beforeMount, mounted, beforeUpdate, updated, activated, deactivated, beforeDestroy, destroyed10. リアクティブではないプロパティ methods 11. レンダリング template/render, renderError ※何となくよく探しそうなやつは太字で
- 投稿日:2021-03-08T04:54:03+09:00
Vue.jsで作図する 凸レンズの焦点問題
突然ですが、こんな図みたことありますよね?
これは理科で定番の凸レンズの焦点に関する問題です。
今回はこの焦点問題を理解しつつVue.jsで作図することを目指します。作ったもの ※PC推奨:
https://convex-lens-d0f3c.web.app/github ソースコード:
https://github.com/tenugui-taro/convex-lens_public準備
Vue2 × composition-api × Vuetify の構成で作ります。
$ vue create {project-name} $ npm install @vue/composition-api $ vue add vuetifycomposition-apiを有効にするにはmain.tsで設定が必要になります。
@vue/composition-apisrc/main.tsimport VueCompositionAPI from '@vue/composition-api' Vue.use(VueCompositionAPI)焦点問題とは
ざっくりいうと凸レンズに入射した光が集まる位置に実像ができる。
虫眼鏡でものが大きくみえる現象はこの原理を利用していたりする。焦点は凸レンズに平行に入射した光が集まる場所
ちなみに、凸レンズの中心から焦点までの距離の2倍に光源があると光源と実像は同じ大きさになる。解答のポイントは光の線を引きその交点を求めること。
- 光軸と平行な線
- 凸レンズの中心を通る線
- 焦点を通る線
線を引く
解答のポイントとなる、光源から出る線を引く方法を考えます。
vuetifyで作図
Vuetifyのドキュメントを眺めているとグラフ作成用UIコンポーネントがありました。
v-sparklineなんとなくおしゃれだしこれで。
まずはサンプルをcomposition-apiを使いつつコンポーネント化します。src/components/Sparkline.vue<template> <v-sparkline :value="value" :gradient="gradient" :smooth="radius || false" :padding="padding" :line-width="width" :stroke-linecap="lineCap" :gradient-direction="gradientDirection" :fill="fill" :type="type" :auto-line-width="autoLineWidth" auto-draw /> </template> <script lang="ts"> import { defineComponent, reactive, toRefs } from "@vue/composition-api"; const GRADIENTS: string[][] = [ ["#222"], ["#42b3f4"], ["red", "orange", "yellow"], ["purple", "violet"], ["#00c6ff", "#F0F", "#FF0"], ["#f72047", "#ffd200", "#1feaea"], ]; export default defineComponent({ setup() { const state = reactive({ width: 2, // 線の太さ radius: 10, // 線同士のつながり 小さいほど角張る padding: 8, // 描画エリアの余白 lineCap: "round", // 線端の形状 gradient: GRADIENTS[5], // 配色 value: [0, 2, 5, 9, 5, 10, 3, 5, 0, 0, 1, 8, 2, 9, 0], // 値 gradientDirection: "top", // グラデーションの方向 fill: false, // グラフの塗りつぶし type: "trend", // グラフの種類 trend or line autoLineWidth: false, // 自動で線を拡張してくれる? }); return { ...toRefs(state), }; }, }); </script>焦点問題の作図をイメージして、3本の線を引きます。
線を重ねるためにpositionを設定しています。src/App.vue<div style="position: relative"> <Sparkline style="position: absolute" :value="[100, 100, 0]" /> <Sparkline style="position: absolute" :value="[100, 0, -100]" /> <Sparkline style="position: absolute" :value="[100, -100, -100]" /> </div>いい感じ......
あれ、他要素との座標をどうやって合わせる......?ただ単純に用意した画像を重ねると以下の通り、これでは0点です。
ここからどうするか......
ごまかしでwidthやheightを調整するうちに心が痛くなったので別の手段を考えますsvgで表現する
図形を組み合わせながら問題を用意します。
src/App.vue<svg xmlns="http://www.w3.org/2000/svg" width="90vw" height="70vh" id="drawing-area" > <ellipse cx="50%" cy="50%" rx="3%" ry="40%" fill="white" stroke="black" /> <line x1="10%" y1="50%" x2="90%" y2="50%" stroke-width="1%" stroke="#455A64" /> <circle cx="35%" cy="50%" r="1%" fill="#E0E0E0" stroke="black" /> <circle cx="65%" cy="50%" r="1%" fill="#E0E0E0" stroke="black" /> <rect x="20%" y="30%" width="1%" height="20%" fill="#e74c3c" /> </svg>
いよいよ作図します。
線を描画する関数、実像を描画する関数を用意しひとまず手計算した値を渡します。src/utils/addLine.ts/** * 線を描画 * @param {number[]} params */ export const addLine = (params: number[]) => { const drawingArea = document.getElementById("drawing-area"); const line = document.createElementNS( "http://www.w3.org/2000/svg", "line" ); line.setAttribute("x1", `${params[0]}%`); line.setAttribute("y1", `${params[1]}%`); line.setAttribute("x2", `${params[2]}%`); line.setAttribute("y2", `${params[3]}%`); line.setAttribute("stroke", "black"); drawingArea!.appendChild(line); }src/utils/addRect.ts/** * 四角形を描画 * @param {number[]} params */ export const addRect = (params: number[]) => { const drawingArea = document.getElementById("drawing-area"); const rect = document.createElementNS( "http://www.w3.org/2000/svg", "rect" ); rect.setAttribute("x", `${params[0]}%`); rect.setAttribute("y", `${params[1]}%`); rect.setAttribute("width", `${params[2]}%`); rect.setAttribute("height", `${params[3]}%`); rect.setAttribute("fill", `#e74c3c`); drawingArea!.appendChild(rect); }src/App.vueonMounted(() => { // addLine([x1, y1, x2, y2]) // 凸レンズの中心を通る線 addLine([20, 30, 80, 70]); // 光軸に対して平行な線 addLine([20, 30, 50, 30]); addLine([50, 30, 80, 70]); // 焦点を通る線 addLine([20, 30, 50, 70]); addLine([50, 70, 80, 70]); // addRect([x, y, width, height]) // 実像 addRect([80, 50, 1, 20]); });自動化
もちろん手計算は面倒です。
交点を算出する関数を用意し、vueファイルとは別に切り出します。
計算ばかりなので興味ある方はソースコードを参照ください。また、好きに動かせるようv-sliderコンポーネントを入れて完成です。
200点
ところで焦点の内側に入ると実像が出来ていませんね。
このときは、虚像が出来るのですが今回は実装見送りです......ホスティング
最後にfirebaseにホスティングして終了です。
https://firebase.google.com/docs/hosting?authuser=0$ firebase init hosting $ firebase deploy --only hosting作ったもの ※PC推奨:
https://convex-lens-d0f3c.web.app/まとめ
凸レンズをふと思い出して作ってみました。
失敗もしましたがv-sparklineの使い方を学べたのでよしということで。また、前回記事よりはまとまった記事にできた気がします。
今後もしょうもないことに取り組んでVueと仲良くなっていきたいです。
- 投稿日:2021-03-08T01:20:49+09:00
imgのファイル指定をv-bindでする時
Vue.jsでimgタグのsrc指定を変数で行う場合、そのままだと使えないので注意
TL;DR
require()を使用する
やってみる
※この例ではNuxtも使用しています
そのまま変数を置いてみる
<template> <img :src="pass"> </template> <script> export default { data() { return { pass: '~/assets/test-img.png' } } } </script>なんとなくいけるような気はするが、結果は
404エラー
となってしまいます。
どうやら、相対パスを表す~
部分が、 そのまま文字列として 読み込まれてしまっている模様。(たぶん)モジュールとして読ませる
そこで、
require()
を利用する。<template> <img :src="pass"> </template> <script> export default { data() { return { pass: require('~/assets/test-img.png') } } } </script>これで晴れて表示させることができます!(表示例がないのでわかりにくいですが...)
(おまけ)static配下の画像の場合
指定したい画像がstatic配下だと、require使わずとも表示させることができます。
<template> <img :src="pass"> </template> <script> export default { data() { return { pass: 'test-img.png' } } } </script>template内で直接指定する場合、
~/static/test-img.png
はtest-img.png
と書くことで、自動でstatic配下に補完してくれるようです。
相対パスの記述がないので、変数で文字列として読ませても、うまく作動するようです。便利〜〜〜
- 投稿日:2021-03-08T00:13:28+09:00
Form SubmitでAPIコールすると失敗
フロントエンド(HTML/CSS/JavaScript)強化中で、いろいろ試している所です。
HTMLでFormを作成して、画面操作を契機としてJavaScript(Vue.js)の処理を連動させてAPIコールする、というコードを書きましたが、つまづきました。
いろいろと試してみた結果、
・<input type="submit" だと失敗
・<input type="button" だと成功
という結果となりました。開発内容
Vue.jsを利用して、画面操作を契機にAPIコールすることを試すだけが目的ですので、開発内容は非常に単純です。
要件:
・郵便番号をテキストボックスから入力し、ボタンクリックしたことを契機にAPIをコール。
・利用したAPI: https://zipaddress.net/
・APIのレスポンスから、都道府県名を取得してアラート表示。
・APIコールでExceptionが発生した場合: アラート「Fail to call API」
・APIのレスポンスに、異常値が設定されていた場合: アラート「Error was returned」最初に書いたコード
practice_submit.html<!DOCTYPE html><html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script> </head> <body> <div id="apiTest"> <h2>Form Click</h2> <form method="post"> <tr> <td>郵便番号</td> <td><input type="text" v-model="zipcode"/></td> </tr> <input type="submit" @click="getAddress" value="送信"/> </form> </div> <script type="text/javascript" src="./practicevue.js"></script> </body> </html>practice.jsnew Vue({ el: "#apiTest", data: { zipcode: "" }, methods: { getAddress: function() { var url = new URL('https://api.zipaddress.net/'); var param = {zipcode: this.zipcode}; url.search = new URLSearchParams(param); fetch(url) .then(response => { return response.json(); }) .then(function(jsonData){ if(jsonData.code == '200') { alert(jsonData.data.pref); } else { alert('Error was returned'); } }).catch(err => { alert('Fail to call API'); }); } } })画面
以下のように、郵便番号を入力するためのテキストボックスと送信ボタンがあるだけの単純な画面です。
発生した問題
上記のvuepractice.js で、fetch(url)を行った結果、Exceptionが発生してしまったようです。
以下のアラートメッセージが表示されたので、APIコールしたサーバからエラーが返されたのではなく、
「 }).catch(err => { 」
に該当して、HTTPリクエスト送信処理自体で例外が発生した、と考えられます。}).catch(err => { alert('Fail to call API'); });解決方法
いろいろ試してみましたが、HTMLのinput属性を、submitからbuttonに変更するだけで、解決しました。
変更前:<input type="submit" @click="getAddress" value="送信"/>
変更後:<input type="button" @click="getAddress" value="送信"/>
郵便番号に「1000000」を設定して送信ボタンをクリックした結果、想定通りに都道府県名を正常に取得できました。
修正後のHTML
上記の通り、1行変更しただけですが、修正後のHTMLは以下です。
practice_submit.html(修正後)<!DOCTYPE html><html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script> </head> <body> <div id="apiTest"> <h2>Form Click</h2> <form method="post"> <tr> <td>郵便番号</td> <td><input type="text" v-model="zipcode"/></td> </tr> <input type="button" @click="getAddress" value="送信"/> </form> </div> <script type="text/javascript" src="./practicevue.js"></script> </body> </html>考察
buttonと、submitの違いだけでなぜこうなる...?
いまいちわかりませんでしたが、これで良しとしました。しかし、ここで気付きました。
「submit」しないのでれば、formである必要がないじゃないか!!
- 投稿日:2021-03-08T00:05:14+09:00
ElectronのexecuteJavascriptでError: Script failed to executeが出る件
はじめに
初投稿です...
ElectronのBrowserViewでexecuteJavascriptを使った際、一見正しそうなスクリプトがエラーを吐く問題で少々悩んだので備忘録として。問題のソースコード
要素の取得に失敗したらfalse、成功したら真を返したい
const result = await browserView.webContents.executeJavaScript(` const anko = document.getElementById("anko"); return anko == null ? false : true; `)正しいソースコード
どうやらreturnで返しちゃだめらしいです。(そんなん知らんよ…)
最後に評価された値が返却されるらしい。const result = await browserView.webContents.executeJavaScript(` let val = true; const anko = document.getElementById("anko"); anko == null ? false : true; `)現在作っているアプリ
Youtube Live用のコメントビュワーです(Electron製)(ベータ版)
配信する方はぜひどうぞ
- https://tubug.netlify.app