AngularJS v1.5.xの始め方
はじめに
作業メモ。あとで清書予定。
必要なツールのインストール
以下のツールをインストールする
$ npm install -g yo grunt-cli gulp bower typings karma $ npm install -g generator-angular $ gem install compass
プロジェクト作成
$ mkdir ng001 $ cd ng001 $ yo angular ng001
_-----_ | | .--------------------------. |--(o)--| | Welcome to Yeoman, | `---------´ | ladies and gentlemen! | ( _´U`_ ) '--------------------------' /___A___\ | ~ | __'.___.'__ ´ ` |° ´ Y ` Out of the box I include Bootstrap and some AngularJS recommended modules. ? Would you like to use Gulp (experimental) instead of Grunt? No ? Would you like to use Sass (with Compass)? Yes ? Would you like to include Bootstrap? Yes ? Would you like to use the Sass version of Bootstrap? Yes ? Which modules would you like to include? angular-animate.js, angular-cookies.js, angular-resource.js, angular-route.js, angular-sanitize.js, angular-touch.js create app/styles/main.scss create app/index.html create bower.json create .bowerrc create package.json create Gruntfile.js create README.md invoke angular:common:/Users/myomi/.anyenv/envs/ndenv/versions/v5.3.0/lib/node_modules/generator-angular/app/index.js create .editorconfig create .gitattributes create .jscsrc create .jshintrc create .yo-rc.json create .gitignore create test/.jshintrc create app/404.html create app/favicon.ico create app/robots.txt create app/views/main.html create app/images/yeoman.png invoke angular:main:/Users/myomi/.anyenv/envs/ndenv/versions/v5.3.0/lib/node_modules/generator-angular/app/index.js create app/scripts/app.js invoke angular:controller:/Users/myomi/.anyenv/envs/ndenv/versions/v5.3.0/lib/node_modules/generator-angular/app/index.js create app/scripts/controllers/main.js create test/spec/controllers/main.js Error angular ng001 You don't seem to have a generator with the name karma:app installed. You can see available generators with npm search yeoman-generator and then install them with npm install [name]. To see the 53 registered generators run yo with the `--help` option.
yo angular --minsafe
ブログ記事では、yo angular 実行時に --minsafe オプションをつける記事がよく見受けられるが、このオプションはすでに削除されている。
参考
依存ライブラリの解決
$ npm install $ bower install
VSCodeでインテリセンスを有効にする
プロジェクトルートにjsconfig.jsonを作成
{ "compilerOptions": { "target": "ES5", "module": "commonjs" } }
$ typings init $ typings install --save --ambient node $ typings install --save --ambient angular $ typings install --save --ambient angular-cookie $ typings install --save --ambient angular-animate
VS Code起動中なら再起動する。
動作確認
$ grunt serve
以下のエラーが出る場合がある。
Fatal error: Port 35729 is already in use by another process.
自分の環境ではVisual Studio Codeが利用しているポートと衝突した。
この場合は、Gruntfile.js のlivereload のポート番号を被らないように変更し、もう一度 grunt serve を実行する
// The actual grunt server settings connect: { options: { port: 9000, // Change this to '0.0.0.0' to access the server from outside. hostname: 'localhost', livereload: 35729 },
nibファイルを使わないテンプレートを作る(2)View-Based Application
(前の記事:nibファイルを使わないテンプレートを作る(1)Window-Based Application)
今回は、iPhone用のView-Based Application をカスタマイズします。
テンプレート View-Based Application をコピーする
/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Project Templates/Application
にある View-Based Application フォルダを
~/Library/Application Support/Developer/Shared/Xcode/Project Templates/Application/
にコピー。
フォルダ名を My View-Based Application に変更します。
main.m を編集する
以下の1行を編集します。
変更前
int retVal = UIApplicationMain(argc, argv, nil, nil);
変更後
int retVal = UIApplicationMain(argc, argv, nil, @"___PROJECTNAMEASIDENTIFIER___AppDelegate");
MainWindow.xib を削除する
削除しちゃいます。
Info.plist を編集する
Main nib file base name を削除します
___PROJECTNAMEASIDENTIFIER___AppDelegate.m を編集する
applicationDidFinishLaunching:メソッドを編集します。
変更前
- (void)applicationDidFinishLaunching:(UIApplication *)application { // Override point for customization after application launch [window addSubview:viewController.view]; [window makeKeyAndVisible]; }
変更後
- (void)applicationDidFinishLaunching:(UIApplication *)application { UIWindow *w = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window = w; self.viewController = [[___PROJECTNAMEASIDENTIFIER___ViewController alloc] initWithNibName:nil bundle:nil]; [window addSubview:viewController.view]; [window makeKeyAndVisible]; [w release]; }
___PROJECTNAMEASIDENTIFIER___ViewController.xib を削除する
削除しちゃいます。
___PROJECTNAMEASIDENTIFIER___ViewController.m を編集する
loadView メソッドを以下のように実装します。
- (void)loadView { UIView *contentView = [[UIView alloc] initWithFrame: [[UIScreen mainScreen] applicationFrame]]; contentView.backgroundColor = [UIColor whiteColor]; self.view = contentView; [contentView release]; }
完成
次はTab Bar Applicationをカスタマイズする予定。
nibファイルを使わないテンプレートを作る(1)Window-Based Application
Interface Builder を使いこなせません。もう使いたくありません。
そこで、Xcode付属のテンプレートをカスタマイズして、nibファイルを使わないテンプレートを作ることにしました。
(参考:d:id:griffin-stewie:20090315:p1)
まずは、iPhone用のWindow-Based Application をカスタマイズします。
テンプレート Window-Based Application をコピーする
/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Project Templates/Application
にある Window-Based Application フォルダを
~/Library/Application Support/Developer/Shared/Xcode/Project Templates/Application/
にコピー。
フォルダ名を My Window-Based Application に変更します。
main.m を編集する
以下の1行を編集します。
変更前
int retVal = UIApplicationMain(argc, argv, nil, nil);
変更後
int retVal = UIApplicationMain(argc, argv, nil, @"___PROJECTNAMEASIDENTIFIER___AppDelegate");
MainWindow.xib を削除する
削除しちゃいます。
Info.plist を編集する
Main nib file base name を削除します
___PROJECTNAMEASIDENTIFIER___AppDelegate.m を編集する
applicationDidFinishLaunching:メソッドを編集します。
変更前
- (void)applicationDidFinishLaunching:(UIApplication *)application { // Override point for customization after application launch [window makeKeyAndVisible]; }
変更後
- (void)applicationDidFinishLaunching:(UIApplication *)application { UIWindow *w = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window = w; [w release]; [window makeKeyAndVisible]; }
完成
この調子でどんどん作るよ。
ant で class ファイルを dump するタスクを作った
package myomi.ant.taskdefs; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.taskdefs.MatchingTask; import org.apache.tools.ant.types.Path; import sun.misc.CharacterEncoder; import sun.misc.HexDumpEncoder; public class Dump extends MatchingTask { private Path classDirs; private File destDir; public Dump() throws ClassNotFoundException, SecurityException, NoSuchMethodException { super(); } public void setClassDir(Path classDir) { if (classDirs == null) { classDirs = classDir; } else { classDirs.append(classDir); } } public void setDestDir(File destDir) { this.destDir = destDir; } public void execute() { log("start dump task"); try { for (String dir: classDirs.list()) { File f = getProject().resolveFile(dir); DirectoryScanner scanner = getDirectoryScanner(f); String[] classes = scanner.getIncludedFiles(); for (String classPath: classes) { javap(dir, classPath); } } } catch (Exception e) { e.printStackTrace(); } log("end dump task"); } private void javap(String classDir, String classPath) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException, ClassNotFoundException, IOException { FileInputStream in = null; FileOutputStream out = null; try { // mkdir File tmp = new File(destDir.getAbsolutePath() + File.separator + classPath); File dir = tmp.getParentFile(); if (!dir.exists()) dir.mkdirs(); // dump in = new FileInputStream(classDir + File.separator + classPath); File file = new File(dir, tmp.getName().replaceAll(".class", ".dump")); out = new FileOutputStream(file); CharacterEncoder encoder = new HexDumpEncoder(); encoder.encodeBuffer(in, out); log("execute dump: " + file.getAbsolutePath()); } finally { try { if (in != null) in.close(); } finally { if (out != null) out.close(); } } } }
使い方
<target name="dump"> <mkdir dir="${dump.dir}"/> <taskdef name="dump" classname="myomi.ant.taskdefs.Dump" classpath="${lib.dir}/myomiTask.jar"/> <dump classdir="${classes.dir}" destdir="${dump.dir}"/> </target>
HexDumpEncoderの存在はここで知った。
http://homepage2.nifty.com/ann/Windows/tools/javap-HexDumpEncoder.htm
「ant で javap するタスクを作った」で作ったタスクと合わせて、これからいろいろと遊んでみます。
ant で javap するタスクを作った
こんなの。
package myomi.ant.taskdefs; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.taskdefs.MatchingTask; import org.apache.tools.ant.types.Path; import sun.tools.javap.Main; public class Javap extends MatchingTask { private Path clazz; private File destDir; public Javap() { super(); } public void setClassDir(Path classDir) { if (clazz == null) { clazz = classDir; } else { clazz.append(classDir); } } public void setDestDir(File destDir) { this.destDir = destDir; } public void execute() { log("start javap task"); try { for (String s: clazz.list()) { File f = getProject().resolveFile(s); DirectoryScanner scanner = getDirectoryScanner(f); String[] files = scanner.getIncludedFiles(); javap(s, files); } } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } log("end javap task"); } private void javap(String classDir, String[] pathes) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException, ClassNotFoundException, IOException { for (String path: pathes) { PrintWriter writer = null; try { // mkdir File tmp = new File(destDir.getAbsolutePath() + File.separator + path); File dir = tmp.getParentFile(); if (!dir.exists()) dir.mkdirs(); // javap Main main = null; File file = new File(dir, tmp.getName().replaceAll(".class", ".javap")); file.createNewFile(); writer = new PrintWriter(file); main = new Main(writer); Class c = Class.forName("sun.tools.javap.Main"); Method m = c.getDeclaredMethod("perform", new Class[]{Class.forName("[Ljava.lang.String;")}); m.setAccessible(true); Object result = m.invoke(main, new Object[]{new String[]{"-verbose", "-classpath", classDir, path.replaceAll(".class", "")}}); log("execute javap: " + file.getAbsolutePath()); } finally { if (writer != null) writer.close(); } } } }
使い方。
(Javap.classを、myomiTask.jarに固めて使ってます)
<target name="javap"> <mkdir dir="javap"/> <taskdef name="javap" classname="myomi.ant.taskdefs.Javap" classpath="myomiTask.jar"/> <javap classdir="classes" destdir="javap"/> </target>
ちょっと息抜き GaucheでXMLから内容を抽出する
XPathが使えますよ(SXPath)。
(use rfc.http) (use sxml.ssax) (use sxml.sxpath) (let-values (((status head body) (http-get "zip.ricollab.jp" "/5340026"))) (let ((xml (call-with-input-string body (cut ssax:xml->sxml <> '((xhtml . "http://www.w3.org/1999/xhtml")))))) ; //xhtml:dd[@class = 'address'] ((sxpath '(// (xhtml:dd (@ (equal? (class "address")))))) xml)))
gosh> ((xhtml:dd (|@| (class "address")) (xhtml:a (|@| (href "/大阪府") (class "prefecture")) "大阪府") (xhtml:a (|@| (href "/大阪府/大阪市都島区") (class "city")) "大\ 阪市都島区") (xhtml:a (|@| (href "/大阪府/大阪市都島区/網島町") (class "town")) "網島町")))
括弧で目が痛い。。。まだまだ研鑽が足りんのか。
ちょっと息抜き GaucheでXMLを読み込む
仕事では、XMLを扱うことが多いのです。『プログラミングGauche』を写経し始めたばかりで、まだまだへっぽこな私ですが、早速、日頃の業務に使ってみようかなぁと思っています。
そこで手習いとして、GaucheでXMLを扱った処理をいくつか書いてみます。最初は読み込みから。手元に手ごろなXMLが無いので、ricollab 郵便番号検索を使って、適当な住所を検索します。
(use rfc.http) (use sxml.ssax) ; 郵便番号534-0026を検索 (let-values (((status head body) (http-get "zip.ricollab.jp" "/5340026"))) (call-with-input-string body (cut ssax:xml->sxml <> ())))
gosh> (*TOP* (http://www.w3.org/1999/xhtml:html (|@| (xml:lang "ja")) (http://www.w3.org/1999/xhtml:head (http://www.w3.org/1999/xhtml:title "〒534-0026")) (http://www\ .w3.org/1999/xhtml:body (http://www.w3.org/1999/xhtml:h1 "〒534-0026") (http://www.w3.org/1999/xhtml:dl (http://www.w3.org/1999/xhtml:dt "番号") (http://www.w3.org/199\ 9/xhtml:dd (|@| (class "zipcode")) "5340026") (http://www.w3.org/1999/xhtml:dt "住所") (http://www.w3.org/1999/xhtml:dd (|@| (class "address")) (http://www.w3.org/1999\ /xhtml:a (|@| (href "/大阪府") (class "prefecture")) "大阪府") (http://www.w3.org/1999/xhtml:a (|@| (href "/大阪府/大阪市都島区") (class "city")) "大阪市都島区") (http\ ://www.w3.org/1999/xhtml:a (|@| (href "/大阪府/大阪市都島区/網島町") (class "town")) "網島町")) (http://www.w3.org/1999/xhtml:dt "フリガナ") (http://www.w3.org/1999/xh\ tml:dd (|@| (class "yomi")) (http://www.w3.org/1999/xhtml:a (|@| (class "prefecture")) "オオサカフ") (http://www.w3.org/1999/xhtml:a (|@| (class "city")) "オオサカシミ\ ヤコジマク") (http://www.w3.org/1999/xhtml:a (|@| (class "town")) "アミジマチョウ"))))))
検索結果が取り出せました。続いてはXMLの加工です。
解決した
手続き ssax:xml->sxml の引数で指定すればよい
(let-values (((status head body) (http-get "zip.ricollab.jp" "/5340026"))) (call-with-input-string body (cut ssax:xml->sxml <> '((xhtml . "http://www.w3.org/1999/xhtml")))))
gosh> (*TOP* (|@@| (*NAMESPACES* (xhtml "http://www.w3.org/1999/xhtml"))) (xhtml:html (|@| (xml:lang "ja")) (xhtml:head (xhtml:title "〒534-0026")) (xhtml:body (xhtml:\ h1 "〒534-0026") (xhtml:dl (xhtml:dt "番号") (xhtml:dd (|@| (class "zipcode")) "5340026") (xhtml:dt "住所") (xhtml:dd (|@| (class "address")) (xhtml:a (|@| (href "/大\\ 阪府") (class "prefecture")) "大阪府") (xhtml:a (|@| (href "/大阪府/大阪市都島区") (class "city")) "大阪市都島区") (xhtml:a (|@| (href "/大阪府/大阪市都島区/網島町") (\ class "town")) "網島町")) (xhtml:dt "フリガナ") (xhtml:dd (|@| (class "yomi")) (xhtml:a (|@| (class "prefecture")) "オオサカフ") (xhtml:a (|@| (class "city")) "オオサ\\ カシミヤコジマク") (xhtml:a (|@| (class "town")) "アミジマチョウ"))))))