20200114のvue.jsに関する記事は7件です。

VuexってThe Elm Architectureみたいに状態を管理出来るの?

半年ぐらい前にElmを少し触って時の印象としては、
- アーキテクチャが決まってるので考えることがない。
- Haskellに似てるけど難しくない言語なので書くことが少ない。
- JavaScriptで書かれたライブラリを使うのが面倒い。
という印象でした。

Elmでthreejsを使うみたいなことは意外と面倒くさそうなんです。

もし、VuexがElmのようにアーキテクチャが決まってるとしたら、考えることがない上に、JavaScriptのライブラリも使えるので素晴らしいのでは?
あとはHaskellに似た文法だけど、TypeScriptで書けば型はあるしそこそこ楽できるのでは?

TypeScript + Vuex + threejsでなんかやりたいなぁ。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

vue.jsの<a href="#" v-on:click="openWindow">で別ウィンドウリンク開こうとしたら親ウィンドウのURLからパスパラメータが消滅した

久しぶりの投稿です。開発した際に躓いたポイントの備忘のため

やりたかったこと

vue.jsにて作成したアプリ内のリンクから、別ウィンドウにて別サイトのURLを開きたい
ここではリンクを設置したルーターパスを/linkとします。
下記のようなリンクのイメージ
https://vuejsproduction.jp/#/link
aタグリンクをクリックしたら別サイトが別ドメインにて開かれるように設計

サンプルソース

いろいろ省略してますが悪しからず

test.vue
<template>
<div>
 <p>こんにちは。<a hlef="#" v-on:click="openWindow">こちら</a>を押したら別ページに飛びます
</div>
</template>

<script>
 methods: {
  openWindow: function() {
   const url = "https://google.com"
   window.open(url, '_blank', 'width=1024,height=768,scrollbars=yes,resizable=yes')
  }
 }
</script>

何が起きたか

サブウィンドウは開かれました。が、
親ウィンドウのアドレスバーが何やら不穏な感じに
https://vuejsproduction.jp/#/
vue-routerで定義しているパスパラメータ部分がすっぽり抜け落ちてしまっていますね。
親ウィンドウ側のURLは変化してほしくなかったので下記のように修正

修正後サンプルソース

test.vue
<template>
<div>
 <p>こんにちは。<a hlef="#" v-on:click.stop.prevent="openWindow">こちら</a>を押したら別ページに飛びます
</div>
</template>

<script>
 methods: {
  openWindow: function() {
   const url = "https://google.com"
   window.open(url, '_blank', 'width=1024,height=768,scrollbars=yes,resizable=yes')
  }
 }
</script>

v-on:click.stop.preventをつけてあげたら大丈夫でした。

.stopでaタグイベントハンドラ自体の親要素への伝搬を止め、
.preventでaタグのイベントを通常通り処理すべきでないと指示している感じらしいです。

参考URL:https://techblog.roxx.co.jp/entry/2019/02/08/122914

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Nuxt.js】ローカル環境をhttpsで起動する方法

なぜこれが必要なのか

位置情報などを取得するGeolocationなど、http通信では動作しないものを動作させたい場合など。

どうやってやればいいか

nuxt.jsの設定でやる方法と、外部サービスを利用する2パターンがあります。

  • Nuxt.jsの設定でやる方法
  • 外部サービス(ngrok)を利用する方法

Nuxt.jsの設定でやる方法

実行環境

  • @nuxt/cli v2.8.1
  • macOS Catalina(v10.15.2)
  • パッケージ管理はyarn

自己署名入り証明書の作成

mkcertを使って、オレオレ証明書を作成します。(コマンド内容は割愛)

$ brew install mkcert
$ mkcert -install
$ mkcert localhost

上記手順で、以下の証明書が発行されます。

  • localhost.pem
  • localhost-key.pem

起動設定にhttpsの起動設定を追加

nuxt.config.jsに、先程作成した証明書を設定します。
Ref:公式ドキュメント - serverプロパティ(HTTPS設定)

  • importの追加
  • serverプロパティにhttpsのプロパティを追加して、発行した証明書を設定する
