SlideShare une entreprise Scribd logo
1  sur  32
Télécharger pour lire hors ligne
+
한장현
han41858@gmail.com
han41858.tistory.com
1
• 전 삼성SDS 선임
• TV플랫폼 JavaScript 어플리케이션 구현
• 리테일 솔루션 서버 & 프론트엔드 구현
• 프리랜서 개발자
• han41858.tistory.com 블로그 운영
• Angular 2 번역서 집필중
• GDG Korea Web Tech 운영진
한장현 (Janghyun Han)
2
3
ngular
1.6.0-rc.2 safety-insurance
4
• 마술 같은 2-way binding
• HTML 표준을 기반으로 기능 확장
• 체계적인 컴포넌트, Web Component로 가는 길
• Front-end 전체를 커버하는 프레임워크
• Google 에서 관리, 개선
• 풍부한 사용자 삽질 경험
Angular를 쓰는 이유
5
Class, inheritance
import/export
Arrow function
Promise
ECMAScript 2015
6
• 간단해지는 코드, syntax sugar
• 클래스, 상속, 모듈 구성 ⇒ 아키텍처 진화
• 브라우저들 지원 계속
• 결국엔 표준, transpiler는 거쳐갈 뿐
ES6를 쓰는 이유
7
8
ES6ES5
script src
<script src="bundle.js"></script>
"devDependencies": {
"angular": "^1.5.8",
"babel-cli": "^6.18.0",
"babel-loader": "^6.2.7",
"babel-preset-es2015": "^6.18.0",
"webpack": "^1.13.3"
}
<script src="angular.js"></script>
<script src="index.js"></script>
"devDependencies": {
"angular": "^1.5.8"
}
9
index.js
ES6ES5
(function () {
var ngApp = angular.module('angular1es5', []);
})();
import angular from 'angular';
(() => {
const ngApp = angular.module('angular1es6', []);
})();
10
Webpack
// this is ES5
var webpack = require('webpack');
module.exports = {
entry : [
'./index.js'
],
output : {
filename : 'build/bundle.js',
sourceMapFilename: '[name].map'
},
module : {
loaders : [
{
test : /.js$/,
loader : 'babel?presets[]=es2015
exclude : /node_modules/
},
{
test : /.pug$/,
loader : 'pug-loader',
exclude : /node_modules/
}
]
},
plugins : [
// new webpack.optimize.UglifyJsPlugin({minimize: true})
]
};
webpack.config.js
"devDependencies": {
"angular": "^1.5.8",
"angular-route": "^1.5.8",
"babel-cli": "^6.18.0",
"babel-loader": "^6.2.7",
"babel-preset-es2015": "^6.18.0",
"file-loader": "^0.9.0",
"pug": "^2.0.0-beta6",
"pug-loader": "^2.3.0",
"webpack": "^1.13.3",
"webpack-dev-server": "^1.16.2"
}
package.json
11
export default class HomeCtrl {
constructor () {
console.log('HomeCtrl.constructor()');
}
}
/view/homeCtrl.js
p this is home
/view/home.pug
import angular from 'angular';
import ngRoute from 'angular-route';
import HomeCtrl from './view/homeCtrl';
const main = () => {
console.log('main()');
const ngApp = angular.module('angular1es6', ['ngRoute']);
ngApp.config(($routeProvider, $locationProvider) => {
console.log('this is angular config');
$routeProvider
.when('/', {
template : require('./view/home.pug'),
controller : 'HomeCtrl',
controllerAs : 'Ctrl'
})
.otherwise({
redirectTo : '/'
});
// need to angular.js routing
$locationProvider.html5Mode({
enabled : true,
requireBase : false
});
});
ngApp.controller('HomeCtrl', HomeCtrl);
};
main();
index.js + ngRoute
12
webpack-dev-server
13
ngApp.directive('CustomDirective', () => new CustomDirective);
ngApp.filter('groupBy', GroupBy);
ngApp.service('CustomSvc', CustomSvc);
ngApp.controller('CustomCtrl', CustomCtrl);
Angular Components
ngApp.directive('CustomDirective', CustomDirective);
ngApp.filter('groupBy', GroupBy);
ngApp.service('CustomSvc', CustomSvc);
ngApp.controller('CustomCtrl', CustomCtrl);
function
Class
new Class
14
Filter
ES6ES5
ngApp.filter('uppercase', uppercase);
function uppercase () {
return function (item) {
return item.toUpperCase();
};
}
<script src="uppercase.filter.js"></script>
const uppercase = () => {
return (input) => {
return input.toUpperCase();
};
};
export default uppercase;
import uppercase from './uppercase.filter';
ngApp.filter('uppercase', uppercase);
15
.ctrlRoot
p this is home ctrl
p {{ Ctrl.title }}
button(ng-click="Ctrl.select()") Select
export default class HomeCtrl {
constructor () {
console.log('HomeCtrl.constructor()');
this.title = 'this is title';
}
select () {
console.log('HomeCtrl.select()');
}
}
$routeProvider
.when('/', {
template : require('./view/home.pug'),
controller : 'HomeCtrl',
controllerAs : 'Ctrl'
})
ngApp.controller('HomeCtrl', HomeCtrl);
function HomeCtrl ($scope) {
console.log('home controller');
$scope.title = 'this is title';
$scope.select = function () {
console.log('HomeCtrl.select()');
}
}
Controller
ES6ES5
.ctrlRoot
p this is home ctrl
p {{ title }}
button(ng-click="select()") Select
16
Service
ES6ES5
ngApp.service('myService', myService);
<script src="./view/myService.js"></script>
function myService () {
this.testFnc = function () {
console.log('myService.testFnc()');
};
return this;
}
export default class MyService {
constructor () {
console.log('MyService');
}
testFnc () {
console.log('MyService.testFnc()');
}
}
ngApp.service('MyService', MyService);
export default class HomeCtrl {
constructor (MyService) {
this.MyService = MyService;
}
select () {
this.MyService.testFnc();
}
}
static 있어야 할 것 같지만 없어야 함
17
ngApp.directive('myDirective', () => new MyDirective);
export default class MyDirective {
constructor () {
console.log('MyDirective.constructor()');
this.restrict = 'E';
this.template = '<p>message : {{ this.msg }}</p>';
this.scope = {
msg : '@'
};
}
link (scope) {
console.log(scope.msg);
}
}
ngApp.directive('myDirective', myDirective);
function myDirective () {
return {
restrict : 'E',
template : '<p>message : {{ msg }}</p>',
scope : {
msg : '@'
},
controller : function ($scope) {
console.log('myDirective.controller()');
console.log($scope.msg);
}
}
}
<script src="myDirective.js"></script>
Directive
ES6ES5
directive 등록할 때 () => new18
Directive vs. Component
19
Directive vs. Component
const customInput = {
bindings : {
model : '='
},
template : '<input ng-model="$ctrl.model"></input>',
controller : function () {
this.$onInit = () => {
}
}
};
export default customInput;
ngApp.component('customInput', customInput);ngApp.directive('customInput', () => new customInput);
export default class customInput {
constructor () {
this.restrict = 'E';
this.scope = {
model : '='
};
this.template = '<input ng-model="model"></input>';
}
controller () {
}
}
20
static delete (param) {
const self = this;
return util.objValidate(param, {
userID : Constants.TYPE.EMAIL
}, Constants.ERROR.USER_CTRL.NO_PARAMETER, log, 'delete()')
.then(param => self.isExists(param))
.then(param => {
// delete records
return recordCtrl.deleteAll({
userID : param.userID
})
.then(() => {
log('remove records ok');
// param 자체를 다시 돌려주기 위해 Promise 필요
return Promise.resolve(param);
});
})
.then(param => {
// delete cards
return cardCtrl.deleteAll({
userID : param.userID
})
.then(() => {
log('remove cards ok');
return Promise.resolve(param);
});
})
.then(param => {
// delete assets
return assetCtrl.deleteAll({
userID : param.userID
})
.then(() => {
log('remove assets ok');
return Promise.resolve(param);
});
})
.then(param => {
// delete user
return User.remove({
userID : param.userID
})
.then(() => {
return Promise.resolve(param);
}, error => {
log(error);
return Promise.reject(new ERROR(Constants.ERROR.MONGOOSE.REMOVE_FAILED, log, 'delete()'));
});
})
.then(param => {
log(`delete ok : ${param.userID}`);
return Promise.resolve(true);
});
}
Promise
21
ngApp.config(($routeProvider, $locationProvider) => {
// include styles
require('./view/home.styl');
$routeProvider
.when('/', {
template : require('./view/home.pug'),
controller : 'HomeCtrl',
controllerAs : 'Ctrl'
})
.otherwise({
redirectTo : '/'
});
});
배포 : webpack
import가 아니므로 require 22
Angular 1 + ES6 + BDD
= Hell
23
describe('homeCtrl.test', () => {
it('module import', () => {
expect(true).to.be.true;
});
});
λ karma start
(node:7564) DeprecationWarning: Using Buffer without `new` will soon stop working. Use `new Buffer()`, or preferably `Buffer.from()`,
`Buffer.allocUnsafe()` or `Buffer.alloc()` instead.
18 11 2016 02:53:45.852:INFO [framework.browserify]: bundle built
18 11 2016 02:53:45.941:INFO [karma]: Karma v1.3.0 server started at http://localhost:9876/
18 11 2016 02:53:45.941:INFO [launcher]: Launching browser Chrome with unlimited concurrency
18 11 2016 02:53:45.952:INFO [launcher]: Starting browser Chrome
18 11 2016 02:53:47.283:INFO [Chrome 54.0.2840 (Windows 10 0.0.0)]: Connected on socket /#AjAqCwTlrwmVmV_sAAAA with id
16857313
Chrome 54.0.2840 (Windows 10 0.0.0): Executed 1 of 1 SUCCESS (0.005 secs / 0.001 secs)
BDD 시작
24
λ karma start
(node:12196) DeprecationWarning: Using Buffer without `new` will soon stop working. Use `new Buffer()`, or preferably `Buffer.from()`, `Buffer.allocUnsafe()` or `Buffer.alloc()` instead.
18 11 2016 02:19:10.237:INFO [framework.browserify]: bundle built
18 11 2016 02:19:10.343:INFO [karma]: Karma v1.3.0 server started at http://localhost:9876/
18 11 2016 02:19:10.343:INFO [launcher]: Launching browser Chrome with unlimited concurrency
18 11 2016 02:19:10.353:INFO [launcher]: Starting browser Chrome
18 11 2016 02:19:11.676:INFO [Chrome 54.0.2840 (Windows 10 0.0.0)]: Connected on socket /#sBpP4RL0XFZAwPtxAAAA with id 52822107
Chrome 54.0.2840 (Windows 10 0.0.0) ERROR
Uncaught SyntaxError: Unexpected token import
at test/homeCtrl.test.js:1
import HomeCtrl from '../view/homeCtrl';
describe('homeCtrl.test', () => {
it('module import', () => {
console.log(HomeCtrl);
expect(true).to.be.true;
expect(HomeCtrl).to.be.ok;
});
});
λ karma start
(node:11580) DeprecationWarning: Using Buffer without `new` will soon stop working. Use `new Buffer()`, or preferably `Buffer.from()`, `Buffer.allocUnsafe()` or `Buffer.alloc()` instead.
18 11 2016 02:31:24.248:INFO [framework.browserify]: bundle built
18 11 2016 02:31:24.339:INFO [karma]: Karma v1.3.0 server started at http://localhost:9876/
18 11 2016 02:31:24.339:INFO [launcher]: Launching browser Chrome with unlimited concurrency
18 11 2016 02:31:24.349:INFO [launcher]: Starting browser Chrome
18 11 2016 02:31:25.657:INFO [Chrome 54.0.2840 (Windows 10 0.0.0)]: Connected on socket /#pvpyGrXqq2TZPTgmAAAA with id 30236974
LOG: class HomeCtrl { ... }
Chrome 54.0.2840 (Windows 10 0.0.0): Executed 1 of 1 SUCCESS (0.007 secs / 0.002 secs)
babel : node6
λ karma start
(node:13196) DeprecationWarning: Using Buffer without `new` will soon stop working. Use `new Buffer()`, or preferably `Buffer.from()`, `Buffer.allocUnsafe()` or `Buffer.alloc()` instead.
18 11 2016 02:58:38.638:INFO [framework.browserify]: bundle built
18 11 2016 02:58:38.728:INFO [karma]: Karma v1.3.0 server started at http://localhost:9876/
18 11 2016 02:58:38.729:INFO [launcher]: Launching browser PhantomJS with unlimited concurrency
18 11 2016 02:58:38.738:INFO [launcher]: Starting browser PhantomJS
18 11 2016 02:58:40.301:INFO [PhantomJS 2.1.1 (Windows 8 0.0.0)]: Connected on socket /#JeRwavdozVZCC8HJAAAA with id 75347319
LOG: function HomeCtrl(MyService) { ... }
PhantomJS 2.1.1 (Windows 8 0.0.0): Executed 1 of 1 SUCCESS (0.008 secs / 0.001 secs)
babel : es2015
ES6가 돌지 않는다…
+
25
var $httpBackend;
beforeEach(function () {
module('angular1es5');
inject(function (_$httpBackend_) {
$httpBackend = _$httpBackend_;
});
});
module 선언, injection 불가
ES6ES5
const $injector, $httpBackend;
beforeEach(() => {
$injector = angular.injector(['angular1es6']);
$httpBackend = $injector.get('$httpBackend');
});
Object is not a constructor (evaluating 'module('angular1es6')')
r:/temp/test/homeCtrl.test.js:15:9 <-
R:/temp/3871fde1c6cf6c302eeae7add18a3b02.browserify:22:9
26
ngMock vs. ngMockE2E
The ngMock module provides support to inject
and mock Angular services into unit tests. In
addition, ngMock also extends various core ng
services such that they can be inspected and
controlled in a synchronous manner within test
code.
The ngMockE2E is an angular module which
contains mocks suitable for end-to-end testing.
Currently there is only one mock present in this
module - the e2e $httpBackend mock.
ngMock ngMockE2E
Fake HTTP backend implementation suitable for end-to-
end testing or backend-less development of applications
that use the $http service.
Fake HTTP backend implementation suitable for unit
testing applications that use the $http service.
.when()
.expect()
.flush()
.verifyNoOutstandingExpectation()
.verifyNoOutstandingRequest()
.resetExpectations()
.when()
ngMockE2E에는 $location 없음 27
Promise + http.flush()
Chrome 54.0.2840 (Windows 10 0.0.0) homeCtrl.test http test ok FAILED
Error: No pending request to flush !
at Function.$httpBackend.flush (node_modules/angular-mocks/angular-mocks.js:1799:34)
at r:/temp/test/homeCtrl.test.js:36:17
promise 함수로 부르면 동작 안함
flush() 타이밍 달라짐
flush는 Util에서 수행
const $injector = angular.injector(['angular1es6']);
const $httpBackend = $injector.get('$httpBackend');
const $http = $injector.get('$http');
beforeEach(() => {
$httpBackend.expectGET('/test')
.respond(['this', 'is', 'GET', 'test', 'data']);
});
it('ok', () => {
return new Promise(resolve => {
$http.get('/test').then(result => {
console.log('get().then()');
console.log(result);
console.log(result.data);
resolve(true);
});
$httpBackend.flush();
});
});
동작하는 코드
static post (uri, param, config) {
// use new promise for flush()
return new Promise((resolve, reject) => {
this.http.post(uri, param, config)
.then(result => {
resolve(result);
}, error => {
console.error(error);
reject(false);
});
if (this.httpBackend && this.httpBackend.flush) {
this.httpBackend.flush();
}
});
}
it('ok', () => {
return new Promise(resolve => {
Promise.resolve()
.then(() => {
$http.get('/test').then(result => {
console.log('get().then()');
console.log(result);
console.log(result.data);
resolve(true);
});
});
$httpBackend.flush();
});
});
28
여러 test 파일 동시 실행
const $injector = angular.injector(['angular1es6']);
const $httpBackend = $injector.get('$httpBackend');
const $http = $injector.get('$http');
Chrome 54.0.2840 (Windows 10 0.0.0) SignInCtrl test logic submit() with ajax error response response error test FAILED
AssertionError: expected [Error: Unexpected request: POST /api/user/signIn
No more request expected] to be an instance of ERROR
Chrome 54.0.2840 (Windows 10 0.0.0) SignUpCtrl test logic submit() error response response error test FAILED
AssertionError: expected [Error: [$rootScope:inprog] $digest already in progress
const ngApp = angular.module(appName, ['ngMock']); // working with $location, but not $httpBackend.whenPOST()...
var $injector = angular.injector(['MoneyBook']);
const $httpBackend = $injector.get('$httpBackend');
const $http = $injector.get('$http');
ClientUtil.http = $http;
ClientUtil.httpBackend = $httpBackend;
29
생성자 안에서 Promise
export default class HomeCtrl {
constructor () {
console.log('HomeCtrl.constructor()');
Promise.resolve(() => {
// do something
});
}
}
describe('constructor()', () => {
it('ok', () => {
// expect what...?
});
});
export default class HomeCtrl {
constructor () {
console.log('HomeCtrl.constructor()');
this.somethingPromise();
}
somethingPromise(){
return Promise.resolve()
.then() => {
// do something
});
}
}
30
Promise + $scope.$apply()
export default class HomeCtrl {
constructor () {
console.log('HomeCtrl.constructor()');
this.count = 0;
}
select () {
console.log('HomeCtrl.select()');
return Promise.resolve()
.then(() => {
this.count++;
});
}
}
export default class HomeCtrl {
constructor ($scope) {
console.log('HomeCtrl.constructor()');
this.$scope = $scope;
this.count = 0;
}
select () {
console.log('HomeCtrl.select()');
return Promise.resolve()
.then(() => {
this.count++;
this.$scope.$apply();
});
}
}
31
+ =
32

