自作アプリ「BookSelf」に持ってるものリストの表示画面を追加
自作アプリ「BookSelf」に持ってるものリストの表示画面を追加しました。
追加にあたり紆余曲折あり、同時にいろいろ改修、修正を実施してしまいかなり時間を要しました。(開始から1ヵ月ぐらい) 実施した内容は以下となります。
- angularのバージョンアップ 2.31から5.2.10へ
- angular-cliのバージョンアップ 1.0.0から1.7.4へ
- bootstrapのバージョンアップ 3.3から4.1へ
- package.jsonの整備
- ナビゲーションバーの作成(bootstrapとng-bootstrapを使用しての作成)
- Paginationのng-bootstrap化
- 持っているものリスト表示の追加
- 画面デザインの一部変更
とりかかってしまった起因は bootstrapのサンプルをマネしてヘッダーの作成→bootstrapのドロップダウンが動かない→ng-bootstrapが必要なことを理解(ここまではあまりこれの意味を理解してなかった)→ng-bootstrapの最新バージョンを確認するとAngularのバージョン古すぎと気づく→バージョンアップ作業に挑む→package.jsonの意味を理解、汚くねとなる→バージョンアップした結果画面崩れたので修正→やっとリスト画面作成
すでに記憶がやばいが覚えてる範囲を記録します。
angularのバージョンアップ 2.31から5.2.10へ
angular-cliのバージョンアップ 1.0.0から1.7.4へ
package.jsonの整備
npm update @angular/cli rm -r node_modules npm cache clean
packag.jsonのdependenciesとdevDependenciesを削除。 npm installコマンドで一からインストールし、package.jsonを整理。
package.jsonのdependenciesに書き込んでインストール npm install --save ○○
devDependenciesに書き込んでインストール(開発時に必要なライブラリ) npm install --save-dev ○○
コンパイルが通らなかったソースの修正
型指定がおかしいからエラーになっていたけど、もともとのほうが怪しい作りになっていたのでチェックが厳しくはなっているってことかなと思います。 anyがあまり好きではないので少し微妙。。。 変更ポイントわかるようにGitの変更差分をコピー(逆にわかりにくい?)
@@ -13,11 +13,13 @@ export class BookapiService { } //対象がない場合は空の配列と0を返却 - public search(searchWord: string, page: number = 1): Observable<[BookItem[], number]> { + public search(searchWord: string, page: number = 1): Observable<any> { var bookApiResult; if(searchWord == '') { - return Observable.of([[], 0]); + let totalItems: number = 0; + let bookItems: BookItem[] = []; + return Observable.of({bookItems, totalItems}); } let paramsMap = this.createParamMap(searchWord, page); @@ -43,7 +45,7 @@ export class BookapiService { let totalItems: number = bookApiResult.totalItems; let bookItems: BookItem[] = this.createBookItems(bookApiResult, bookPossesions); - return [bookItems, totalItems]; + return {bookItems, totalItems}; }); }
ナビゲーションバーの作成
持っているものリスト表示の追加
リスト表示画面についてはメイン画面にリスト等に遷移するためのナビゲーションバーを追加しました。
「List」をクリックでリスト画面に遷移します。
メイン画面(ヘッダーが追加)
リスト画面(持っている本の一覧を表示)
ソースはGithubにコミット済みです。(ずっと前に)
いろいろ他のことやってたりして下書きのままでずっと止まってたので、いったんここでブログ更新します。。。
こまめなコミット、ブログ記録を改めて心がけよう。
実力不足を痛感したのでCodilityでLessonをやってみた
更新がまたおろそかになっていたので最近やっていたことを記載。
仕事ではコーディングすることがあまりないのもあって、最近実力不足を痛感したことがありました。 そこでCodilityってサイトのLessonがよさげだったので何問か実施してみました。 英語だけど翻訳ツールを使えばわりと素直に訳せてとっつきやすいし、わりと何となく慣れます。
実施した問題の解答はGithubにアップしています。(回答言語はJavaでテスト100%を満たせたもの) パッケージ名が問題名とリンクしています。
このCodilityですが、最近結果確認画面でロード中状態が続いてしまう現象が発生するようになり、調べてみると以下のようなエラーが出ているようでした。 裏では結果判定しているっぽいので、再度Lessonのページに戻ると結果のパーセンテージだけは分かるのですが。。。使い勝手が微妙な状態です。 ※ChromeとEdgeで確認
コーディングのトレーニングは続けたいので、 Codilityは一旦お休みして、他の問題サイトをやっていこうかなと思ってます。
自作アプリ「BookSelf」のWebAPI部分の解説
前々述した自作アプリのWebAPI部分の解説になります。
JAX-RSを使用しJSONのサービスを作成しています。
内部ではJPAを使用してDBの読書きも行っております。
記述を減らしてWebサービスを記載できそうなのとわりと新しい技術かなと思ったからです。(仕事ではレガシーシステム担当だから新しく感じるのかも)
今回は記録のため、環境準備の手順とポイントを記載します。
環境準備
まずはEclipseを使っての準備手順を記載していきます。
Jerseyを使ってJAX-RSを使用します。
新規Mavenプロジェクト⇒ワークスペースはお任せ(デフォルトでOK)⇒groupId=org.glassfish.jersey.archetypes、ArtifactId=jersey-quickstart-webappを探して選択
⇒GroupId, ArtifactId, Version, Packageを任意で設定(ArtifactId=がプロジェクト名となります)
これでJAX-RSで動くサンプルが作成されます。
※参考
EclipseでJersey(JAX-RS)を始める - Qiita
JSONを使用するために
JSONとクラスをマッピングして扱えるようにします。
@Consumes("application/json")@Produces("application/json")だけでも大丈夫そうな記載がされているサイトもあったがこれがないと私の環境ではダメでした。
pom.xmlに以下を追加
<dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-moxy</artifactId> <version>2.26-b09</version> </dependency>
JSONのレスポンスを実装するクラス
@Path("myresource") public class MyResource { @POST @Path("アクセスするパス") @Consumes("application/json") @Produces("application/json") public JSONで返すクラス function(JSONからクラスに変化されたクラス) { } }
※参考
JAX-RSとかの話 — 裏紙
Jersey 2.x系でのJSONサービスに関して - クロノスの技術系ブログ
Jerseyの設定1(web.xmlとかApplicationクラスとか) - edgegram
JPAでDBへアクセス
DBにはMySQL、JPAにはEclipseLinkを使用しました。
pom.xmlに以下を追加
<dependency> <groupId>org.eclipse.persistence</groupId> <artifactId>eclipselink</artifactId> <version>2.5.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.31</version> </dependency>
src/main/resources/META-INF/にpersistence.xmlを作成します。
url、user、passwordは任意の値を
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"> <persistence-unit name="test" transaction-type="RESOURCE_LOCAL"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <exclude-unlisted-classes>false</exclude-unlisted-classes> <properties> <property name="javax.persistence.jdbc.url" value="jdbc:mysql://0.0.0.0/test"/> <property name="javax.persistence.jdbc.user" value="test"/> <property name="javax.persistence.jdbc.password" value="pass"/> <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/> </properties> </persistence-unit> </persistence>
JPAの使い方例(JPQLを使用)
EntityManagerFactory fac = Persistence .createEntityManagerFactory("test");// DB test EntityManager em = fac.createEntityManager(); List<BookStorage> bookStorageList = em .createQuery("SELECT c FROM BookStorage c WHERE c.id = :id", BookStorage.class).setParameter("id", bookInfo.getId()) .getResultList();
※参考
JAX-RS(Jersey)とJPAで簡単なサンプル - Qiita
JavaEE使い方メモ(JPA その3 - JPQL) - Qiita
CORS対応
私の実施している環境だとAngularとのクロスドメインでのアクセス許可を実施しないと通信ができなかったためフィルター機能で対応しています。
@Provider public class AddHeaderCRFilter implements ContainerResponseFilter { @Override public void filter(ContainerRequestContext arg0, ContainerResponseContext arg1) throws IOException { arg1.getHeaders().add("Access-Control-Allow-Origin", "*"); arg1.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization"); arg1.getHeaders().add("Access-Control-Allow-Credentials", "true"); arg1.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD"); } }
※参考
JAX-RS2.0でRESTサービスを作る際にヘッダーを指定する - 自分の仕事を憎むには人生は余りにも短い
今回作ったサービスは以下に上げてます。
Tomcat 8でのみ動作確認済みです。
wildflyではeclipseLink関係でうまくうごかなかったです。
github.com
またサービスを動かすためにはDBにテーブルが必要です。
以下のSQLでテーブルを作成できます。
CREATE TABLE BOOKSTORAGE (ID VARCHAR(255) NOT NULL PRIMARY KEY, ISBN VARCHAR(255), TITLE VARCHAR(255), AUTHOR VARCHAR(255));
今回でBookSelfの記事は終了です。
今後改修等あれば記事の作成をしていこうかなと思ってます。(実用的にするにはまずGoogleBookAPIをやめるのが最初かな)
自作アプリ「BookSelf」のAngular部分の解説
前記事に書いたBookSelfのAngular部分の解説となります。
Angular部分はangular CLIを使用して作成しました。
言語はTypeScriptになります。
起動
cd BookSelfApp ng serve
構成
ソースの構成は以下のような形です。
src │ app.module.ts │ ├─component │ home.component.css │ home.component.html │ home.component.ts │ router.component.ts │ ├─model │ bookInfo.ts │ bookItem.ts │ ├─profile │ profile.ts │ └─service │ bookapi.service.ts │ bookself.database.service.ts │ └─common http.service.ts
以下のような方針でディレクトリは分けています。
component
画面(component単位)を格納するディレクトリ
画面に対してロジック(TypeScript)、HTML、CSSを分けて格納するディレクトリ
model
データオブジェクト(言い方が難しい。JavaではJOJO?)を格納するディレクトリ
profile
環境情報を格納するディレクトリ
UI
UIデザインにはbootstrapを使用しています。
npm install --save bootstrap
"apps" : [ ... "styles" : [ "../node_modules/bootstrap/dist/css/bootstrap.min.css", "styles.css" ],
※ng-bootstrapもインストールしてますがこちらはまだ使用していないです。
今回はいったんこんな感じの構成で作成しました。
いろいろ考えましたが、経験が増えていくうちにこれから変わってくかもしれません。
わかりずらく、いろいろ調べたところ
自分の記録用と同じことで迷った人の参考までに
■app.module.tsへの記載
AngularCLIを使用している場合はapp.module.tsになるがその指定はmain.tsとなります。
さらにはangular-cli.jsonで起動もとをmain.tsに決めています。(あとindex.htmlとかも)
・Componentを追加や削除の場合
ngModuleのdeclarationsに記載をする
・ライブラリの機能を追加したい場合
モジュールのimportを記載し、ngModuleのimportにも記載する
・constructorでインスタンス化する場合(DI)
ngModuleのproviderに記載をする
※HttpとかはHttpModuleの内部でproviderの宣言がされているのでHttpModuleをImportすることで解決している
・最初の起動画面
ngModuleのbootstrapに記載
■インクリメンタルサーチ
検索フォームはインクリメンタルサーチを作っています。(必要だろうか。。。)
f: FormGroup; public constructor(private fb: FormBuilder, ....) {...}
fというフォームグループの宣言とFormBuilderのDIをします。
this.f = this.fb.group({ 'searchWord': ['', Validators.required] }); this.f.valueChanges.debounceTime(500).subscribe(value => { this.searchWord = value['searchWord']; this.bookapiService.search(this.searchWord).subscribe(value => { this.bookItems = value[0]; this.totalItems = value[1]; this.initPaginationList(this.page); }); });
これをngOnInit(最初に動く)で実装しています。
searchWordフォームコントロールをFormBuilderに設定します。
Validators.requiredは指定フィールドが必須という意味らしい。今回はあまり意識しないです。
値に変更がかかってから次の変更を検知するまで500ミリ秒待ちを入れてます。無駄に反応しすぎるのをさけるためです。
変更を受けて検索処理が入るようになり、インクリメンタルサーチ的な動きをつくっています。
<form class="form-search" [formGroup]="f"> <div class="form-group"> <input type="text" value="utf8" formControlName="searchWord" class="form-control" placeholder="Please enter keywords"> </div> </form>
フォームは以下のような形で上記のフォームグループfの紐づけとsearchWordの紐づけをします。
※参考
qiita.com
qiita.com
■APIの結果を受けて(この結果も使用する)、さらにAPIを投げる場合
参考(動作未確認、ソースではbookapi.service.tsで実装例あり)
//結果をObservable型でComponentのsubcribeに返すことですべての結果を非同期でわたせるっぽい return http.request(最初のAPI) .map(res => { let result = res.json(); return result; }) .map(result => { firstApiResult = result; let reses = [];//複数のAPI結果の格納先 for(var i in result){ let res = http.request(次のAPI, i) .map(res => { let result = res.json(); return result; }); reses.push(res); } return reses; }) .flatMap(reses => Observable.forkJoin(reses))//flatMapとObservable.forkJoinを使用することで最初のAPI結果を待って、次のAPIも非同期で処理できる .map(secondApiResults => { return [firstApiResult, secondApiResults];//secondApiResultsは配列 });
※参考
Angular2でObservableを使ってHTTPリクエストを複数送信 | VPSサーバーでWebサイト公開 備忘録 ~Linux、MySQLからAJAXまで
アプリ作成前にWeb以外では以下の本を参考にさせてもらっています。
Webアプリケーション作りの基本的なところは押さえられると思います。
Angular2によるモダンWeb開発 TypeScriptを使った基本プログラミング
- 作者: 末次章
- 出版社/メーカー: 日経BP社
- 発売日: 2017/01/18
- メディア: 単行本
- この商品を含むブログ (1件) を見る
今回のAngularのソースはGitHubにコミットしてます。
github.com
自作のWebAPI部分(DBも)がないと動きません。
その部分に関しての詳細は次の記事に記載します。
AngularでSPAを作ってみる
久しぶりの投稿。。。
突然ですがAngularを使ってSPAを作成してみました。(勉強中なので機能はまだまだですが)
記録のためにブログ書きます。
画面(UI)とロジックは疎結合にするべきという考えを持ちながらもあまりそういう開発を学んでこれなかったため、
HTML、JavaScriptを使用して画面を作り、WebAPIを使用してサーバから画面表示に必要なデータを取ってくるSingle-page Applicationを作成しようと思いました。
構成
フロント:Angular
バック:JAX-RS
DB:MySQL
構成はいろいろと悩みましたがフロント面はいろいろ調べた結果AngularがSPAの考えを取り入れているうえに、使っている人口数も多そうでしたので選びました。
バックは簡単に作れてかつ、いままで使ってこなかった技術を使用するということでJAX-RSで開発することに決めました。
DBは触ったことある無難なMySQLです。
今回は作成したアプリケーションの紹介になります。
作りのポイント、手順や細かい構成については次回以降に書こうと思います。
アプリケーション
持ってる本管理アプリケーション
「Bookself」
経緯
上でも書いた通り「アプリを作る!」と思いアプリケーションの構成決めから初めてしまったのでどんなアプリにするかのアイディアはまったくありませんでした。
でもコンテンツとなるものは外部APIを呼んでみようかなと考えていましたのでそこからアイディア探しをしました。
外部APIには本データがとれるAPIもあり、そういえば「マンガを買うときに何巻まで買ったっけ?」と困ったことがあったことから、
買う前に本を検索して持っているかどうかがわかるアプリがあればいいなという考えで作成をはじめました。
紹介
検索フォームから確認したい本を検索できます。
検索方法は(APIの)キーワード検索になっています。
検索結果としてAPIから取得した本の画像とタイトルと著者がでます。(ゲームで検索した場合)
検索結果はページングで次の検索結果を表示できます。
BookMarkアイコンをクリックして持っている本を登録できます。
登録した本はアイコンの色が変わるので、どの本がもっているのかすぐにわかるようになっています。
登録した本は再度アイコンをクリックで解除できます。
機能はこれだけですが、本さえ検索できれ目的のどの本を持っているかはわかります。
今後は持ってる本の一覧や検索機能がないとなーと機能追加を考えています。
ORA-28001: the password has expiredでOracleにつながらないよ
久々の更新でいきなりですが、VMで用意していた開発用のOracleDBに接続できなくなりました。
エラー内容は
ORA-28001: the password has expired
Oracle 11gからだとデフォルトで180日でパスワードが切れるらしい・・・
DBの管理ユーザでログインし、状況を確認。
ユーザの状態を確認
SELECT USERNAME,PROFILE FROM DBA_USERS WHERE USERNAME = '[ユーザ名]';
PROFILEの状態を確認
SELECT * FROM DBA_PROFILES WHERE RESOURCE_NAME = ‘PASSWORD_LIFE_TIME';
私の状態だとユーザのPROFILEがDEFAULTでDEFAULTだとLIMITが180日になっており、
ようするに180日で私のユーザのパスの期限が切れます。
以下のコマンドで期限切れのユーザを使えるようにするのと今後の有効期限をなくします。
デフォルトの有効期限を無期限に変更します。
ALTER PROFILE DEFAULT LIMIT PASSWORD_LIFE_TIME UNLIMITED;
ユーザのパスを変更します。
ALTER USER [ユーザ名] IDENTIFIED BY [新しいパスワード(同じパスでもいけます)];
ユーザのロックを解除
ALTER USER [ユーザ名] ACCOUNT UNLOCK;
みなさんも気をつけてください。
MVCを意識したい
だいぶ期間が開いてしまいましたけど、いちおう成果の記録をと・・・
DBのSQLエディターを作成したくて、その練習としてSwingでMVCを意識して3分タイマーをJavaで作成しました。
なぜタイマーかというとこの記事の影響です。
作成したタイマーはGitHubにあげてるので、よろしくお願いします。
工夫というかいろいろ調べる中で意識したのはObserverモデルでモデルからビューに変更を通知してるところですかね。
ruihub/Timer · GitHub
MVCの考え方というか各機能の分離はホント難しい・・・
このへんいろいろ意見とか考え方とか他のひとにもきけたらなぁ
記事には関係ないですが、今年もいよいよ今日で最後・・・
もっとプログラミングできるようになりたい!
今興味があるのデザインパターンを使った設計とかどうやって実装するかとか設計とコーディングの間みたいなところと、テストの自動化とか効率化に関することだから、
来年はその辺を勉強してきたい!