nuxt.config.js
import path from 'path'
import fs from 'fs'

export default {
server: {
    port: 3000,
    host: "0.0.0.0",
    https: {
      key: fs.readFileSync(path.resolve(__dirname, "localhost-key.pem")),
      cert: fs.readFileSync(path.resolve(__dirname, "localhost.pem"))
    },
    // 以下その他設定...
}

起動

この状態で

$ yarn run dev

とすればhttpsでローカル環境が起動します。

外部サービスを利用する方法

設定周りをいじらなくても、ngrokというサービスを使うと、ローカル環境をhttpsで公開してくれます。便利!

利用方法は以下のブログが詳しく説明されています(外部リンク)
ngrokを使ってローカル開発中のVueアプリをHTTPSで公開する

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsを参考に、BladeファイルにJavaScript, CSSをまとめてみる。

Vue.jsの開発者的メリット

  • HTML, JavaScript, CSSを1つのファイルに記述するというのがシンプルで良い
  • scopedが便利
  • コードが複雑化しにくい

そして思ったのです。
これって普通のアプリケーションにある程度応用できそう。

実際に考えてみる

ちょっと考えてみました。方針としては、

  • 仕様に応じてBladeのディレクトリ構成をシンプルに考える
  • JavaScript, CSSはBladeのディレクトリ構成に合わせる。
  • 影響範囲は対応するBladeファイル内にとどまるように気をつける。

Bladeファイルのディレクトリ構成

サンプルです。実際には画面仕様などにより異なるはずです。

  • ルール
    • views/直下は、layouts/および、メニューに対応するディレクトリ(menus/を作ろうと思ったが階層が深くなりすぎるのが嫌なので却下)
    • メニューごとのディレクトリには、ページに対応するbladeファイルがある
    • メニューごとのディレクトリには、parts/があり、ページを構成する部品を格納する。
    • formやbuttonなどの部品はparts/に格納する。
    • ページに対応するファイルからは、parts/配下のファイルをincludeする。
    • parts/配下のファイルからは、parts/配下のファイルををincludeする
    • ページに対応するファイルから、parts/配下の部品をincludeしない
    • 同階層のファイルをincludeしない
resources/
- views/
    - layouts/
        - app
        - parts/
            - header
            - footer
    - home/
        - index
        - parts/
            - eye_catching
            - new_product
    - product/
        - index
        - show
        - edit
        - parts/
            - product
            - details
    - account/
        - show
        - edit
        - parts/
            - table
    - parts/
        - forms/
            - radio
            - checkbox
        - buttons/
            - search
            - submit
            - delete
        - flash_message

JavaScriptとCSS(Sass)は、Bladeファイルと同じディレクトリ構成

JavaScript, CSS(Sass)はBladeファイルと同じディレクトリ構成とします。

resources/
- js
    - modules/
        - confirm
    - layouts/
        - app
        - parts/
            - header
    - ...

- sass
    - layouts/
        - app
        - parts/
            - header
    - ...

Bladeファイルに、JavaScriptとCSSを記述する。

まず、レイアウトファイル

layout/app.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">

  {{-- CSRF Token --}}
  <meta name="csrf-token" content="{{ csrf_token() }}">

  {{-- title --}}
  <title>@yield('title', 'laravel app')</title>

  {{-- 対応するCSSファイル --}}
  <link rel="stylesheet" href="{{ mix('css/layouts/app.css') }}">

  {{-- include先のcssファイルがここに追加される --}}
  @yield('css')
</head>
<body>

  @include('layouts.parts.header')

  <div id="container">
    @yield('content')
  </div>

  @include('layouts.parts.footer')

  {{-- 対応するJSファイル --}}
  <script src="{{ mix('js/layout/app.js') }}"></script>

  {{-- include先のJSファイルがここに追加される --}}
  @yield('js')

</body>
</html>

そして、includeするファイル

layouts/parts/header.blade.php
@section('css')
  <script src={{ mix('/js/layouts/parts/header.css') }}>
@endsection

@section('js')
  <script src={{ mix('/js/layouts/parts/header.js') }}>
@endsection

<header>
  <div>ヘッダー</div>
</header>

これでOKと思ったのですが、これでは、include先に記述されたJavaScript, CSSが反映されません。
そこで@stack - @pushを利用します。

layout/app.blade.php
<head>
  (省略)

  {{-- include先のcssファイルがここに追加される --}}
  @stack('css')
</head>

<body>
  (省略)

  {{-- include先のJSファイルがここに追加される --}}
  @stack('js')
</body>
layouts/parts/header.blade.php
@push('css')
  <script src={{ mix('/js/layouts/parts/header.css') }}>
@endpush

@push('js')
  <script src={{ mix('/js/layouts/parts/header.js') }}>
@endpush

<header>
  <div>ヘッダー</div>
</header>

これで、include先で記述されたJavaScript, CSSファイルが反映されるようになりました。
ただし、同じファイルを複数回includeすると回数分JavaScript, CSSが読み込まれてしまいます。

home/index.blade.php
  // deleteボタンを複数回includeすると回数分JavaScript, CSSが読み込まれる。
  @include('parts.buttons.delete')
  @include('parts.buttons.delete')
  @include('parts.buttons.delete')

ですので、1度だけ読み込むように、カスタムディレクティブを設定します。
参考: bladeのcomponent化による再利用

app/Providers/AppServiceProvider.php
/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    \Blade::directive('pushonce', function ($expression) {
        $var = '$__env->{"__pushonce_" . md5(__FILE__ . ":" . __LINE__)}';

        return "<?php if(!isset({$var})): {$var} = true; \$__env->startPush({$expression}); ?>";
    });

    \Blade::directive('endpushonce', function ($expression) {
        return '<?php $__env->stopPush(); endif; ?>';
    });
}
layouts/parts/header.blade.php
@pushonce('css')
  <script src={{ mix('/js/layouts/parts/header.css') }}>