Contenu connexe

Tendances

Ansible loves Python, Python Philadelphia meetup
Ansible loves Python, Python Philadelphia meetupAnsible loves Python, Python Philadelphia meetup
Ansible loves Python, Python Philadelphia meetupGreg DeKoenigsberg
 
Nginx and friends - putting a turbo button on your site
Nginx and friends - putting a turbo button on your siteNginx and friends - putting a turbo button on your site
Nginx and friends - putting a turbo button on your siteWim Godden
 
Find bottleneck and tuning in Java Application
Find bottleneck and tuning in Java ApplicationFind bottleneck and tuning in Java Application
Find bottleneck and tuning in Java Applicationguest1f2740
 
Future Decoded - Node.js per sviluppatori .NET
Future Decoded - Node.js per sviluppatori .NETFuture Decoded - Node.js per sviluppatori .NET
Future Decoded - Node.js per sviluppatori .NETGianluca Carucci
 
Node.js in action
Node.js in actionNode.js in action
Node.js in actionSimon Su
 
Europython 2011 - Playing tasks with Django & Celery
Europython 2011 - Playing tasks with Django & CeleryEuropython 2011 - Playing tasks with Django & Celery
Europython 2011 - Playing tasks with Django & CeleryMauro Rocco
 
Flexviews materialized views for my sql
Flexviews materialized views for my sqlFlexviews materialized views for my sql
Flexviews materialized views for my sqlJustin Swanhart
 
