[React]入力フォームでフォーカスが外れたらエラーメッセージを出す、他

2023/12/07 (木) - 00:00 JavaScript

Reactでフロントを構築している入力フォームなどで、入力フォームでフォーカスが外れたら「入力されていません」などのメッセージを出したい時。単純にonBlurイベントハンドラで関数を指定すればいいだけのようです。

import React, { useState } from 'react';
function InputTextbox() {
 const [InputName, setInputName] = useState('');
 const [formErr, setformErr] = useState('');
 const onChange = (e) => {
  setInputName(e.target.value);
 };
 const onBlur = (e) =>{
  //ここで何かしらの処理判定
  if(!e.target.value){
   setformErr('入れてね');
  }else{
   setformErr('');
  }
 }
 return(
  <form>
   <label htmlFor="name">お名前:
    <input id="name" name="name" type="text" placeholder="お名前を入れてください" value={InputName} onChange={onChange} onBlur={onBlur} required />
    <em>{formErr}</em>
   </label>
  </form>
 );
}
export default InputTextbox;

例では未記入の場合はエラーを出すですが、これを応用して「郵便番号の書式になっていなかったらエラーを出す」「電話番号の桁が間違っていたら警告を出す」等の処理を噛ませばいいと思います。

エラーのイメージ

おまけ:入力中に文字を処理する

onChangeイベントでもリアルタイムに文字を処理することが出来ます。以下の例ではメールアドレスで使えない文字が入ったら除去するみたいなやつです。

import React, { useState } from 'react';
function InputEmailbox() {
 const [InputEmail, setInputEmail] = useState('');
 const onChange = (e) => {
  //ここで何かしらの処理
  const mailStr = e.target.value.replace(/[^@_/.-0-9a-z]/gi, ''); //英数記号以外の文字は除去
  //stateを更新
  setInputEmail(mailStr);
 };
 return(
  <form>
   <label htmlFor="email">メールアドレス:
    <input id="email" name="email" type="email" placeholder="メールアドレスを入れてください" value={InputEmail} onChange={onChange} required />
    <em>{formErr}</em>
   </label>
  </form>
 );
}
export default InputEmailbox;

おまけ:クレジットカード番号の入力支援

4111111111111111という風に文字入力すると、4111 1111 1111 1111とスペースで区切られます。

import { useState } from "react";
function InputCreditCard() {
 const [value,setValue] = useState('');
 const onChange = (e) => {
  const inputValue = e.target.value.replace(/[^0-9]/g, '').match(/.{1,4}/g);
  const inputValueJoin = inputValue ? inputValue.join(' ') : '';
  setValue(inputValueJoin);
 }
 return (
  <div>
   <input id="ccn" name="ccn" type="tel" maxLength="19" value={value} onChange={onChange} />
  </div>
 );
}
export default InputCreditCard;

PC ブラウザ版のYoutubeでピクチャー イン ピクチャー(PiP)を使う

2023/12/05 (火) - 00:00 Others

PCのブラウザ版Youtubeでは出ピクチャー・イン・ピクチャーが出来ないと思っていたのですが、実は出来ました。できることを知ってる人も多いとは思いますが個人的メモ。ピクチャー イン ピクチャーとは他のアプリで作業しながら、その画面上に動画をフローティングさせ再生できる機能のことです。

Youtubeで右クリック

通常ブラウザに表示される動画は右クリックなどのコンテキストメニューから「ピクチャー イン ピクチャー」を選択すると実現できるのですが、Youtubeではこのようにオリジナルのコンテキストメニューが表示されてしまいます。ピクチャー イン ピクチャーの選択肢はありません。

Youtubeでさらに右クリック

しかし、このメニューを表示させた状態でさらに動画のどこかで更に右クリックすると、このようにコンテストメニューが表示されその中にある「ピクチャー イン ピクチャー」を選択すると、ピクチャー イン ピクチャーを実現することが出来ます。

ピクチャー イン ピクチャー再生中のデスクトップ画面