@endpushonce

@pushonce('js')
  <script src={{ mix('/js/layouts/parts/header.js') }}>
@endpushonce

<header>
  <div>ヘッダー</div>
</header>

レイアウトファイルも少し修正します。
includeする前に@stackを記述するとその時点では、pushされるファイルがないので、
include先のファイルが反映されません。
そのため、bodyの閉じタグの直前に記述します。(ここは少し悩ましいところです。。。)
参考: Blade Stacks in Sub-View

layout/app.blade.php
<head>
  (省略)

  // ここだとinclude先のファイルが反映されない
  {{-- @stack('css') --}}
</head>

<body>
  (省略)

  {{-- include先のcssファイルがここに追加される --}}
  @stack('css')

  {{-- include先のJSファイルがここに追加される --}}
  @stack('js')
</body>

これで、include先のファイルも反映されるようになります。

なお、CSSは原則そのファイルにしかスタイルが適用されないよう、
スコープを限定するように記載します。

home/index.scss
// home/index.blade.php
// <div class="home-index">
//   <div class="test-class">
//     コンテンツ
//  </div>
// </div>

.home-index { // スコープを限定する
  .test-class {

  }
}

以上です。

参考

bladeのcomponent化による再利用
Blade Stacks in Sub-View
Blade Templates

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Nuxt.js】inject is 何?

TL;DR.

  • injectを利用すると関数を共通化することができる
  • 関数はthis.$hogeで呼び出すことができる

injectを使用した処理の例

ページに'hoge'を返す関数を作ってみる

<plugins/hoge.js>

export default function injectHoge(context, inject) {
  const returnHoge = num => {
    return 'hoge' * num
  }
  inject('hoge', { get: returnHoge })
}

これをnuxt.config.jsに設定する

<nuxt.config.js>

plugins: [
  { src: '@/plugins/hoge', ssr: false }
]

このようにするとthis.$hoge.get(num)でVueインスタンスで関数を実行することができる

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Nuxt.js】 inject is 何?

TL;DR.

  • injectを利用すると関数を共通化することができる
  • 関数はthis.$hogeで呼び出すことができる

