Translate

2017년 9월 28일 목요일

[Angular 2+][DateTimePicker] ng-pick-datetime 컴포넌트 적용





Angular 초보가 Angular 4 에 DateTimePicker 컴포넌트를 붙이면서 공식문서만 보고 하려니 삽질이 하늘을 찔렀다.

모듈명: ng-pick-datetime
링크: https://danielykpan.github.io/date-time-picker/


JHipster(Angular+SpringBoot) v4.8.0 에 해당 컴포넌트를 아래와 같이 연동했다.
(같은 디자인의 DateTimePicker 가 여러곳에서 사용이 되기에 데이터 양방향 바인딩 가능한 WrapperComponent를 만들었다.)
1004lucifer

[package.json]
"dependencies": {
 "@angular/animations": "4.3.2",
 "@angular/common": "4.3.2",
 "@angular/compiler": "4.3.2",
 "@angular/core": "4.3.2",
 "@angular/forms": "4.3.2",
 "@angular/http": "4.3.2",
 "@angular/platform-browser": "4.3.2",
 "@angular/platform-browser-dynamic": "4.3.2",
 "@angular/router": "4.3.2",
 "@ng-bootstrap/ng-bootstrap": "1.0.0-beta.1",
 "bootstrap": "4.0.0-beta",
 "core-js": "2.4.1",
 "font-awesome": "4.7.0",
 "jquery": "3.2.1",
 "ng-jhipster": "0.2.12",
 "ng-pick-datetime": "5.0.0-beta.6",
 "ng2-webstorage": "1.8.0",
 "ngx-cookie": "1.0.0",
 "ngx-infinite-scroll": "0.5.1",
 "reflect-metadata": "0.1.10",
 "rxjs": "5.4.2",
 "swagger-ui": "2.2.10",
 "tether": "1.4.0",
 "zone.js": "0.8.16"
},

PS. 작성 후 'npm install' 이나 'yarn install' 명령어를 통해 해당 패키지를 설치한다.





[vendor.css] node_module CSS가 들어가는 파일
/* after changing this file run 'yarn run webpack:build' */
/*@import '~bootstrap/dist/css/bootstrap.min.css';*/
@import '~ng-pick-datetime/assets/style/picker.min.css';
@import '~font-awesome/css/font-awesome.css';



1004lucifer

[datetimepicker.module.ts] (이름을 custom-datetimepicker.~ 형식으로 바꿀껄 그랬나..)
import {CUSTOM_ELEMENTS_SCHEMA, NgModule} from '@angular/core';
import {DateTimePickerModule} from 'ng-pick-datetime';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {CustomDateTimePickerComponent} from './datetimepicker.component';
import {FormsModule} from '@angular/forms';

