1日1%成長するブログ

毎日成長するために仕事/プライベートで得た学びをアウトプットするブログです

Webアプリの新規作成の実装パターン

一覧 -> 作成画面に遷移して新規作成

フォームでPOSTして、エラーならエラーメッセージを表示、成功したら一覧画面にリダイレクトさせるというオーソドックスなパターン。

Flashメッセージの実装は必要。作成後にセッションに保存して一覧画面で取得したらすぐに消すような仕組み。大体フレームワークに用意されてることが多い。

バリデーションをどうするかも重要。 サーバーサイドでバリデーション結果をテンプレートに埋め込んで表示するタイプか、JSでバリデーションをリアルタイムに実装するか。

Vueでリアルタイムバリデーションして、システム側のエラーは例外。ユーザー起因のエラーはFlashメッセージがいいかな。

一覧からモーダルを開いて新規作成

非同期処理が完了したらJSで動的に一部の画面を切り替える

このパターンはユーザーに画面遷移をさせないで済み、変更がすぐに反映されるので一番理想。 実装方法としては以下の2つになると思う。

ただこの方法で厄介なのは、JS側でテンプレートを描写することになるため サーバーサイドのテンプレートエンジンが使えないこと。

最たる例がi18n対応。サーバーサイドで翻訳した結果を出力する形になっている場合、 JS側でもi18n用の機能を実装する必要が出てくる。

また非同期処理が完了するまで画面に何も表示されないというのも難点。 これを解決するにはローディングを出したり、取得中はDOM自体をださないとか、 gon等のライブラリを使ってDOMにJSの値を吐き出しておく等の方法がある。

またこの方法の場合、前述のFlashメッセージではなくトースターを表示する実装が必要になる。

ということからもわかるように、この方法はJSフレームワークのコード資産があまり無い段階では 実装コストが高めなのでスピードを優先する時はオススメできない。

非同期処理が完了したら画面をリロードさせる

このパターンは非同期処理が完了したら、location.href で画面をリロードさせることで変更を反映させる。 データ数が多くなければ、そこまで違和感が無い。テンプレートエンジンも流用できる。

ただ作成した時のトースターを表示したい時は一工夫が必要。 非同期処理が成功したという情報をどこかに持っておかないといけない。

自分はグローバル変数は汚したくないので、ローカルストレージを使って実装することが多い。 ローカルストレージの実装は必要になるが、JSで動的に切り替えるよりは実装コストは低いのでオススメ。

JavaScriptのオブジェクトとJSONは別物

JavaScriptのオブジェクトとJSONは見た目がほとんど変わらないので、 違いがよくわかっていなかったのですが、明確に違いがあるようなのでメモしておきます。

JavaScriptのオブジェクト

キーがクォーテーション無し & 文字列をダブルクォーテーションで囲む

{ 
  id: 0,
  title: "title",
  description:  "desc"
}

キーがクォーテーション無し & 文字列をシングルクォーテーションで囲む

{ 
  id: 0,
  title: 'title',
  description:  'desc'
}

キーをクォーテーションで囲むことも出来るけど、囲まない方が推奨。

JSON

キーをダブルクオーテーションで囲み、文字列をダブルクォーテーションで囲むのがJSONJavaScriptのオブジェクトでもある。

{
  "id": 0,
  "title": "title",
  "description":  "desc"
}

JSONのフォーマットとして正しいかどうかは以下のツールを使うと、簡単に判定できるので一度使ってみてください。

lab.syncer.jp

JSON.stringify()とJSON.parse()

JSON.stringifyJavaScriptのオブジェクトをJSON文字列に変換する関数。 JSON.parseJSON文字列をJavaScriptオブジェクトに変換する関数。

出力を見ると、JSON文字列はキーがどちらもダブルクォーテーションで囲まれていることがわかると思います。

var jsVar = {
  id: 1,
  title: "title"
};

var jsonStr = JSON.stringify(jsVar);

var jsParsed = JSON.parse(jsonStr);