injectを使用した処理の例

ページに'hoge'を返す関数を作ってみる

plugins/hoge.js
const returnHoge = () => {
  return 'hoge'
}

export default(context, inject) {
  inject('hoge', returnHoge)
}

これをnuxt.config.jsに設定する

nuxt.config.js
plugins: [
  { src: '@/plugins/hoge', ssr: false }
]

このようにするとthis.$hogeでVueインスタンスで関数を実行することができる

引数を渡したい場合や、関数名を代替したい場合はこのようにする

const calcNumber = (num) => {
  return 3 * num
}

export default(context, inject) {
  inject('calc', { get: calcNumber })
}

これでthis.$calc.get(4)を実行すると12が返ってくる

コンポーネントから呼び出す場合

asyncDataやfetchで使用したい場合はこのように呼び出す

pages/index.vue
export default {
  asyncData(context) {
    context.app.$hoge()
  }
}

injectを使用する利点

  • 確定した処理の共通化が可能
    • 関数を呼び出して必ず実行される処理や分岐に対しては有効
    • ex. 取得処理が成功したら値を返し、失敗したらエラーの処理を実行するなど
  • いちいちページでimportしなくていい
    • これに関しては何を呼び出しているかが明示的にわかりにくくなるので、良し悪しがある
    • 共通化したい処理の中でも関数の実態を把握できるような関数名や共通認識があるものであれば良いが、そうでなければページに残したいところ。。
    • axiosやfirebaseのget, delete、日付処理など、誰が見てもわかり切っているものなら便利そう
  • Vuexストア内でも同様にthis.$で関数を呼ぶことができる
  • 同様の方法にmixinを使用して関数をグローバルに実行する方法があるが、injectの場合はasyncDatafetch内でも共通の関数を利用することができる

参照

Nuxt.js 統合された注入

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Salesforce の開発メモ

メインは Web アプリケーションの開発ですが最近 Salesforce (以下 SF) の開発も関わるようになっていて、徐々に知見が溜まってきたので整理がてらメモしていきます。

キャッチアップ

Trailhead とドキュメント。どちらも量が凄いので圧倒される。

Trailhead

  • SF を使っていく上での教材らしい。自習用の SF 環境も提供されて、自由に使える。
  • 全部やる必要はなく、管理者向け、開発者向けなどコンテンツが別れている。
  • 地味に時間を食う。概念や背景の説明だったりサンプルの事例を読ませたりしてくるので。
  • 必要そうな単元をつまみ食いしてるけど通しでちゃんとやるのがよさそうな気はする。
  • ぐぐって引っ掛かったページにアクセスした際、 Trailhead へのログインのセッションが切れているとトップページに戻されてしまうのが困る。

公式ドキュメント

  • 読んでいくのは辛いので、必要な時に必要な場所だけ読む。
  • でも表示に時間が掛かるので頻繁な利用はストレスになる。
  • Trailhead に無い情報を探す時、 SF の機能の確認、 Class や Object の仕様を調べる時などに使ってる。

開発環境

VSCode + SFDX

バックエンド (Apex)

言語

  • Apex という言語で開発をする。古い Java っぽいらしい。 Java はよく知らないから今のところふんいきでやってる。
  • 静的片付け言語なのでコードの読み書き自体は楽。
  • 本番リリースをするためには、テストコードを作成して Apex コード全体に対しカバレッジが 75% を越えている必要がある。

Apex 内での DML 実行とコールアウト

同一トランザクション中で DML 実行後にコールアウト (Web API の利用など) を行うことは出来ない。
コールアウトを行ってからの DML 操作は出来る。

(参考: 「You have uncommitted work pending. Please commit or rollback before calling out」エラーについて)

SQL (SOQL)

アスタリスクが無い

SELECT * FROM Account みたいなことが出来ないので、カラムは指定しなきゃいけない。
負荷対策で出来なくしていると考えれば腑に落ちる。
Apex でオブジェクトの項目名を取得する事も出来るみたいなので、同様の事をやりたければこれになるのかも。(参考: Apex上でSelect *を実装してみた - Qiita)

