Error: [$compile:multidir] Multiple directives
表題の通り実行時にエラーが出たので原因と解決方法のメモです。
Webpack + AngularJS + TypeScript構成です。
最終的にエラーになる理由はディレクティブを複数定義したからです。 Webpackが生成するbundle.jsが同じのを吐いているんですよね。
// 呼び出し元 import './app/app'; import './header/header';
// 変換後 __webpack_require__(8); __webpack_require__(9); /***/ }, /* 8 */ /***/ function(module, exports) { // 省略 angular.module('app') .component('myApp', new MyAppComponent()); /***/ }, /* 9 */ /***/ function(module, exports) { // 省略 angular.module('app') .component('myApp', new MyAppComponent()); /***/ },
Step 1
いろいろためしてみたところtsconfig.jsonをリネームして読まないようにして実行したところすんなり通る。。
ちなみにこんなんです。
{ "compilerOptions": { "module": "commonjs", "target": "es5", "noImplicitAny": false, "outDir": ".", "out": "dummy.js", "rootDir": ".", "sourceMap": false }, "files": [ "./src/ts/app.ts" ] }
Step 2
1つずつ消していったところ
"out": "dummy.js",
を消した時にエラーが消えました。
いやーよくわからない(自分がわかってないだけ)ですね。
tsconfingがTypeScriptコンパイラのオプションてのはわかるんですがそこにts-loader経由でWebpackに入れるとなんでこういう動きになるのか。。。
もともとWebpackを使わずにコンパイルしてたのでoutとかが残っちゃってたのですが一旦tsconfigを消しても不都合はなさそうなのでその作戦でいきます。
webpackとts-loaderとが何をしているかちゃんとみないとあかんですね、黒魔術すぎて詰まるとさっぱりわからない。。。
Angular JS require
Angularをrequireしてやったぜと意気揚々としていたのですが
angularという変数がグローバルに作られててなんでや、、、と思ってソースを追っていたら
angularが結局グローバルに書き込んでいるみたいですね。。
https://github.com/angular/angular.js/blob/0b1b9112a341f7f798db915575fad63e0e59894e/src/Angular.js#L163
angular = window.angular || (window.angular = {}),
つまりエントリーポイントの頭でrequireしてやればその他のところでは普通にangular.XXXが使えるということ。。
var myAngular = require('angular'); // つまりrequire('angular');だけでいい console.log(myAngular); // Object {version: Object, callbacks: Object} console.log(angular); // Object {version: Object, callbacks: Object}
webpackメモ
全然わかっていないのですが、webpackというのがフロントエンド開発でよさげらしいので試してみたメモ。
基本的な使い方は公式のチュートリアルがわかりやすいのでそちらを参照。
SourceMap
そのまま1つのjsにまとめられたらデバッグができなくて死が見えているのでSourceMapを出力する。
webpack実行時にオプションを追加して実行
実行時に--devtool source-map
を追加するだけ。
webpack --devtool source-map source.js dest.js
Developer toolsのsourceタブにwebpack://
というのができています。
ここからブレークポイントを入れることが可能です。簡単ですね。
configファイルを使う
コマンド打つのもだるいのでconfigファイルを作ります。
リファレンスは以下のページ
http://webpack.github.io/docs/configuration.html
configファイルを作る
touch webpack.config.js
//webpack.config.js module.exports = { entry: './source.js', output: { filename: 'dest.js' }, devtool: "#source-map", };
実行
webpack
wetpack.config.js
というのがデフォルトの設定ファイル名です。
開発用とか切り替えたい場合は別ファイル名で作って--config
で指定可能です。
webpack --config webpack.config-dev.js
gulpからwebpackを呼ぶ
gulp-webpackというのを使います。
npm install -D gulp-webpack
touch gulpfile.js
//gulpfile.js var gulp = require('gulp'); var webpack = require('gulp-webpack'); gulp.task('webpack', webpackFunc); function webpackFunc() { gulp.src('source.js') .pipe(webpack(require('./webpack.config.js'))) .pipe(gulp.dest('./')); }
gulpのsrcに元ファイルを組み込んでいるのでwebpack.config.jsのentryは意味が無い扱いになっているんでしょうかね。ちょっと不透明な部分ですね。
実行
gulp webpack
gulp 初期構築メモ
gulpでsassを使うまで
前提条件
nodeJSが使えること。
gulp
gulpのインストール
npm i gulp -g # iはinstallのエイリアス
package.jsonを作る
npm init
sass
sassのファイルを作る
mkdir sass && touch sass/style.scss
/*style.scss*/ h1 { color: red; &:hover { font-size: large; } }
sassのライブラリをインストール
npm i -D gulp-sass # -Dは--save-devのエイリアス
gulpfile.jsを作る
srcに対象ファイルでpipeで処理を繋げていきます。
touch gulpfile.js
// gulpfile.js 'use strict'; var gulp = require('gulp'); var sass = require('gulp-sass'); gulp.task('sass', sassFunc); function sassFunc() { gulp.src('sass/**/*.scss') .pipe(sass()) .pipe(gulp.dest('./css')); }
実行
sassというタスク名で登録してあるので以下のように実行
gulp sass
css/style.cssが作成されます
/* style.css */ h1 { color: red; } h1:hover { font-size: large; }
自動化
タスクの追加
このままだと毎回手打ちとかめんどくさいのでファイルの監視を入れます
watchに監視対象ファイル、タスク名を渡すだけですね。これは簡単。
// gulpfile.js gulp.task('default', defaultFunc); function defaultFunc() { gulp.watch(['sass/**/*.scss'],['sass']); }
実行
defaultタスクに入れたのでそのまま実行
gulp
あとは自動検知して勝手にsassタスクをおこなってくれます。
IntellijIDEAでTypeScriptのコード補完
TypeScriptの設定
Enable TypeScript Compiller
をON
AngularJSのライブラリをダウンロードしてみる
この状態でコード補完が微妙に効く
several definition
ってのは
複数定義されているから出せない?ってことなのですが、よくわからない。
ちなみにこのtsを自動トランスパイルしたjsの場合は以下のようにちゃんと引数まで出てきます。
$scopeを解釈していないため$waではじまるものを適当に引っ張っているみたいです。
なので後述する型定義ファイルの追加
と$scopeの型指定
をすればちゃんとコード補完されます。
この状態だとトランスパイル時にエラー
つまりこのライブラリの追加というのはコード補完時の参照であってトランスパイル時の参照ではないということですかね。
Error:(4, 1) TS2304: Cannot find name 'angular'.
型定義ファイルの追加
これでトランスパイル時に参照してくれるはず!
tsdというツールを使用
tsd init
で初期化
tsd query angular -rosa install
で定義ファイルをDL
定義を追加
/// <reference path="./typings/tsd.d.ts" />
エラーが消えました!
コード補完もちゃんと効きます。
まとめ
- JavaScriptのライブラリ追加はコード補完用
- 定義ファイルの追加をすればトランスパイル時のエラーは消える
- 定義ファイルの追加をすればJavaScriptのライブラリ追加をしなくてもコード補完される模様(同階層のjsだったり定義ファイルの追加でIDEAが自動で解釈してくれるみたい
IDEAは賢すぎて逆にわかりづらい。
TypeScriptはイイ。
IDEA プラグインの追加(AngularJS)
WebStormではデフォルトであったのですがIDEAにはなかったのでIDEAにAngularJS用のプロジェクトを作るためのメモ
他のプロジェクトも同様でしょうきっと。
メニューのItetelliJ IDEAから
AngularJS プロミス $q
jQueryでいう$.Deferred
ですかね。
https://code.angularjs.org/1.4.8/docs/api/ng/service/$q
$q.defer()でdeferredオブジェクトを取得してdeferred.promiseを返すようにする。
deferredオブジェクトにはpromiseと3つのメソッドが存在し必要に応じて使用する。
promise: Promise resolve: (value) // 成功用 reject: (value) // 失敗用 notify: (value) // 通知用
promiseには以下の3つのメソッドが存在する。
通常はthenを使用し失敗時だけハンドリングしたい場合はcatch、結果問わず後処理をしたい場合はfinallyを使用。
then(successCallback, errorCallback, notifyCallback) catch(errorCallback) finally(callback, notifyCallback)
$q.all(promises);
ですべてのプロミスが解決したあとに解決するプロミスを生成する