@NgModule({
    imports: [
        FormsModule,
        DateTimePickerModule,
        BrowserAnimationsModule
    ],
    declarations: [CustomDateTimePickerComponent],
    exports: [CustomDateTimePickerComponent],
    schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class CustomDatetimepickerModule {
}





[datetimepicker.component.ts]
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';

@Component({
    selector: 'jhi-datetimepicker',
    templateUrl: './datetimepicker.component.html',
    styles: []
})
export class CustomDateTimePickerComponent implements OnInit {

    componentDatetime: Date;
    dateFormat = 'YYYY-MM-DD HH:mm';

    ko = {
        firstDayOfWeek: 0,
        dayNames: [ '일요일', '월요일', '화요일', '수요일', '목요일', '금요일', '토요일' ],
        dayNamesShort: [ ' 일', ' 월', ' 화', ' 수', ' 목', ' 금', ' 토' ],
        monthNames: [ '1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월' ],
        monthNamesShort: [ '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12' ]
    };

    @Input()
    get datetime() {
        return this.componentDatetime;
    }
    set datetime(value) {
        this.componentDatetime = value;
        this.datetimeChange.emit(this.componentDatetime);
    }
    @Output() datetimeChange = new EventEmitter();

    constructor() {
    }

    ngOnInit() {
    }
}
1004lucifer
설명:
짧은 이름의 요일명(일/월/화/수/목/금/토)이 화면상에 너무 왼쪽에 붙고 &nbsp 가 적용되지 않아 앞에 눈에 보이지 않는 특수문자를 넣어주었다. (ㄱ => 한자 => 1번)





[datetimepicker.component.html]
<owl-date-time
    [(ngModel)]="datetime"
    [locale]="ko"
    [dateFormat]="dateFormat"
    [placeHolder]="''"
></owl-date-time>





[적용.module.ts]
@NgModule({
    imports: [
        CustomDatetimepickerModule,
  ...
    ],
    declarations: [...],
 ...
})
export class UseModule {
}





[적용.component.ts]
@Component({
    ...
})
export class UseComponent implements OnInit {

    public startDateTime: Date;

    constructor() {
    }

    ngOnInit() {
    }

}





[적용.component.html]
<div>
<jhi-datetimepicker [(datetime)]="startDateTime"></jhi-datetimepicker>
</div>





PS.
적용한 컴포넌트가 기본적으로 상위의 CSS를 상속받아 Demo 와는 스타일이 약간 변경되었는데 시/분 입력란이 왼쪽으로 치우져처 있어서 아래와 같이 CSS 속성을 추가했다.
1004lucifer
[global.css] 전체 페이지에 적용되는 CSS
.owl-timer-wrapper .owl-timer-input {
    text-align: center;
}







적용된 결과는 아래 모습과 같다.



[Angular 2+] Angular 2+ 사용가능한 DateTimePicker 컴포넌트





Angular 4 에서 사용할만한 DateTimePicker 를 찾아봤는데 몇개 없다.
(DatePicker / TimePicker 는 정말 많이 있지만 괜찮은 DateTimePicker는 정말 찾기 힘들었다.)


그나마 좀 찾아본걸 리뷰 해본다.


1. ng-pick-datetime
  - 링크: https://danielykpan.github.io/date-time-picker/ (Demo 링크 동일)
1004lucifer
  - 찾아본 DateTimePicker 중에 가장 마음에 들며 디자인이나 기능도 무난하다.



PS.
위의 모듈을 아래의 글과 같이 적용했다.
http://1004lucifer.blogspot.kr/2017/09/angulardatetimepicker-angular-4-ng-pick.html





2. ng2-eonasdan-datetimepicker
  - 링크: https://github.com/atais/ng2-eonasdan-datetimepicker (Demo 링크)
1004lucifer
  - 처음엔 사용방법이 마음에 안들었었는데 지금에서야 다시보니 나쁘지 않은것 같기도..






3. ng2-datetime-picker
  - 링크: https://github.com/ng2-ui/datetime-picker  (Demo 링크)
1004lucifer
  - 사용성에 심각한 문제가 있다.
    날짜/시간/분 을 클릭하면 바로 팝업이 닫히며 시각이 입력된다.
    보통 날짜=>시간=>분 입력을 할텐데.. 암튼 사용해보면 안다.






PS.
https://ng-bootstrap.github.io 의 Component 중 pagination 을 사용하는데..
여기에 DateTimePicker 컴포넌트도 같이 있으면 좋았으려만..
DatePicker / TimePicker 이렇게 컴포넌트가 나뉘어져 있어 결국 다른 컴포넌트를 사용할 수 밖에 없었다.
다른 괜찮다 싶은 DateTimePicker 컴포넌트를 발견하니 AngularJS(1.x) 사용가능한 모듈이었다.


2017년 9월 26일 화요일

[JHipster] Angular 4 사용 시 Audits 메뉴 표시되지 않는 문제




version: JHipster 4.8.0



증상
1004lucifer
JHipster 프로젝트 생성 하고 admin 로그인 후 '관리자=>Audit' 메뉴 들어가게되면 화면이 오류가 나며 정상적으로 보여지지 않게된다.







{
  "type" : "http://www.jhipster.tech/problem/problem-with-message",
  "title" : "Bad Request",
  "status" : 400,
  "detail" : "Failed to convert value of type 'java.lang.String[]' to required type 'java.time.LocalDate'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.RequestParam java.time.LocalDate] for value '2017년-08월-26일'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2017년-08월-26일]",
  "message" : "error.http.400"
}




디버깅을 해보니 날짜를 넘겨줄 때 '2017-08-26' 이렇게 넘겨줘야 하는걸 년/월/일 을 붙여서 넘겨주고 있었다.

Angular 에서 기본적으로 사용하는 DatePipe 모듈에서 timezone 개념이 있는데 EndUser의 시스템의 로케일을 기본으로 가져온다고 한다.




해결방법

아래와 같이 Locale 을 en-US 형식으로 DatePipe를 생성하여 정상적으로 나오는 것을 확인했다.
1004lucifer

src/main/webapp/app/admin/audits/audits.component.ts
export class AuditsComponent implements OnInit {
    audits: Audit[];
    fromDate: string;
    itemsPerPage: any;
    links: any;
    page: number;
    orderProp: string;
    reverse: boolean;
    toDate: string;
    totalItems: number;
    datePipe: DatePipe;

    constructor(
        private auditsService: AuditsService,
        private parseLinks: JhiParseLinks,
        private paginationConfig: PaginationConfig,
        // private datePipe: DatePipe
    ) {
        this.itemsPerPage = ITEMS_PER_PAGE;
        this.page = 1;
        this.reverse = false;
        this.orderProp = 'timestamp';
        this.datePipe = new DatePipe('en-US');
    }
 
 ...
}






[TypeScript] TS2345 Argument of type A is not assignable to parameter of type A 이슈




Framework: Angular 4


HTTP 요청에 파라미터를 추가하여 날리기 위해 URLSearchParams 에서 AppendAll 메소드를 사용하는데 아래와 같이 오류가 발생을 했다.



1004lucifer

TS2345:Argument of type 'URLSearchParams' is not assignable to parameter of type 'URLSearchParams'. Property 'rawParams' is missing in type 'URLSearchParams'.

(URLSearchParams 를 URLSearchParams 파라미터로 할당할 수 없다고!???)





알고보니 좀 허무했는데..

Angular 에 URLSearchParams 라는 클래스가 있고,
TypeScript 자체에 URLSearchParams 라는 인터페이스가 있다.
1004lucifer
Angular의 URLSearchParams를 사용해야 했는데 import를 하지 않으니 TypeScript의 URLSearchParams를 기본으로 사용을 해서 문제가 되었다.





다음과 같이 Angular 의 해당 클래스를 import 해주니 문제가 없어졌다.





참고:
https://github.com/Microsoft/TypeScript/issues/9358


2017년 9월 25일 월요일

[Angular 2+][ng-bootstrap] Modal Component 데이터 전달





version: Angular 4

ng-bootstrap: Modal
(https://ng-bootstrap.github.io/#/components/modal/examples)

1004lucifer
Component 간에 데이터 전달 시 태그 속성으로 emit 이벤트를 전달받을 함수를 넣으면 되는데.. Modal 사용 시 HTML 태그를 명시하지 않아 스크립트에서 어떻게 처리를 하는지 알아보았다.




modal.component.ts
data: string;
@Output() outputData = new EventEmitter<SystemDeviceFilter>();

constructor(
 // ng-bootstrap Modal
 public activeModal: NgbActiveModal
) {
}

doOutput() {
 this.outputData.emit(this.data);
 this.activeModal.close(); // Modal Close
}



1004lucifer

parent.component.ts
constructor(
 private modalService: NgbModal,
 ...
) {
}

const modalComponent = this.modalService.open(ModalComponent).componentInstance;
modalComponent.outputData.subscribe((data) => {
 // data을 활용한 로직
});



참조:
https://github.com/ng-bootstrap/ng-bootstrap/issues/861#issuecomment-253500089

2017년 9월 20일 수요일

[Angular 2+][ng-bootstrap] Customize the CSS for "ngb-pagination" Component





ngb-pagination Component:
(https://ng-bootstrap.github.io/#/components/pagination/examples)









jHipster(Angular 4) 를 이용해 프로젝트 제작 중 Paging 디자인을 아래와 같이 변경하였다.
1004lucifer
<ngb-pagination> Component 가 렌더링 된 후의 HTML 과 적용된 CSS를 수정하여 디자인을 대충 맞춰놨는데 bootstrap css 를 사용하지 않도록 vender.css(jHipster)에서 bootstrap을 주석처리하니 많이 틀어져버려 bootstrap css의 일부를 가져와야 했다.

 
<div class="row justify-content-center">
 <ngb-pagination [collectionSize]="totalItems" [(page)]="page" (pageChange)="loadPage(page)"></ngb-pagination>
</div>
 

 
.row.justify-content-center {
    display: -ms-flexbox;
    display: flex;
    -ms-flex-wrap: wrap;
    flex-wrap: wrap;
    margin-right: -15px;
    margin-left: -15px;
    justify-content: center!important;
    margin-top: 20px;
}
ngb-pagination > .pagination {
    display: -ms-flexbox;
    display: flex;
    padding-left: 0;
    list-style: none;
    border-radius: .25rem;
}
ngb-pagination > .pagination > ol,ul,li {
    list-style: none;
}
ngb-pagination > ul > .page-item .page-link {
    box-sizing: border-box;
    border:0;
    width: 35px;
    height: 35px;
    font-size: 14px;
    text-align: center;
    padding: 8px;
    margin: 0 2px 0 2px;
    text-decoration: none;
    color: #555;
}
ngb-pagination > ul > .page-item .page-link > span {
    width: 0;
    height: 0;
    font-size: 0;
}
ngb-pagination > ul > .page-item > a {
    display: block;
}
ngb-pagination > ul > .page-item.active > a {
    color: #0e77d9;
    font-weight: bold;
}
ngb-pagination > ul > .page-item > a:hover {
    color: #0e77d9;
    font-weight: bold;
}
ngb-pagination > ul > .page-item > a[aria-label="First"] {
    background-image: url('../images/common/count_first.gif');
}
ngb-pagination > ul > .page-item > a[aria-label="Previous"] {
    background-image: url('../images/common/count_prev.gif');
}
ngb-pagination > ul > .page-item > a[aria-label="Next"] {
    background-image: url('../images/common/count_next.gif');
}
ngb-pagination > ul > .page-item > a[aria-label="Last"] {
    background-image: url('../images/common/count_end.gif');
}
 




PS.

How to customize the CSS for ng-bootstrap controls using Angular 2 / Bootstrap 4
1004lucifer
stackoverflow 사이트에서 위의 글에서는 /deep/ 또는 > 를 사용하라고 되어있어 처음에 /deep/ combinator를 사용했었다.
하지만 Chrome 에서는 정상적으로 나오지만 IE11 에서 문제가 있다는 것을 발견했다.
> selector 사용시 이슈가 해결되었다.


Browser: Internet Explorer 11

위는 퍼블리싱 소스
아래는 <ngb-pagination>

/deep/ Selector

> selector 



알아보니 Chrome 과 Opera 를 제외하고 Shadow DOM 을 지원하지 않아 발생한 이슈였다.
1004lucifer
[Shadow DOM 지원 브라우저 확인]
http://caniuse.com/#feat=shadowdom

[Shadow DOM 및 /deep/ combinator 설명]
https://www.html5rocks.com/ko/tutorials/webcomponents/shadowdom-201/



2017년 9월 13일 수요일

[JHipster] microservice 프로젝트 Thymeleaf 대신에 JSP 사용할 수 있게 변경 방법





JHipster 에서 JSP를 사용해야 할 일이 있어 Thymeleaf 대신에 JSP를 사용하도록 변경을 했다.
Version: JHipster Generator v4.8.0


우선 아래와 같은 셋팅으로 프로젝트를 생성했다.




1004lucifer
아래와 같이 수정을 했다.

1. build.gradle 수정

dependencies {

    ...

    compile ("org.springframework.boot:spring-boot-starter-web") {
//        exclude module: 'spring-boot-starter-tomcat'
    }
    compile "org.apache.tomcat.embed:tomcat-embed-jasper"
    compile 'javax.servlet:jstl:1.2'
//    compile "org.springframework.boot:spring-boot-starter-undertow"
//    compile "org.springframework.boot:spring-boot-starter-thymeleaf"

    ...

}





2. src/main/resources/config/application.yml 수정

messages:
 basename: i18n/messages
mvc:
 view:
  prefix: /WEB-INF/jsp/
  suffix: .jsp
 favicon:
  enabled: false
thymeleaf:
 mode: XHTML





3. src/main/java/kr/co/_1004lucifer/config/WebConfigurer.java 수정

@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
 MimeMappings mappings = new MimeMappings(MimeMappings.DEFAULT);
 // IE issue, see https://github.com/jhipster/generator-jhipster/pull/711
 mappings.add("html", "text/html;charset=utf-8");
 // CloudFoundry issue, see https://github.com/cloudfoundry/gorouter/issues/64
 mappings.add("json", "text/html;charset=utf-8");
 container.setMimeMappings(mappings);

 /*
  * Enable HTTP/2 for Undertow - https://twitter.com/ankinson/status/829256167700492288
  * HTTP/2 requires HTTPS, so HTTP requests will fallback to HTTP/1.1.
  * See the JHipsterProperties class and your application-*.yml configuration files
  * for more information.
  */
// if (jHipsterProperties.getHttp().getVersion().equals(JHipsterProperties.Http.Version.V_2_0) &&
//  container instanceof UndertowEmbeddedServletContainerFactory) {
//
//  ((UndertowEmbeddedServletContainerFactory) container)
//   .addBuilderCustomizers(builder ->
//    builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true));
// }
}




1004lucifer
4. src/main/java/kr/co/_1004lucifer/config/ThymeleafConfiguration.java 파일 삭제





5. src/main/java/kr/co/_1004lucifer/web/TestJspController.java 파일 추가

package kr.co._1004lucifer.web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class TestJspController {

    @RequestMapping("test")
    public String testJsp() {
        return "test";
    }

}




1004lucifer
6. src/main/webapp/WEB-INF/jsp/test.jsp 파일 추가

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
Test Jsp Page
</body>
</html>





위의 작업한 프로젝트를 압축하여 파일을 공유한다.
파일 다운받기
1004lucifer


참고:
https://www.mkyong.com/spring-boot/spring-boot-hello-world-example-jsp/


[JHipster] 'yarn global upgrade generator-jhipster' 업그레이드 시 'No lockfile found' 오류





JHipster 새로운 버전이 나왔다고 해서 업그레이드를 하기위해 가이드대로 입력을 했더니 정상적으로 수행이 되지 않았다.



1004lucifer

C:\Users\1004lucifer\IdeaProjects\jhipster-microservice>yarn global upgrade generator-jhipster
yarn global v0.27.5
info No lockfile found.
error Couldn't find a package.json file in "C:\\Users\\1004lucifer\\AppData\\Local\\Yarn\\config\\global"
info Visit https://yarnpkg.com/en/docs/cli/global for documentation about this command.

C:\Users\1004lucifer\IdeaProjects\jhipster-microservice>






jhipster 를 설치 후 yarn 을 설치했더니 yarn 설치된 패키지중에 jhipster 가 없는 듯 했다.
하긴.. yarn을 통해 설치한 패키지가 하나도 없는 것 같다.


이것저것 알아보다가 그냥 npm으로 다시 설치해보니 정상적으로 업데이트가 된 것을 볼 수 있었다.


1004lucifer


C:\Users\1004lucifer\IdeaProjects\jhipster-microservice>npm install -g generator-jhipster
C:\Users\1004lucifer\AppData\Roaming\npm\jhipster -> C:\Users\1004lucifer\AppData\Roaming\npm\node_modules\generator-jhipster\cli\jhipster.js
+ generator-jhipster@4.8.0
updated 9 packages in 40.33s

C:\Users\1004lucifer\IdeaProjects\jhipster-microservice>