クエリへの Apex 変数の組み込み

そのまま

String id = getAccountId();
Account acc = [SELECT Id, Name FROM Account WHERE Id = :id LIMIT 1];

文字列の連結

String keyword = 'foo';
List<Account> accList = [SELECT Id, Name FROM Account WHERE Name LIKE :('%' + keyword + '%')];

フロントエンド (Visualforce)

Visualforce (以下 VF) を使ったフロントエンドのマークアップ、開発について。

言語

  • 言語というか、仕組み?組み込みコンポーネント?
  • 何かを表示するための基本的な画面を作る際にこの仕組みを使う。
  • HTMLと必要に応じて組み込みコンポーネントを組み合わせて記述する。

VF の組み込みコンポーネント

VF には組み込みコンポーネントがあり、これを利用すると Salesforce の UI を楽に利用できる。
当然 VF で提供されている物に限るので、凝ったことをしたい場合は自前での開発が必要になる。

以下の例だと、 table タグによるレイアウト構築と、 Account オブジェクトの Name フィールドのラベルと値の入出力のための input タグが出力される。

<apex:page standardController="Account">
  <apex:pageBlock>
    <apex:pageBlockSection>
      <apex:pageBlockSectionItem>
        <apex:outputLabel value="{! Name }" />
        <apex:inputField value="{! Name }" />
      </apex:pageBlockSectionItem>
    </apex:pageBlockSection>
  </apex:pageBlock>
</apex:page>

どのコンポーネントがどんな HTML を吐き出すのかはドキュメントには書いてないため、実際に出力させてみる必要がある。

レコード詳細ページのアクションボタンから、カスタムコントローラを使う VF ページに移りたい

遷移先の VF でカスタムコントローラは使えないけど、スタンダードコントローラを拡張してそれを使わせる事は出来る。

  • Visualforce の apex:page で対象のオブジェクトの standardController を使用する。
  • standardController で指定したコントローラを拡張する自前の Apex Class を extension 属性に指定する。

こんな感じになる。

<apex:page standardController="Account" extensions="MyAccountExtension">
  ...
</apex:page>

standard controller の拡張については以下を参照。
コントローラ拡張の作成 | Visualforce 開発者ガイド | Salesforce Developers

VF から Apex Controller のメソッドを実行した際に別の VF 画面に遷移したい

メソッドから PageReferense を返せばいい。

class SomeController {
  // 'SomeVfPage' という名前の VF ページに遷移する
  public PageReference gotoNext() {
    return Page.SomeVfPage;
  }
}

VF 間での値の引き継ぎ

それぞれの VF で同一のコントローラを利用すれば、特に何もしなくてもメンバ変数の値を引き継ぐことができる。
そうでない場合は apex:form でパラメータを渡し、受け取り側コントローラで次のようなコードでパラメータを取り出せる。

<apex:page controller="FirstPageController">
  <apex:form>
    <input type="text" name="foo" value="bar" />
    <apex:commandButton value="送信" action="{! goToNextPage }" />
  </apex:form>
</apex:page>
// 遷移先 VF のコントローラ
public with sharing class NextPageController {
  public NextPageController() {
    String fooValue = (String) ApexPages.currentPage().getParameters().get('foo'); // => "bar"
  }
}

(参考: apex - How to display the POST requested parameter in Visualforce page during load? - Salesforce Stack Exchange)

VF ページで PDF をプレビューしたい

<apex:iframe> が使える。多分普通に iframe でも問題ない。
(参考: Preview PDF file on visualforce page - Salesforce Stack Exchange)

VF と開発 FW との組み合わせ

Vue.js の利用を想定。

VF は HTML ではない

VF には VF で許容されたマークアップしかリリースできない。

例えば Vue.js の以下の構文を VF に書き込むと構文エラーとして扱われてしまう。

  • vue-on:click="myFunc" (または @click="myFunc")
  • vue-bind:foo="bar" (または :foo="bar")

Vue.js を使う場合は root の要素だけ空で作って、そこへコンポーネントをマウントする形になる。