このように天気予報を見ながら作業したり、ウェビナーやWebスクールの授業動画を見ながら実践できたりするのでおすすめです。GoogleChrome、Safari、Firefoxなどでも同様に実現可能です。

EC-CUBE4で商品タグを指定したアイテムをブロックに表示させる

2023/12/01 (金) - 00:00 PHP&CMS

EC-CUBE4でトップページや下層ページなどに、特定の商品タグを指定した商品を表示させる方法。例えばピックアップ商品タグやおすすめ商品タグとして指定した商品をページ内に並べて販促訴求したい場合などに使えます。

まず、予めピックアップしたい商品タグを用意します。今回は例として[おすすめ商品]タグの商品を表示させるとし、タグIDを[2]とします。

商品タグのIDを把握します

商品ブロックを準備

EC-CUBEにログインし、[コンテンツ管理]→[ブロック管理]に遷移し、[新規作成]を押下し新規ブロックを作成します。ブロックのテンプレートファイルをrecommend_item.twigとします。

新規ブロックを作成

制御用PHPファイルの準備

動的に商品を呼び出すPHPファイルを{EC-CUBE4のディレクトリ}/app/Customize/Twig/Extension/のディレクトリに作成します。今回は例としてTagExtension.phpとします。

<?php
namespace Customize\Twig\Extension;
use Doctrine\ORM\EntityManagerInterface;
use Eccube\Repository\ProductRepository;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
class TagExtension extends AbstractExtension{
 public function __construct(
  EntityManagerInterface $entityManager,
  ProductRepository $productRepository
 ) {
  $this->entityManager = $entityManager;
  $this->productRepository = $productRepository;
 }
 public function getFunctions(){
  return [
   new TwigFunction('TagProducts',array($this, 'getTagProducts')),
  ];
 }
 public function getTagProducts(){
  $DB = $this->entityManager->getRepository('Eccube\Entity\Product')
  ->createQueryBuilder('Prod');
  $DB->innerJoin('Prod.ProductTag', 'ProTag')
  ->innerJoin('ProTag.Tag', 'Tag')
  ->andWhere('Tag = :Tag')
  ->setParameter('Tag', 2);//タグIDを指定 2とする
  $Product = $DB->getQuery()->getResult();
  return $Product;
 }
}
?>

商品ブロックのtwigファイルを編集

recommend_item.twig に以下を記述します。

{% set recoProducts = TagProducts() %}
{% if recoProducts|length > 0 %}
 {% for Product in recoProducts %}
  <div>
   <a href="{{ url('product_detail', {'id': Product.id}) }}">
   <img src="{{ asset(Product.main_list_image|no_image_product, 'save_image') }}">
   <p class="productName">{{ Product.name }}</p>
   <p class="productPrice">{% if Product.hasProductClass %}
 {% if Product.getPrice02Min == Product.getPrice02Max %}
  {{ Product.getPrice02IncTaxMin|price }}
 {% else %}
  {{ Product.getPrice02IncTaxMin|price }} ~ {{ Product.getPrice02IncTaxMax|price }}
 {% endif %}
{% else %}
 {{ Product.getPrice02IncTaxMin|price }}
{% endif %}</p>
   </a>
  </div>
 {% endfor %}
{% endif %}

最後に、作成したブロックを[レイアウト管理]の管理画面で、任意の位置に配置します。デザインなどはCSSでカスタマイズしてください。以上で[おすすめ商品]タグを指定した商品が、おすすめ商品ブロックに表示されます。

おすすめ商品ブロックを表示した例

以上。

ngnixでドメインのwww有無を統一し、https(SSL)へリダイレクトをする

2023/11/28 (火) - 00:00 Server

nginxのWebサーバで、www有無を統一しなおかつHTTPSにリダイレクトしたい。例えば、http://example.com、http://www.example.com、https://www.example.com のドメインでアクセスしたら、https://example.com のドメインにリダイレクトしたい、などという時。

nginxのdefault.confを開きます。

$ sudo vi /etc/nginx/conf.d/default.conf

対象サーバに以下のように記述し保存します。