Taking a Test Drive
Taking a Test DriveTaking a Test Drive
Taking a Test DriveGraham Lee
 
Asynchronous programming done right - Node.js
Asynchronous programming done right - Node.jsAsynchronous programming done right - Node.js
Asynchronous programming done right - Node.jsPiotr Pelczar
 
"Swoole: double troubles in c", Alexandr Vronskiy
"Swoole: double troubles in c", Alexandr Vronskiy"Swoole: double troubles in c", Alexandr Vronskiy
"Swoole: double troubles in c", Alexandr VronskiyFwdays
 
Guide to Node.js: Basic to Advanced
Guide to Node.js: Basic to AdvancedGuide to Node.js: Basic to Advanced
Guide to Node.js: Basic to AdvancedEspeo Software
 
Why Redux-Observable?
Why Redux-Observable?Why Redux-Observable?
Why Redux-Observable?Anna Su
 
NetApp ontap simulator
NetApp ontap simulatorNetApp ontap simulator
NetApp ontap simulatorAshwin Pawar
 
Jeroen Vloothuis Bend Kss To Your Will
Jeroen Vloothuis   Bend Kss To Your WillJeroen Vloothuis   Bend Kss To Your Will
Jeroen Vloothuis Bend Kss To Your WillVincenzo Barone
 
Performance measurement and tuning
Performance measurement and tuningPerformance measurement and tuning
Performance measurement and tuningAOE
 
"The little big project. From zero to hero in two weeks with 3 front-end engi...
"The little big project. From zero to hero in two weeks with 3 front-end engi..."The little big project. From zero to hero in two weeks with 3 front-end engi...
"The little big project. From zero to hero in two weeks with 3 front-end engi...Fwdays
 
BATTLESTAR GALACTICA : Saison 5 - Les Cylons passent dans le cloud avec Vert....
BATTLESTAR GALACTICA : Saison 5 - Les Cylons passent dans le cloud avec Vert....BATTLESTAR GALACTICA : Saison 5 - Les Cylons passent dans le cloud avec Vert....
BATTLESTAR GALACTICA : Saison 5 - Les Cylons passent dans le cloud avec Vert....La Cuisine du Web
 

Tendances (20)

Ansible loves Python, Python Philadelphia meetup
Ansible loves Python, Python Philadelphia meetupAnsible loves Python, Python Philadelphia meetup
Ansible loves Python, Python Philadelphia meetup
 
Nginx and friends - putting a turbo button on your site
Nginx and friends - putting a turbo button on your siteNginx and friends - putting a turbo button on your site
Nginx and friends - putting a turbo button on your site
 