ビルドファイル、バンドルファイルの利用方法

静的リソースにアップロードする。
複数ある場合は ZIP としてアップするといい感じ。開発資材やビルド元ファイルは含めず、出力されたファイルのみ含める。

わざわざ ZIP 形式に固める必要もなく、メタデータファイルと、メタデータに対応する名前のディレクトリを作って、そこに出力ファイルを放り込んでおけば SFDX がよしなにしてくれる。
開発ディレクトリは node_modules ディレクトリ等があるので SF にはアップしない方がよさそう。そのため開発資材は別ディレクトリで独自で管理する事になる。

以下は VF から参照する際のイメージ。

ディレクトリ構成:

~/
  └ force-app/
      └ main/
          └ default/
              └ staticresources/
                 ├ MyApp.resource-meta.xml
                 └ MyApp/
                     ├ app.css
                     └ app.js

適当な VF ページ:

<apex:page>
  <div id="app"></div>
  <apex:stylesheet value="{! URLFOR($Resource.MyApp, 'app.css') }" />
  <apex:includeScript value="{! URLFOR($Resource.MyApp, 'app.js') }" loadOnReady="true"/>
</apex:page>

MyApp メタデータファイル:

<?xml version="1.0" encoding="UTF-8"?>
<StaticResource xmlns="http://soap.sforce.com/2006/04/metadata">
    <cacheControl>Public</cacheControl>
    <contentType>application/zip</contentType>
    <description>ビルドしたファイルです</description>
</StaticResource>

開発資材の管理、共有はどうするのがベストなのかまだ分からない。
「スクラッチ組織」を理解すれば解決するのかもしれない。

VF 画面上での Vue.js アプリケーションのデバッグ

SF の lightning から呼び出された VF 画面で VueDevtools は動かない。 (classic から呼ばれた場合は試してない)
Vue.js での開発に十分慣れていないと大変かも。
そういうのもあるので、大規模な開発は避けた方がよさそうだと感じた。

ルーティング

  • JS でルーティングの管理は試してないけど URL に # が入っていたりするので SF 側で何か管理してそう。
  • なので、必要に迫られない限りルーティングは VF, Apex に任せるのが無難と考えてる。

JavaScript から Apex メソッドの呼び出し

JavaScript Remoting という仕組みがあって、Controller に @RemoteAction でメソッドを定義する事で API を生やせる。
Web API のように URL 指定で利用するのではなく、 global 変数に専用の呼び出し機構が定義されているのでそれを使っていく。

実装の際のイメージはこちらを見ると分かりやすい。
JavaScript Remoting の例 | Visualforce 開発者ガイド | Salesforce Developers

ただし、上記ページにある以下の方法は VF ページに直接 JS を書いていく場合にしか動作しない。

Visualforce.remoting.Manager.invokeAction(
  '{! $RemoteAction.MyController.MyMethod }',
  'myArg',
  function(result, event) {/* ... */},
  {escape: true}
)

ビルドを伴う開発スタイルの場合は次のように呼び出す。

window.MyController.MyMethod(
  'myArg', 
  function(result, event) {/* ... */},
  {escape: true}
)

戻り値はコールバック関数で受け取る事になるので、 Promise で返すラッパーを作るといい感じ。

JS 側の詳細な挙動や引数、戻り値については公式の情報が微妙に少ないので、呼び出し時に実行されるコードや実際のレスポンスを見るしかない。
JavaScript Remoting にはガバナ制限が無いので気楽に使えるけど、レスポンスサイズやタイムアウトに制限がある。(参考: JavaScript Remoting の制限および考慮事項 | Visualforce 開発者ガイド | Salesforce Developers

その他

Attachment オブジェクトに保存したファイルを URL で参照したい

/servlet/servlet.FileDownload?file=ATTACHMENT_ID

(参考: [salesforce]メモ&添付ファイルに添付されたファイルのURLを取得する - KayaMemo)

これも参考になりそう。
カスタマーポータルサイトとForce.comサイトから 添付ファイル(Attachment)を参照したい - Salesforce Developer Community

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む