〜中略〜
server {
 listen 80 default_server;
 listen 443;
 server_name www.example.com;
 return 301 https://example.com$request_uri; #HTTPS化&www有無ドメインを統一
}
server {
 listen 443;
 ssl on;
 server_name example.com;
 root /var/www/html/;
 error_page 403 404 500 503 =404 /error.html;
 location = /error.html{
  root /var/www/html/;
  internal;
 }
〜中略〜

保存したら nginxを再起動しませう。

$ sudo service nginx restart

Reactで改行コードを<br />に変換したい時

2023/11/26 (日) - 00:00 JavaScript

ReactでPHPのnl2brのような事をしたい時。たとえばAPIなどで以下のtextのような改行コード混じりの値があったとします。これをこのまま表示させてもHTMLでは改行せず読みづらくなってしまいます。

※こちらは気象庁の公式APIで東京都の天気予報のJSONです。

{
 "publishingOffice": "気象庁",
 "reportDatetime": (中略),
 "targetArea": "東京都",
 "headlineText": "",
 "text": " 日本付近は冬型の気圧配置となっています。\n\n 東京地方は、晴れています。\n\n(中略)"
}

単純に\nの改行コードをreplaceで置換して<br />にしてもそのまま文字列としてエスケープされてしまい改行しませんでした。ではどうするか…

フラグメントを使う

そこでフラグメントを使います。Fragment、或いは<>〜</>で包括した部分を単体でまとめることが出来ます。

import React, { Component } from 'react';
import { render } from 'react-dom';
class Weather extends Component {
 constructor(props) {
  super(props);
  this.state = {
   isLoaded: false,
   data: []
  };
 }
 componentDidMount() {
  fetch('https://www.jma.go.jp/bosai/forecast/data/overview_forecast/130000.json')
   .then(res => res.json())
   .then(json => {
    this.setState({
     isLoaded: true,
     data: json
    });
   });
 }
 render() {
  var { data, isLoaded } = this.state;
  if (!isLoaded) {
   return <p>お待ち下さい</p>;
  } else {
   return (
    <section>
    <h1>{data.targetArea}の天気</h1>
    <p>{ data.text.split( /\n/ ).map( ( item ) => {
      return (
       <React.Fragment>{ item }<br /></React.Fragment>
      );
     } )
    }</p>
    </section>
   );
  }
 }
}
render(<Weather />, document.getElementById('app'));

こちらではtextの値に対して改行コードの数だけループを回し、<br />で分割しています。

改行を反映した表示結果

ただフラグメントをあまり多用すると、コードの可読性が悪くなるうえにパフォーマンスにも影響が出てくるので、ここぞと言うときだけにとどめた方が良いと思います。

JavaScriptでCSVファイルを読み込み、表として表示する

2023/11/24 (金) - 00:00 JavaScript

JavaScriptでCSVを読み込み、HTMLのTable要素の表として表示させます。

今回は例として以下のCSVを用意しました。番号、都道府県、年齢、職業、性別の値を持っているものとします。

1,東京都,24歳,学生,その他
2,北海道,35歳,会社員,女性
3,神奈川県,30歳,自営業,男性
4,大阪府,15歳,学生,女性
5,沖縄県,29歳,会社員,男性

HTML側ではベースとなるtable要素を用意しておきます。

<table id="tableCSV">
<tr>
 <th>番号</th>
 <th>都道府県</th>
 <th>年齢</th>
 <th>職業</th>
 <th>性別</th>
</tr>
</table>

以下が実行コードのJavaScriptとなります。

async function loadCSV() {
 const data = await fetch("data.csv");
 const resCSV = await data.text();
 const dataCSV = resCSV.trim().split(/\r\n|\n/).map(line => line.split(','));
 const htmlData = dataCSV.map(data => `<tr><th>${data[0]}</th><td>${data[1]}</td><td>${data[2]}</td><td>${data[3]}</td><td>${data[4]}</td></tr>`).join('');
 document.getElementById('tableCSV').insertAdjacentHTML('beforeend',htmlData);
}
loadCSV();

仕組みとしては外部のCSVファイルをテキストとして読み込んだ後、その値を1行毎分割し配列に変換します。さらにその配列をカンマで区切ってまた配列に変換し、その値をHTMLに埋め込んで表示処理をさせます。

実行した画面は以下のようになります。見た目はCSSで調整ください。

CSV表示結果

注意点としては、文字エンコーディングがHTML側と同じ(UTF-8等)でないと、マルチバイトの文字を読み込んだ際に文字化けしてしまうことです。正常に処理するために外部ファイル、JavaScript、HTMLの文字コードを一致させるようにしましょう(とくにMicrosoftExcelでCSV形式で保存するとデフォルトではShift_JISとなってしまうため)。

同人誌やWeb漫画に使える!Adobe Fontsの貂明朝アンチック

2023/11/21 (火) - 00:00 Design

Adobe Fontsで新たに「貂明朝アンチック」というフォントがリリースされました。その名の通りアンチック系の書体で、漫画のセリフなどで使える書体になります。漫画同人誌での利用のほか、ブログやX(旧Twitter)で掲載するWeb漫画など活用の場面が多くなりそうです。

しかも、Adobe IDを持っていればAdobe Fontsから無料でダウンロードできるので、Adobe CCを契約している人は即使えて非常に便利です。早速使ってみました。

そうかな…そうかも…

アンチック体は昔からの名残の書体で、ひらがなやカタカナが明朝体、漢字がゴシック体であることが特徴。さらに貂明朝アンチックはウェイトが6種類用意されており場面に合わせて調整可能です。

戸棚のウラはネズミの卵でいっぱいだー!!

漫画独特の嘆符・疑問符も縦書きに対し横並びになるなど扱いやすく種類も多いです。従来はPhotoshopやIllustratorなどで縦中横のオプションを利用する必要がありましたが、その必要もなく自動で横並びの入力が可能です。

かめはめ波 魔貫光殺砲 やったか!?

OpenTypeフォント機能の[前後関係に依存する字形]をオンにすることで、音を引き伸ばす長音の棒や波線も描写されるなど、非常に使い勝手が良いです。

同じくAdobe Fontsから入手できるアンチック体のしっぽりアンチックと比較します。

新世界の神となる

しっぽりアンチックと比較すると、貂明朝アンチックは仮名と漢字のサイズのバランスが調整され仮名が小さくなっています。また、しっぽりアンチックはウェイトが1種しかダウンロードできないのに対し、貂明朝アンチックは6ウェイトあるので表現の幅が広がります。

長音の音引きや嘆符・疑問符の表現も貂明朝アンチックのほうが豊富なので、貂明朝アンチックを選択するのは有りなのではないでしょうか。

PokeAPIとJavaScriptで日本語のポケモン情報をゲットする

2023/11/17 (金) - 00:00 JavaScript

ポケモンに関する様々な情報を取得できるAPIPokeAPI。勉強がてらPokeAPIでポケモンの簡単な情報を取得できるスクリプトを書いてみました。素のJavaScriptのみですが、コードがあまりきれいじゃないのでエレガントに書きたいものです。なお、PokeAPIで取れる情報は以下の記事を参考にしています。

一意のポケモンのデータを取得するには、https://pokeapi.co/api/v2/pokemon/{ポケモンの番号} でアクセスすると該当するポケモンの情報がJSON形式で返ってきます。ポケモンの番号の指定が1ならフシギダネ、448ならルカリオといった感じです。それを活用しポケモンの番号、ポケモンの名前、ポケモンの画像、ポケモンのタイプを表示させてみます。

まずはHTMLコード。

<p id="txtNo">{ポケモンの番号}</p>
<img src="{ポケモンの画像}" alt="" loading="lazy" id="imgPokemon">
<p id="txtType">{ポケモンのタイプ}</p>
<p id="txtPokemon">{ポケモンの名前}</p>

JavaScriptコード。pokeNoの値に番号を指定します。サンプルでは25を指定しています。

const pokeNo = 25; //ポケモン番号
const pokeAPI = 'https://pokeapi.co/api/v2/pokemon/'+pokeNo;
const txtNo = document.getElementById('txtNo');
const txtType = document.getElementById('txtType');
const txtPokemon = document.getElementById('txtPokemon');
const imgPokemon = document.getElementById('imgPokemon');
const getAPI = async(url) => {
 const data = await fetch(url).then((res) => res.json());
 return data;
}
async function getPokemonName(data){
 const PokemonName = data.names.find((val) => val.language.name === "ja");
 return PokemonName.name;
}
async function getPokemonType(data){
 let typeLength = data.length;
 let type = new Array();
 for (let i = 0 ; i < typeLength ; i++){
  const getTypes = await getAPI(data[i].type.url);
  const getType = getTypes.names.find((val) => val.language.name === "ja");
  type[i] = getType.name;
 }
 return type;
}
async function init(){
  const dataAPI = await getAPI(pokeAPI);
  const getIMG = dataAPI.sprites.other['official-artwork'].front_default;
  const getName = await getAPI(dataAPI.species.url);
  getPokemonType(dataAPI.types).then(res => {
   txtType.textContent = res;
  });
  getPokemonName(getName).then(res => {
   txtPokemon.textContent = res;
  });
  imgPokemon.setAttribute('src',getIMG);
  txtNo.textContent = 'No.'+pokeNo;
}
init();

PokeAPIは多言語に対応しております。しかしデフォルトのままだとすべて英語になってしまいます。そこでポケモンの名前とタイプはそれぞれlanguage.nameの値がja(日本語)のものを走査し、該当する日本語の値を取得しています。

実行結果です。25番のピカチュウが表示されました。

ピカチュウの情報

番号を変えてみます。250番のホウオウです。ポケモンのタイプは複数ある場合があるのでそれぞれ配列で処理されます。

ホウオウの情報

現時点で最新版のポケモン スカーレット・バイオレットのパルデア地方のポケモン情報も取得できます。

ニャオハの情報

ポケモン情報、ゲットだぜ!

WordPressでブログをAWS CloudFrontで配信する時のビヘイビア設定

2023/11/11 (土) - 00:00 PHP&CMSServer

AWSでEC2とCloudFront経由で非常にシンプルなWordPressのサイトを構築したときのはなし。検索ページや記事へのコメントフォームを実装したり、アクセスのたびに関連記事やお勧め記事をランダムに表示させる実装をしたのであまりキャッシュがしつこいと効果が薄れてしまう…その時に設定したかなりザックリとしたCloudFrontのキャッシュ設定の備忘録。

今回CloudFrontを1から導入する場合の手順や、ドメイン設定、SSL証明書設定などは割愛しています。

AWSのおおまかな構成図

AWSのコンソールからCloudFrontにアクセスし、[ディストリビューション]→[対象のディストリビューション]にアクセス。[ビヘイビア]のタブを押して移動し、[ビヘイビアを作成]を押下します。

フロントのキャッシュ設定

  • パスパターン:/WordPressのディレクトリ/*
  • オリジンとオリジングループ:WordPressのあるEC2を選択
  • オブジェクトを自動的に圧縮:Yes
  • ビューワープロトコルポリシー:Redirect HTTP to HTTPS
  • ビュワーのアクセスを制限する: No
  • 許可された HTTP メソッド:GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE(検索やコメント、フォームプラグインなどを使う場合)
  • キャッシュキーとオリジンリクエスト:Legacy cache settings
  • ヘッダー:すべて
  • クエリ文字列:すべて(プラグインやアクセス解析などを使う場合)
  • cookie:すべて(プラグインやアクセス解析などを使う場合)
  • オブジェクトキャッシュ:Customize
    • 最小 TTL:300
    • 最大 TTL:300
    • デフォルト TTL:300

と設定し保存しデプロイが終わるまでしばし待ちます。

コンタクトフォームやWordPressの管理画面用のキャッシュ設定

コンタクトフォームや管理画面や動的なのでキャッシュさせてしまうと厄介です。キャッシュさせないようTTLを0にします。

CloudFront ビヘイビア設定

  • パスパターン:/WordPressのディレクトリ/wp-admin/* & /WordPressのディレクトリ/wp-login.php & /WordPressのディレクトリ/フォームのパーマリンク/*
  • オリジンとオリジングループ:WordPressのあるEC2を選択
  • オブジェクトを自動的に圧縮:Yes
  • ビューワープロトコルポリシー:Redirect HTTP to HTTPS
  • ビュワーのアクセスを制限する: No
  • 許可された HTTP メソッド:GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE
  • キャッシュキーとオリジンリクエスト:Legacy cache settings
  • ヘッダー:すべて
  • クエリ文字列:すべて
  • cookie:すべて
  • オブジェクトキャッシュ:Customize
    • 最小 TTL:0
    • 最大 TTL:0
    • デフォルト TTL:0

と設定し保存しデプロイが終わるまでしばし待ちます。

WordPressの設定

EC2からCloudFrontに変えてしまうと、EC2にあるWordPressに直接アクセスできなくなります。そこでWordPressのURL設定を変更します。WordPressのデータベースでURLを変えてもいいのですが、最も楽なのはwp-config.phpで直接設定値を変更することです。

$ cd /WordPressのディレクトリ/
$ vi wp-config.php

wp-config.phpを開き

define('WP_HOME','https://クラウドフロントのドメイン/WordPressのディレクトリ');
define('WP_SITEURL','https://クラウドフロントのドメイン/WordPressのディレクトリ');

WP_HOMEWP_SITEURLの設定値と編集し保存します。ブラウザでWordPressにアクセスし正常に表示され、遷移できること確認します。その上で管理画面にアクセスし問題なくログインができ、正常に操作ができることも確認ください。

PHPでURLリライト(rewrite)を使ってみる

2023/11/03 (金) - 00:00 PHP&CMSServer

WordPressのようなCMSやLaravelのようなPHPフレームワークでは、Webサーバのrewrite機能でURLを取得して処理分岐をするケースが多いです。

rewrite機能(URLのリライト)とは、ユーザなどがURLにアクセスした際そのリクエストに応じてサーバーやプログラムが適切なレスポンスを返す処理です。例えば、https://example.com/hoge/というURLにアクセスしているけど/hoge/というディレクトリを表示させるのではなく、実際は内部処理で https://example.com/?page=hogeという内容を表示させたりできる…という機能になります。

今日はURL rewriteをつかってPHPの簡単な実験をしてみます。

Webサーバーの設定

Apacheの場合は対象ディレクトリの.htaccessや、https.confの対象のディレクトリ設定箇所に以下を記述します。予めmod_rewriteを有効にしている前提となります。

RewriteEngine On
RewriteRule ^ index\.php [L]

nginxの場合は、nginx.confの対象ディレクトリの設定箇所に以下を記述します。

location / {
 index index.php;
 rewrite ^ /index.php last;

上記を記述し反映すると、対象のディレクトリにアクセスするとindex.phpの内容が表示されます。

PHPの記述

テストでindex.phpに以下を記述し、アクセスします。すると…

<?php $url = $_SERVER['REQUEST_URI']; ?>

表示結果は

/

と表示されるはずです。また/test/にアクセスすると

表示結果

/test/

と表示されるはずです。

URLによって処理を分岐する

以下のようなテストプログラムを書いてみます。

<p><?php $url = $_SERVER['REQUEST_URI']; ?></p>
ディレクトリ:<?php  echo $url; ?>
<?php if($url=='/'){ ?>
<h1>トップページです</h1>
<?php }elseif($url=='/company/'){ ?>
<h1>会社概要ページです</h1>
<?php }elseif($url=='/service/'){ ?>
<h1>サービスページです</h1>
<?php }else{ ?>
<h1>ページが見つかりません</h1>
<?php } ?>

/にアクセスすると…

表示結果

ディレクトリ:/
トップページです

/company/にアクセスすると…

表示結果

ディレクトリ:/company/
会社概要ページです

アクセスしたイメージ

と表示されると思います。以上、簡易的なURLRewriteの実験でした。

ページの先頭へ