Find bottleneck and tuning in Java Application
Find bottleneck and tuning in Java ApplicationFind bottleneck and tuning in Java Application
Find bottleneck and tuning in Java Application
 
Future Decoded - Node.js per sviluppatori .NET
Future Decoded - Node.js per sviluppatori .NETFuture Decoded - Node.js per sviluppatori .NET
Future Decoded - Node.js per sviluppatori .NET
 
Node.js in action
Node.js in actionNode.js in action
Node.js in action
 
Europython 2011 - Playing tasks with Django & Celery
Europython 2011 - Playing tasks with Django & CeleryEuropython 2011 - Playing tasks with Django & Celery
Europython 2011 - Playing tasks with Django & Celery
 
Flexviews materialized views for my sql
Flexviews materialized views for my sqlFlexviews materialized views for my sql
Flexviews materialized views for my sql
 
Taking a Test Drive
Taking a Test DriveTaking a Test Drive
Taking a Test Drive
 
Rxjs marble-testing
Rxjs marble-testingRxjs marble-testing
Rxjs marble-testing
 
Asynchronous programming done right - Node.js
Asynchronous programming done right - Node.jsAsynchronous programming done right - Node.js
Asynchronous programming done right - Node.js
 
Spring 4 - A&BP CC
Spring 4 - A&BP CCSpring 4 - A&BP CC
Spring 4 - A&BP CC
 
"Swoole: double troubles in c", Alexandr Vronskiy
"Swoole: double troubles in c", Alexandr Vronskiy"Swoole: double troubles in c", Alexandr Vronskiy
"Swoole: double troubles in c", Alexandr Vronskiy
 
Guide to Node.js: Basic to Advanced
Guide to Node.js: Basic to AdvancedGuide to Node.js: Basic to Advanced
Guide to Node.js: Basic to Advanced
 
Why Redux-Observable?
Why Redux-Observable?Why Redux-Observable?
Why Redux-Observable?
 
NetApp ontap simulator
NetApp ontap simulatorNetApp ontap simulator
NetApp ontap simulator
 
Jeroen Vloothuis Bend Kss To Your Will
Jeroen Vloothuis   Bend Kss To Your WillJeroen Vloothuis   Bend Kss To Your Will
Jeroen Vloothuis Bend Kss To Your Will
 
Performance measurement and tuning
Performance measurement and tuningPerformance measurement and tuning
Performance measurement and tuning
 
Celery
CeleryCelery
Celery
 
"The little big project. From zero to hero in two weeks with 3 front-end engi...
"The little big project. From zero to hero in two weeks with 3 front-end engi..."The little big project. From zero to hero in two weeks with 3 front-end engi...
"The little big project. From zero to hero in two weeks with 3 front-end engi...
 
BATTLESTAR GALACTICA : Saison 5 - Les Cylons passent dans le cloud avec Vert....
BATTLESTAR GALACTICA : Saison 5 - Les Cylons passent dans le cloud avec Vert....BATTLESTAR GALACTICA : Saison 5 - Les Cylons passent dans le cloud avec Vert....
BATTLESTAR GALACTICA : Saison 5 - Les Cylons passent dans le cloud avec Vert....
 

Similaire à [W3C HTML5 2016] Angular + ES6

Solving anything in VCL
Solving anything in VCLSolving anything in VCL
Solving anything in VCLFastly
 
using Mithril.js + postgREST to build and consume API's
using Mithril.js + postgREST to build and consume API'susing Mithril.js + postgREST to build and consume API's
using Mithril.js + postgREST to build and consume API'sAntônio Roberto Silva
 
Live deployment, ci, drupal
Live deployment, ci, drupalLive deployment, ci, drupal
Live deployment, ci, drupalAndrii Podanenko
 
Building and deploying React applications
Building and deploying React applicationsBuilding and deploying React applications
Building and deploying React applicationsAstrails
 
Ch ch-changes cake php2
Ch ch-changes cake php2Ch ch-changes cake php2
Ch ch-changes cake php2markstory
 
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)Igor Bronovskyy
 
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 202010 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020Matt Raible
 
Future of Web Apps: Google Gears
Future of Web Apps: Google GearsFuture of Web Apps: Google Gears
Future of Web Apps: Google Gearsdion
 
(DEV204) Building High-Performance Native Cloud Apps In C++
(DEV204) Building High-Performance Native Cloud Apps In C++(DEV204) Building High-Performance Native Cloud Apps In C++
(DEV204) Building High-Performance Native Cloud Apps In C++Amazon Web Services
 
Bonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsBonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsFrancois Zaninotto
 
Patterns Are Good For Managers
Patterns Are Good For ManagersPatterns Are Good For Managers
Patterns Are Good For ManagersAgileThought
 
Angular JS deep dive
Angular JS deep diveAngular JS deep dive
Angular JS deep diveAxilis
 
Railsconf2011 deployment tips_for_slideshare
Railsconf2011 deployment tips_for_slideshareRailsconf2011 deployment tips_for_slideshare
Railsconf2011 deployment tips_for_slidesharetomcopeland
 
Real World Lessons on the Pain Points of Node.JS Application
Real World Lessons on the Pain Points of Node.JS ApplicationReal World Lessons on the Pain Points of Node.JS Application
Real World Lessons on the Pain Points of Node.JS ApplicationBen Hall
 
Android Best Practices
Android Best PracticesAndroid Best Practices
Android Best PracticesYekmer Simsek
 
Developing web-apps like it's 2013
Developing web-apps like it's 2013Developing web-apps like it's 2013
Developing web-apps like it's 2013Laurent_VB
 
MinbilDinbil Django Speed Tricks
MinbilDinbil Django Speed TricksMinbilDinbil Django Speed Tricks
MinbilDinbil Django Speed TricksLorenzo Setale
 
Slaven tomac unit testing in angular js
Slaven tomac   unit testing in angular jsSlaven tomac   unit testing in angular js
Slaven tomac unit testing in angular jsSlaven Tomac
 

Similaire à [W3C HTML5 2016] Angular + ES6 (20)

Solving anything in VCL
Solving anything in VCLSolving anything in VCL
Solving anything in VCL
 
using Mithril.js + postgREST to build and consume API's
using Mithril.js + postgREST to build and consume API'susing Mithril.js + postgREST to build and consume API's
using Mithril.js + postgREST to build and consume API's
 
Live deployment, ci, drupal
Live deployment, ci, drupalLive deployment, ci, drupal
Live deployment, ci, drupal
 
Building and deploying React applications
Building and deploying React applicationsBuilding and deploying React applications
Building and deploying React applications
 
Ch ch-changes cake php2
Ch ch-changes cake php2Ch ch-changes cake php2
Ch ch-changes cake php2
 
Capistrano
CapistranoCapistrano
Capistrano
 
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
 
Introduction to angular js
Introduction to angular jsIntroduction to angular js
Introduction to angular js
 
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 202010 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020
10 Excellent Ways to Secure Spring Boot Applications - Okta Webinar 2020
 
Future of Web Apps: Google Gears
Future of Web Apps: Google GearsFuture of Web Apps: Google Gears
Future of Web Apps: Google Gears
 
(DEV204) Building High-Performance Native Cloud Apps In C++
(DEV204) Building High-Performance Native Cloud Apps In C++(DEV204) Building High-Performance Native Cloud Apps In C++
(DEV204) Building High-Performance Native Cloud Apps In C++
 
Bonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsBonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node js
 
Patterns Are Good For Managers
Patterns Are Good For ManagersPatterns Are Good For Managers
Patterns Are Good For Managers
 
Angular JS deep dive
Angular JS deep diveAngular JS deep dive
Angular JS deep dive
 
Railsconf2011 deployment tips_for_slideshare
Railsconf2011 deployment tips_for_slideshareRailsconf2011 deployment tips_for_slideshare
Railsconf2011 deployment tips_for_slideshare
 
Real World Lessons on the Pain Points of Node.JS Application
Real World Lessons on the Pain Points of Node.JS ApplicationReal World Lessons on the Pain Points of Node.JS Application
Real World Lessons on the Pain Points of Node.JS Application
 
Android Best Practices
Android Best PracticesAndroid Best Practices
Android Best Practices
 
Developing web-apps like it's 2013
Developing web-apps like it's 2013Developing web-apps like it's 2013
Developing web-apps like it's 2013
 
MinbilDinbil Django Speed Tricks
MinbilDinbil Django Speed TricksMinbilDinbil Django Speed Tricks
MinbilDinbil Django Speed Tricks
 
Slaven tomac unit testing in angular js
Slaven tomac   unit testing in angular jsSlaven tomac   unit testing in angular js
Slaven tomac unit testing in angular js
 

Plus de 양재동 코드랩

T12_1_김나람_웹 기술로 구축하는 모바일 애플리케이션 - React Native
T12_1_김나람_웹 기술로 구축하는 모바일 애플리케이션 - React NativeT12_1_김나람_웹 기술로 구축하는 모바일 애플리케이션 - React Native
T12_1_김나람_웹 기술로 구축하는 모바일 애플리케이션 - React Native양재동 코드랩
 
T13_2_이은호_비개발자 대표의 3개월 서비스 개발기
T13_2_이은호_비개발자 대표의 3개월 서비스 개발기T13_2_이은호_비개발자 대표의 3개월 서비스 개발기
T13_2_이은호_비개발자 대표의 3개월 서비스 개발기양재동 코드랩
 
T11_1_한종원_20181126 AWS S3, SPA, 그리고 Vue.JS - HBSmith는 어떻게 Fron...
T11_1_한종원_20181126 AWS S3, SPA, 그리고 Vue.JS - HBSmith는 어떻게 Fron...T11_1_한종원_20181126 AWS S3, SPA, 그리고 Vue.JS - HBSmith는 어떻게 Fron...
T11_1_한종원_20181126 AWS S3, SPA, 그리고 Vue.JS - HBSmith는 어떻게 Fron...양재동 코드랩
 
T13_1_김건_오픈소스 컨트리뷰션 101
T13_1_김건_오픈소스 컨트리뷰션 101T13_1_김건_오픈소스 컨트리뷰션 101
T13_1_김건_오픈소스 컨트리뷰션 101양재동 코드랩
 
T11-2 장기효_Progressive Web Apps - 미래가 아닌 현재
T11-2 장기효_Progressive Web Apps - 미래가 아닌 현재T11-2 장기효_Progressive Web Apps - 미래가 아닌 현재
T11-2 장기효_Progressive Web Apps - 미래가 아닌 현재양재동 코드랩
 
[Codelab 2017] Ionic Framework을 통한 하이브리드앱 개발하기
[Codelab 2017] Ionic Framework을 통한 하이브리드앱 개발하기[Codelab 2017] Ionic Framework을 통한 하이브리드앱 개발하기
[Codelab 2017] Ionic Framework을 통한 하이브리드앱 개발하기양재동 코드랩
 
[Codelab 2017] Docker 기초 및 활용 방안
[Codelab 2017] Docker 기초 및 활용 방안[Codelab 2017] Docker 기초 및 활용 방안
[Codelab 2017] Docker 기초 및 활용 방안양재동 코드랩
 
[W3C HTML5 2016] Univeral Rendering
[W3C HTML5 2016] Univeral Rendering[W3C HTML5 2016] Univeral Rendering
[W3C HTML5 2016] Univeral Rendering양재동 코드랩
 
[W3C HTML5 2016] Ionic 하이브리드 앱 개발하기, 사례와 시사점
[W3C HTML5 2016] Ionic 하이브리드 앱 개발하기, 사례와 시사점[W3C HTML5 2016] Ionic 하이브리드 앱 개발하기, 사례와 시사점
[W3C HTML5 2016] Ionic 하이브리드 앱 개발하기, 사례와 시사점양재동 코드랩
 
[W3C HTML5 2016] 컨테이너와 웹 어플리케이션
[W3C HTML5 2016] 컨테이너와 웹 어플리케이션[W3C HTML5 2016] 컨테이너와 웹 어플리케이션
[W3C HTML5 2016] 컨테이너와 웹 어플리케이션양재동 코드랩
 
[W3C HTML5 2016] 일렉트론, 웹 기술로 담아내는 데스크탑 애플리케이션
[W3C HTML5 2016] 일렉트론, 웹 기술로 담아내는 데스크탑 애플리케이션[W3C HTML5 2016] 일렉트론, 웹 기술로 담아내는 데스크탑 애플리케이션
[W3C HTML5 2016] 일렉트론, 웹 기술로 담아내는 데스크탑 애플리케이션양재동 코드랩
 
[W3C HTML5 2017] Electron과 TypeScript로 만드는 Visual Studio Code, 그리고 ProtoPie
[W3C HTML5 2017] Electron과 TypeScript로 만드는 Visual Studio Code, 그리고 ProtoPie[W3C HTML5 2017] Electron과 TypeScript로 만드는 Visual Studio Code, 그리고 ProtoPie
[W3C HTML5 2017] Electron과 TypeScript로 만드는 Visual Studio Code, 그리고 ProtoPie양재동 코드랩
 
[W3C HTML5 2017] Docker & DevOps에서 Serverless & NoOps로의 여정
[W3C HTML5 2017] Docker & DevOps에서 Serverless & NoOps로의 여정[W3C HTML5 2017] Docker & DevOps에서 Serverless & NoOps로의 여정
[W3C HTML5 2017] Docker & DevOps에서 Serverless & NoOps로의 여정양재동 코드랩
 
[W3C HTML5 2017] 예제를 통해 쉽게 살펴보는 Vue.js
[W3C HTML5 2017] 예제를 통해 쉽게 살펴보는 Vue.js [W3C HTML5 2017] 예제를 통해 쉽게 살펴보는 Vue.js
[W3C HTML5 2017] 예제를 통해 쉽게 살펴보는 Vue.js 양재동 코드랩
 

Plus de 양재동 코드랩 (15)

T12_1_김나람_웹 기술로 구축하는 모바일 애플리케이션 - React Native
T12_1_김나람_웹 기술로 구축하는 모바일 애플리케이션 - React NativeT12_1_김나람_웹 기술로 구축하는 모바일 애플리케이션 - React Native
T12_1_김나람_웹 기술로 구축하는 모바일 애플리케이션 - React Native
 
T13_2_이은호_비개발자 대표의 3개월 서비스 개발기
T13_2_이은호_비개발자 대표의 3개월 서비스 개발기T13_2_이은호_비개발자 대표의 3개월 서비스 개발기
T13_2_이은호_비개발자 대표의 3개월 서비스 개발기
 