console.log(jsVar); // {id: 1, title: "title"}
console.log(jsonStr); // {"id":1,"title":"title"}
console.log(jsParsed); // {id: 1, title: "title"}

WebpackでReactを本番反映する時に行う設定

webpackの各種プラグインを導入

DefinePluginでprocess.env.NODE_ENVを置換する

特定の文字を任意の文字に置き換えることのできるプラグインです。

たとえば、

//webpak.config.js
plugins: [
      new webpack.DefinePlugin({
        'process.env':{
          'NODE_ENV': JSON.stringify('production')
        }
      }),
],

という定義があった時に、Reactのコード内の

if process.env.NODE_ENV !== ‘production’ { 
  //開発環境用のコード
}

if ‘production’ !== ‘production’ {
  //開発環境用のコード
}

に置き換えられる。

この設定単体だとダメで、後述のUglifyJSPluginと組み合わせることで、 未到達のコードは削除されてファイル容量を削減することができます。

UglifyJsPluginでJSをminifyする

minifyとは、コメントや改行,スペースを削除したり、変数名を短くして無駄な部分を取り除くことです。

//webpak.config.js
plugins: [
      new webpack.DefinePlugin({
        'process.env':{
          'NODE_ENV': JSON.stringify('production')
        }
      }),
      new webpack.optimize.UglifyJsPlugin()

とすると、

  WARNING in xx.js from UglifyJs
    Condition always false [./~/react/lib/React.js:29,0]
    Dropping unreachable code [./~/react/lib/React.js:29,0]
    Declarations in unreachable code! [./~/react/lib/React.js:30,0]
    Condition always false [./~/react/lib/React.js:44,0]

のような警告文が大量にでることがあるので、

//webpak.config.js
plugins: [
      new webpack.DefinePlugin({
        'process.env':{
          'NODE_ENV': JSON.stringify('production')
        }
      }),
      new webpack.optimize.UglifyJsPlugin({
        compress: {
          warnings: false
        },
        comments: require("uglify-save-license")
      })

にして、警告文を非表示にします。 またuglify-save-license を使うことでライセンスのコメントは残す設定も入れておきましょう。

その他

あとは環境に応じて、APIのリクエスト先を変えるような場合 以下のようにして置き換えられるようにしておくと良いと思います。

//webpak.config.js
plugins: [
      new webpack.DefinePlugin({
        'process.env':{
          'NODE_ENV': JSON.stringify('production'),
     'API_URL': 'https://api.xxx.com'
        }
      }),
      new webpack.optimize.UglifyJsPlugin({
        compress: {
          warnings: false
        },
        comments: require("uglify-save-license")
      })

ReduxのProviderコンポーネントとContainerコンポーネントを理解する

Providerコンポーネント

const App = () => {
  return (
    <Provider store={createStore(reducers)}>
      <View>
         <Header>
         <PostList>
         <Footer> 
      </View>
    </Provider>
  )
}

Containerコンポーネント

  • connect関数でReactコンポーネントをラッピングしたもの
  • Connected Componentとも呼ぶ
  • Reactでは「Stateを親コンポーネントに持たせる」と表現するが、それをReduxでは名前をつけただけ
  • ただし、ReduxではStateはStoreに持つので、connectしてStateをPropsとして受け取る形になる
class PostList extends React.Component {
  render() {
    //ここにstateの中身がくる
    console.log(this.props);
  }
}

const mapStateToProps = state => {
  return { posts: state.posts };
}

//PostListをconnectでラッピングしたここがContainerコンポーネント
export default connect(mapStateToProps)(PostList);

このようにすることで、stateの中のpostsのみをpropsで受け取ることができる

CSSで記号を実装する

今回は文字の前後に記号を追加したい。 CSSの疑似セレクタ(:before, :after)を使えば要素の前後に値を表示できます。

値を指定するには疑似セレクタのcontentプロパティを使う。 IE7以下は未対応らしいですが、今回は無視しています。

contentプロパティで表示できる項目

  • テキスト
  • 属性名
  • 画像

上記の3つを扱うことができる。 記号を表示するだけであれば、テキストを使う。 その他の使い方に関しては以下の記事がわかりやすい。

tenderfeel.xsrv.jp

テキストの表現方法

機種依存文字Shift_JIS等で文字化けする時があるので、実体参照を使う方が無難 (最近はUTF-8が主流なのであまり気にしなくなってきているらしい)

実体参照について

人にわかりやすい文字で表現されているので使い易いが、 文字実体参照が無い記号も多い(例: ✔) 。この場合はUnicodeの10進数/16進数で表現する数値文字参照を使うしかない。

数値文字参照の調べ方

使いたい記号の数値文字参照を調べるツールも以下の記事に記載されているので、 困ったら見てみると便利だと思います。

tenderfeel.xsrv.jp

サンプルコード

.check-mark:after
  content: "\002714"
  color: red

こんな感じで✔を表示することが出来ます。

Objective-CのAppDelegate.mって何をしてるの?

  • アプリを作った段階でデフォルトで作られるファイルの一つ。アプリ全体のライフタイムイベントを管理するためのクラス
application: didFinishLaunchingWithOptions
  • アプリが初めて起動した時に呼び出される
application:applicationWillResignActive
  • アプリが非Activeになる直前に呼び出される
application:applicationDidEnterBackground
  • アプリが非Activeになりバックグランド実行になった際に呼び出される
application:applicationWillEnterForeground
  • 2回目以降の起動時に呼び出される(Backgroundにアプリがある場合)
application:applicationDidBecomeActive
  • アプリがActiveになった際に呼び出される
application:applicationWillTerminate
  • システムからのアプリ終了の際に呼び出される

IOSアプリの各状態については以下の記事が詳しい。

iOSアプリの状態遷移とライフサイクル - Qiita

Objective-Cの基礎的文法の備忘録

変数

  • 変数の型 変数名:
int num = 100;
int num;
num = 100;
  • データの種類
    • int: 整数
    • float: 小数点つきの数値
    • BOOL: YES/NOに使う
    • 変数には数値しか入らない

ポインター変数

  • ポインター変数とは「メモリ上のオブジェクトを指し示すアドレス」の数値が入っている
  • 作る時は「*」をつけて作り、使う時は「*」を外して使う

文字データを扱う

  • NSStringを使う
  • @“"で囲む
NSString *str = @"Hello World";

NSLogで出力する

  • 文字列は%@で囲み、数値は%dで囲む
NSLog(@"%@%d%@", @"残りは", 3, @"回です");

クラス定義

  • ヘッダ部と実装部で分ける
/* クラスの宣言部 */
@interface MyClass : NSObject {
}
- (void)myMethod;
@end

/* クラスの実装部 */
@implementation MyClass
- (void)myMethod {
    // メソッドの処理を記述
    printf("Hello World.\n");
}
@end

メソッド定義

-(返す型)メソッド名 {
   メソッドの中身
}

が基本。「-」はインスタンスメソッド。「+」はクラスメソッド

@implementation MyClass
- (void)myMethod {
    printf("Hello World.\n");
}
+ (void)myMethod2
{
    printf("Hello World2.\n");
}
@end

メソッド実行

id test = [[MyClass alloc] init];
//インスタンスメソッド実行
[test myMethod];
//クラスメソッド実行
[MyClass myMethod2];
  • それぞれ[インスタンス名 メソッド名]、[クラス名 メソッド名]で実行できる

allocとinitとnew

  • alloc

  • init

    • メモリ上にロードされたインスタンスに対して実行する。 → インスタンスがデータを保存出来る状態にする。コンストラクタを呼ぶ
  • new

    • alloc + init
//以下は同じ意味
id test = [[MyClass alloc] init];
id test = [MyClass new];

id型

  • 汎用オブジェクト型
  • 最初に型を決めずに変数を用意し、実際に使うタイミングで型を決定する
 id test; //型は決まっていない
 test = [MyClass new]; //この時点でMyClass型になる