T11_1_한종원_20181126 AWS S3, SPA, 그리고 Vue.JS - HBSmith는 어떻게 Fron...
T11_1_한종원_20181126 AWS S3, SPA, 그리고 Vue.JS - HBSmith는 어떻게 Fron...T11_1_한종원_20181126 AWS S3, SPA, 그리고 Vue.JS - HBSmith는 어떻게 Fron...
T11_1_한종원_20181126 AWS S3, SPA, 그리고 Vue.JS - HBSmith는 어떻게 Fron...
 
T13_1_김건_오픈소스 컨트리뷰션 101
T13_1_김건_오픈소스 컨트리뷰션 101T13_1_김건_오픈소스 컨트리뷰션 101
T13_1_김건_오픈소스 컨트리뷰션 101
 
T11-2 장기효_Progressive Web Apps - 미래가 아닌 현재
T11-2 장기효_Progressive Web Apps - 미래가 아닌 현재T11-2 장기효_Progressive Web Apps - 미래가 아닌 현재
T11-2 장기효_Progressive Web Apps - 미래가 아닌 현재
 
[Codelab 2017] ReactJS 기초
[Codelab 2017] ReactJS 기초[Codelab 2017] ReactJS 기초
[Codelab 2017] ReactJS 기초
 
[Codelab 2017] Ionic Framework을 통한 하이브리드앱 개발하기
[Codelab 2017] Ionic Framework을 통한 하이브리드앱 개발하기[Codelab 2017] Ionic Framework을 통한 하이브리드앱 개발하기
[Codelab 2017] Ionic Framework을 통한 하이브리드앱 개발하기
 
[Codelab 2017] Docker 기초 및 활용 방안
[Codelab 2017] Docker 기초 및 활용 방안[Codelab 2017] Docker 기초 및 활용 방안
[Codelab 2017] Docker 기초 및 활용 방안
 
[W3C HTML5 2016] Univeral Rendering
[W3C HTML5 2016] Univeral Rendering[W3C HTML5 2016] Univeral Rendering
[W3C HTML5 2016] Univeral Rendering
 
[W3C HTML5 2016] Ionic 하이브리드 앱 개발하기, 사례와 시사점
[W3C HTML5 2016] Ionic 하이브리드 앱 개발하기, 사례와 시사점[W3C HTML5 2016] Ionic 하이브리드 앱 개발하기, 사례와 시사점
[W3C HTML5 2016] Ionic 하이브리드 앱 개발하기, 사례와 시사점
 
[W3C HTML5 2016] 컨테이너와 웹 어플리케이션
[W3C HTML5 2016] 컨테이너와 웹 어플리케이션[W3C HTML5 2016] 컨테이너와 웹 어플리케이션
[W3C HTML5 2016] 컨테이너와 웹 어플리케이션
 
[W3C HTML5 2016] 일렉트론, 웹 기술로 담아내는 데스크탑 애플리케이션
[W3C HTML5 2016] 일렉트론, 웹 기술로 담아내는 데스크탑 애플리케이션[W3C HTML5 2016] 일렉트론, 웹 기술로 담아내는 데스크탑 애플리케이션
[W3C HTML5 2016] 일렉트론, 웹 기술로 담아내는 데스크탑 애플리케이션
 
[W3C HTML5 2017] Electron과 TypeScript로 만드는 Visual Studio Code, 그리고 ProtoPie
[W3C HTML5 2017] Electron과 TypeScript로 만드는 Visual Studio Code, 그리고 ProtoPie[W3C HTML5 2017] Electron과 TypeScript로 만드는 Visual Studio Code, 그리고 ProtoPie
[W3C HTML5 2017] Electron과 TypeScript로 만드는 Visual Studio Code, 그리고 ProtoPie
 
[W3C HTML5 2017] Docker & DevOps에서 Serverless & NoOps로의 여정
[W3C HTML5 2017] Docker & DevOps에서 Serverless & NoOps로의 여정[W3C HTML5 2017] Docker & DevOps에서 Serverless & NoOps로의 여정
[W3C HTML5 2017] Docker & DevOps에서 Serverless & NoOps로의 여정
 
[W3C HTML5 2017] 예제를 통해 쉽게 살펴보는 Vue.js
[W3C HTML5 2017] 예제를 통해 쉽게 살펴보는 Vue.js [W3C HTML5 2017] 예제를 통해 쉽게 살펴보는 Vue.js
[W3C HTML5 2017] 예제를 통해 쉽게 살펴보는 Vue.js
 

Dernier

Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsJoaquim Jorge
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUK Journal
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024The Digital Insurer
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century educationjfdjdjcjdnsjd
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Scriptwesley chun
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilV3cube
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoffsammart93
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProduct Anonymous
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfsudhanshuwaghmare1
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 

Dernier (20)

Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of Brazil
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 

[W3C HTML5 2016] Angular + ES6

  • 2. • 전 삼성SDS 선임 • TV플랫폼 JavaScript 어플리케이션 구현 • 리테일 솔루션 서버 & 프론트엔드 구현 • 프리랜서 개발자 • han41858.tistory.com 블로그 운영 • Angular 2 번역서 집필중 • GDG Korea Web Tech 운영진 한장현 (Janghyun Han) 2
  • 3. 3
  • 5. • 마술 같은 2-way binding • HTML 표준을 기반으로 기능 확장 • 체계적인 컴포넌트, Web Component로 가는 길 • Front-end 전체를 커버하는 프레임워크 • Google 에서 관리, 개선 • 풍부한 사용자 삽질 경험 Angular를 쓰는 이유 5
  • 7. • 간단해지는 코드, syntax sugar • 클래스, 상속, 모듈 구성 ⇒ 아키텍처 진화 • 브라우저들 지원 계속 • 결국엔 표준, transpiler는 거쳐갈 뿐 ES6를 쓰는 이유 7
  • 8. 8
  • 9. ES6ES5 script src <script src="bundle.js"></script> "devDependencies": { "angular": "^1.5.8", "babel-cli": "^6.18.0", "babel-loader": "^6.2.7", "babel-preset-es2015": "^6.18.0", "webpack": "^1.13.3" } <script src="angular.js"></script> <script src="index.js"></script> "devDependencies": { "angular": "^1.5.8" } 9
  • 10. index.js ES6ES5 (function () { var ngApp = angular.module('angular1es5', []); })(); import angular from 'angular'; (() => { const ngApp = angular.module('angular1es6', []); })(); 10
  • 11. Webpack // this is ES5 var webpack = require('webpack'); module.exports = { entry : [ './index.js' ], output : { filename : 'build/bundle.js', sourceMapFilename: '[name].map' }, module : { loaders : [ { test : /.js$/, loader : 'babel?presets[]=es2015 exclude : /node_modules/ }, { test : /.pug$/, loader : 'pug-loader', exclude : /node_modules/ } ] }, plugins : [ // new webpack.optimize.UglifyJsPlugin({minimize: true}) ] }; webpack.config.js "devDependencies": { "angular": "^1.5.8", "angular-route": "^1.5.8", "babel-cli": "^6.18.0", "babel-loader": "^6.2.7", "babel-preset-es2015": "^6.18.0", "file-loader": "^0.9.0", "pug": "^2.0.0-beta6", "pug-loader": "^2.3.0", "webpack": "^1.13.3", "webpack-dev-server": "^1.16.2" } package.json 11
  • 12. export default class HomeCtrl { constructor () { console.log('HomeCtrl.constructor()'); } } /view/homeCtrl.js p this is home /view/home.pug import angular from 'angular'; import ngRoute from 'angular-route'; import HomeCtrl from './view/homeCtrl'; const main = () => { console.log('main()'); const ngApp = angular.module('angular1es6', ['ngRoute']); ngApp.config(($routeProvider, $locationProvider) => { console.log('this is angular config'); $routeProvider .when('/', { template : require('./view/home.pug'), controller : 'HomeCtrl', controllerAs : 'Ctrl' }) .otherwise({ redirectTo : '/' }); // need to angular.js routing $locationProvider.html5Mode({ enabled : true, requireBase : false }); }); ngApp.controller('HomeCtrl', HomeCtrl); }; main(); index.js + ngRoute 12
  • 14. ngApp.directive('CustomDirective', () => new CustomDirective); ngApp.filter('groupBy', GroupBy); ngApp.service('CustomSvc', CustomSvc); ngApp.controller('CustomCtrl', CustomCtrl); Angular Components ngApp.directive('CustomDirective', CustomDirective); ngApp.filter('groupBy', GroupBy); ngApp.service('CustomSvc', CustomSvc); ngApp.controller('CustomCtrl', CustomCtrl); function Class new Class 14
  • 15. Filter ES6ES5 ngApp.filter('uppercase', uppercase); function uppercase () { return function (item) { return item.toUpperCase(); }; } <script src="uppercase.filter.js"></script> const uppercase = () => { return (input) => { return input.toUpperCase(); }; }; export default uppercase; import uppercase from './uppercase.filter'; ngApp.filter('uppercase', uppercase); 15
  • 16. .ctrlRoot p this is home ctrl p {{ Ctrl.title }} button(ng-click="Ctrl.select()") Select export default class HomeCtrl { constructor () { console.log('HomeCtrl.constructor()'); this.title = 'this is title'; } select () { console.log('HomeCtrl.select()'); } } $routeProvider .when('/', { template : require('./view/home.pug'), controller : 'HomeCtrl', controllerAs : 'Ctrl' }) ngApp.controller('HomeCtrl', HomeCtrl); function HomeCtrl ($scope) { console.log('home controller'); $scope.title = 'this is title'; $scope.select = function () { console.log('HomeCtrl.select()'); } } Controller ES6ES5 .ctrlRoot p this is home ctrl p {{ title }} button(ng-click="select()") Select 16
  • 17. Service ES6ES5 ngApp.service('myService', myService); <script src="./view/myService.js"></script> function myService () { this.testFnc = function () { console.log('myService.testFnc()'); }; return this; } export default class MyService { constructor () { console.log('MyService'); } testFnc () { console.log('MyService.testFnc()'); } } ngApp.service('MyService', MyService); export default class HomeCtrl { constructor (MyService) { this.MyService = MyService; } select () { this.MyService.testFnc(); } } static 있어야 할 것 같지만 없어야 함 17
  • 18. ngApp.directive('myDirective', () => new MyDirective); export default class MyDirective { constructor () { console.log('MyDirective.constructor()'); this.restrict = 'E'; this.template = '<p>message : {{ this.msg }}</p>'; this.scope = { msg : '@' }; } link (scope) { console.log(scope.msg); } } ngApp.directive('myDirective', myDirective); function myDirective () { return { restrict : 'E', template : '<p>message : {{ msg }}</p>', scope : { msg : '@' }, controller : function ($scope) { console.log('myDirective.controller()'); console.log($scope.msg); } } } <script src="myDirective.js"></script> Directive ES6ES5 directive 등록할 때 () => new18
  • 20. Directive vs. Component const customInput = { bindings : { model : '=' }, template : '<input ng-model="$ctrl.model"></input>', controller : function () { this.$onInit = () => { } } }; export default customInput; ngApp.component('customInput', customInput);ngApp.directive('customInput', () => new customInput); export default class customInput { constructor () { this.restrict = 'E'; this.scope = { model : '=' }; this.template = '<input ng-model="model"></input>'; } controller () { } } 20
  • 21. static delete (param) { const self = this; return util.objValidate(param, { userID : Constants.TYPE.EMAIL }, Constants.ERROR.USER_CTRL.NO_PARAMETER, log, 'delete()') .then(param => self.isExists(param)) .then(param => { // delete records return recordCtrl.deleteAll({ userID : param.userID }) .then(() => { log('remove records ok'); // param 자체를 다시 돌려주기 위해 Promise 필요 return Promise.resolve(param); }); }) .then(param => { // delete cards return cardCtrl.deleteAll({ userID : param.userID }) .then(() => { log('remove cards ok'); return Promise.resolve(param); }); }) .then(param => { // delete assets return assetCtrl.deleteAll({ userID : param.userID }) .then(() => { log('remove assets ok'); return Promise.resolve(param); }); }) .then(param => { // delete user return User.remove({ userID : param.userID }) .then(() => { return Promise.resolve(param); }, error => { log(error); return Promise.reject(new ERROR(Constants.ERROR.MONGOOSE.REMOVE_FAILED, log, 'delete()')); }); }) .then(param => { log(`delete ok : ${param.userID}`); return Promise.resolve(true); }); } Promise 21
  • 22. ngApp.config(($routeProvider, $locationProvider) => { // include styles require('./view/home.styl'); $routeProvider .when('/', { template : require('./view/home.pug'), controller : 'HomeCtrl', controllerAs : 'Ctrl' }) .otherwise({ redirectTo : '/' }); }); 배포 : webpack import가 아니므로 require 22
  • 23. Angular 1 + ES6 + BDD = Hell 23
  • 24. describe('homeCtrl.test', () => { it('module import', () => { expect(true).to.be.true; }); }); λ karma start (node:7564) DeprecationWarning: Using Buffer without `new` will soon stop working. Use `new Buffer()`, or preferably `Buffer.from()`, `Buffer.allocUnsafe()` or `Buffer.alloc()` instead. 18 11 2016 02:53:45.852:INFO [framework.browserify]: bundle built 18 11 2016 02:53:45.941:INFO [karma]: Karma v1.3.0 server started at http://localhost:9876/ 18 11 2016 02:53:45.941:INFO [launcher]: Launching browser Chrome with unlimited concurrency 18 11 2016 02:53:45.952:INFO [launcher]: Starting browser Chrome 18 11 2016 02:53:47.283:INFO [Chrome 54.0.2840 (Windows 10 0.0.0)]: Connected on socket /#AjAqCwTlrwmVmV_sAAAA with id 16857313 Chrome 54.0.2840 (Windows 10 0.0.0): Executed 1 of 1 SUCCESS (0.005 secs / 0.001 secs) BDD 시작 24
  • 25. λ karma start (node:12196) DeprecationWarning: Using Buffer without `new` will soon stop working. Use `new Buffer()`, or preferably `Buffer.from()`, `Buffer.allocUnsafe()` or `Buffer.alloc()` instead. 18 11 2016 02:19:10.237:INFO [framework.browserify]: bundle built 18 11 2016 02:19:10.343:INFO [karma]: Karma v1.3.0 server started at http://localhost:9876/ 18 11 2016 02:19:10.343:INFO [launcher]: Launching browser Chrome with unlimited concurrency 18 11 2016 02:19:10.353:INFO [launcher]: Starting browser Chrome 18 11 2016 02:19:11.676:INFO [Chrome 54.0.2840 (Windows 10 0.0.0)]: Connected on socket /#sBpP4RL0XFZAwPtxAAAA with id 52822107 Chrome 54.0.2840 (Windows 10 0.0.0) ERROR Uncaught SyntaxError: Unexpected token import at test/homeCtrl.test.js:1 import HomeCtrl from '../view/homeCtrl'; describe('homeCtrl.test', () => { it('module import', () => { console.log(HomeCtrl); expect(true).to.be.true; expect(HomeCtrl).to.be.ok; }); }); λ karma start (node:11580) DeprecationWarning: Using Buffer without `new` will soon stop working. Use `new Buffer()`, or preferably `Buffer.from()`, `Buffer.allocUnsafe()` or `Buffer.alloc()` instead. 18 11 2016 02:31:24.248:INFO [framework.browserify]: bundle built 18 11 2016 02:31:24.339:INFO [karma]: Karma v1.3.0 server started at http://localhost:9876/ 18 11 2016 02:31:24.339:INFO [launcher]: Launching browser Chrome with unlimited concurrency 18 11 2016 02:31:24.349:INFO [launcher]: Starting browser Chrome 18 11 2016 02:31:25.657:INFO [Chrome 54.0.2840 (Windows 10 0.0.0)]: Connected on socket /#pvpyGrXqq2TZPTgmAAAA with id 30236974 LOG: class HomeCtrl { ... } Chrome 54.0.2840 (Windows 10 0.0.0): Executed 1 of 1 SUCCESS (0.007 secs / 0.002 secs) babel : node6 λ karma start (node:13196) DeprecationWarning: Using Buffer without `new` will soon stop working. Use `new Buffer()`, or preferably `Buffer.from()`, `Buffer.allocUnsafe()` or `Buffer.alloc()` instead. 18 11 2016 02:58:38.638:INFO [framework.browserify]: bundle built 18 11 2016 02:58:38.728:INFO [karma]: Karma v1.3.0 server started at http://localhost:9876/ 18 11 2016 02:58:38.729:INFO [launcher]: Launching browser PhantomJS with unlimited concurrency 18 11 2016 02:58:38.738:INFO [launcher]: Starting browser PhantomJS 18 11 2016 02:58:40.301:INFO [PhantomJS 2.1.1 (Windows 8 0.0.0)]: Connected on socket /#JeRwavdozVZCC8HJAAAA with id 75347319 LOG: function HomeCtrl(MyService) { ... } PhantomJS 2.1.1 (Windows 8 0.0.0): Executed 1 of 1 SUCCESS (0.008 secs / 0.001 secs) babel : es2015 ES6가 돌지 않는다… + 25
  • 26. var $httpBackend; beforeEach(function () { module('angular1es5'); inject(function (_$httpBackend_) { $httpBackend = _$httpBackend_; }); }); module 선언, injection 불가 ES6ES5 const $injector, $httpBackend; beforeEach(() => { $injector = angular.injector(['angular1es6']); $httpBackend = $injector.get('$httpBackend'); }); Object is not a constructor (evaluating 'module('angular1es6')') r:/temp/test/homeCtrl.test.js:15:9 <- R:/temp/3871fde1c6cf6c302eeae7add18a3b02.browserify:22:9 26
  • 27. ngMock vs. ngMockE2E The ngMock module provides support to inject and mock Angular services into unit tests. In addition, ngMock also extends various core ng services such that they can be inspected and controlled in a synchronous manner within test code. The ngMockE2E is an angular module which contains mocks suitable for end-to-end testing. Currently there is only one mock present in this module - the e2e $httpBackend mock. ngMock ngMockE2E Fake HTTP backend implementation suitable for end-to- end testing or backend-less development of applications that use the $http service. Fake HTTP backend implementation suitable for unit testing applications that use the $http service. .when() .expect() .flush() .verifyNoOutstandingExpectation() .verifyNoOutstandingRequest() .resetExpectations() .when() ngMockE2E에는 $location 없음 27
  • 28. Promise + http.flush() Chrome 54.0.2840 (Windows 10 0.0.0) homeCtrl.test http test ok FAILED Error: No pending request to flush ! at Function.$httpBackend.flush (node_modules/angular-mocks/angular-mocks.js:1799:34) at r:/temp/test/homeCtrl.test.js:36:17 promise 함수로 부르면 동작 안함 flush() 타이밍 달라짐 flush는 Util에서 수행 const $injector = angular.injector(['angular1es6']); const $httpBackend = $injector.get('$httpBackend'); const $http = $injector.get('$http'); beforeEach(() => { $httpBackend.expectGET('/test') .respond(['this', 'is', 'GET', 'test', 'data']); }); it('ok', () => { return new Promise(resolve => { $http.get('/test').then(result => { console.log('get().then()'); console.log(result); console.log(result.data); resolve(true); }); $httpBackend.flush(); }); }); 동작하는 코드 static post (uri, param, config) { // use new promise for flush() return new Promise((resolve, reject) => { this.http.post(uri, param, config) .then(result => { resolve(result); }, error => { console.error(error); reject(false); }); if (this.httpBackend && this.httpBackend.flush) { this.httpBackend.flush(); } }); } it('ok', () => { return new Promise(resolve => { Promise.resolve() .then(() => { $http.get('/test').then(result => { console.log('get().then()'); console.log(result); console.log(result.data); resolve(true); }); }); $httpBackend.flush(); }); }); 28
  • 29. 여러 test 파일 동시 실행 const $injector = angular.injector(['angular1es6']); const $httpBackend = $injector.get('$httpBackend'); const $http = $injector.get('$http'); Chrome 54.0.2840 (Windows 10 0.0.0) SignInCtrl test logic submit() with ajax error response response error test FAILED AssertionError: expected [Error: Unexpected request: POST /api/user/signIn No more request expected] to be an instance of ERROR Chrome 54.0.2840 (Windows 10 0.0.0) SignUpCtrl test logic submit() error response response error test FAILED AssertionError: expected [Error: [$rootScope:inprog] $digest already in progress const ngApp = angular.module(appName, ['ngMock']); // working with $location, but not $httpBackend.whenPOST()... var $injector = angular.injector(['MoneyBook']); const $httpBackend = $injector.get('$httpBackend'); const $http = $injector.get('$http'); ClientUtil.http = $http; ClientUtil.httpBackend = $httpBackend; 29
  • 30. 생성자 안에서 Promise export default class HomeCtrl { constructor () { console.log('HomeCtrl.constructor()'); Promise.resolve(() => { // do something }); } } describe('constructor()', () => { it('ok', () => { // expect what...? }); }); export default class HomeCtrl { constructor () { console.log('HomeCtrl.constructor()'); this.somethingPromise(); } somethingPromise(){ return Promise.resolve() .then() => { // do something }); } } 30
  • 31. Promise + $scope.$apply() export default class HomeCtrl { constructor () { console.log('HomeCtrl.constructor()'); this.count = 0; } select () { console.log('HomeCtrl.select()'); return Promise.resolve() .then(() => { this.count++; }); } } export default class HomeCtrl { constructor ($scope) { console.log('HomeCtrl.constructor()'); this.$scope = $scope; this.count = 0; } select () { console.log('HomeCtrl.select()'); return Promise.resolve() .then(() => { this.count++; this.$scope.$apply(); }); } } 31