diff --git a/modules/angular2/http.ts b/modules/angular2/http.ts index 2eb5112e1f..5491226cfb 100644 --- a/modules/angular2/http.ts +++ b/modules/angular2/http.ts @@ -2,10 +2,14 @@ import {bind, Binding} from 'angular2/di'; import {Http, HttpFactory} from './src/http/http'; import {XHRBackend} from 'angular2/src/http/backends/xhr_backend'; import {BrowserXHR} from 'angular2/src/http/backends/browser_xhr'; +import {BaseRequestOptions} from 'angular2/src/http/base_request_options'; export {Http}; export var httpInjectables: List = [ + bind(BrowserXHR) + .toValue(BrowserXHR), XHRBackend, - bind(BrowserXHR).toValue(BrowserXHR), - bind(Http).toFactory(HttpFactory, [XHRBackend]) + BaseRequestOptions, + bind(HttpFactory).toFactory(HttpFactory, [XHRBackend, BaseRequestOptions]), + Http ]; diff --git a/modules/angular2/src/http/base_request_options.ts b/modules/angular2/src/http/base_request_options.ts index 602865b639..616ca60ad7 100644 --- a/modules/angular2/src/http/base_request_options.ts +++ b/modules/angular2/src/http/base_request_options.ts @@ -6,10 +6,10 @@ import {RequestOptions} from './interfaces'; import {Injectable} from 'angular2/di'; import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection'; -export class RequestOptionsClass { +export class RequestOptionsClass implements RequestOptions { method: RequestMethods = RequestMethods.GET; headers: Headers; - body: URLSearchParams | FormData | string; + body: URLSearchParams | FormData | Blob | string; mode: RequestModesOpts = RequestModesOpts.Cors; credentials: RequestCredentialsOpts; cache: RequestCacheOpts; diff --git a/modules/angular2/src/http/enums.ts b/modules/angular2/src/http/enums.ts index 1e67c48cd6..65bf3644f2 100644 --- a/modules/angular2/src/http/enums.ts +++ b/modules/angular2/src/http/enums.ts @@ -5,7 +5,7 @@ export enum RequestCacheOpts {Default, NoStore, Reload, NoCache, ForceCache, Onl export enum RequestCredentialsOpts {Omit, SameOrigin, Include}; -export enum RequestMethods {GET, POST, PUT, DELETE, OPTIONS, HEAD}; +export enum RequestMethods {GET, POST, PUT, DELETE, OPTIONS, HEAD, PATCH}; export enum ReadyStates {UNSENT, OPEN, HEADERS_RECEIVED, LOADING, DONE, CANCELLED}; diff --git a/modules/angular2/src/http/http.ts b/modules/angular2/src/http/http.ts index b4e1da0423..7d257921a4 100644 --- a/modules/angular2/src/http/http.ts +++ b/modules/angular2/src/http/http.ts @@ -6,6 +6,8 @@ import {Request} from './static_request'; import {Response} from './static_response'; import {XHRBackend} from './backends/xhr_backend'; import {BaseRequestOptions} from './base_request_options'; +import {RequestMethods} from './enums'; +import {URLSearchParams} from './url_search_params'; import * as Rx from 'rx'; /** @@ -43,9 +45,59 @@ import * as Rx from 'rx'; * **/ +function httpRequest(backend: XHRBackend, request: Request) { + return >(Observable.create(observer => { + var connection: Connection = backend.createConnection(request); + var internalSubscription = connection.response.subscribe(observer); + return () => { + internalSubscription.dispose(); + connection.dispose(); + } + })) +} + // Abstract @Injectable() export class Http { + constructor(private backend: XHRBackend, private defaultOptions: BaseRequestOptions) {} + + request(url: string, options?: RequestOptions): Rx.Observable { + return httpRequest(this.backend, new Request(url, this.defaultOptions.merge(options))); + } + + get(url: string, options?: RequestOptions) { + return httpRequest(this.backend, new Request(url, this.defaultOptions.merge(options) + .merge({method: RequestMethods.GET}))); + } + + post(url: string, body: URLSearchParams | FormData | Blob | string, options?: RequestOptions) { + return httpRequest(this.backend, + new Request(url, this.defaultOptions.merge(options) + + .merge({body: body, method: RequestMethods.POST}))); + } + + put(url: string, body: URLSearchParams | FormData | Blob | string, options?: RequestOptions) { + return httpRequest(this.backend, + new Request(url, this.defaultOptions.merge(options) + .merge({body: body, method: RequestMethods.PUT}))); + } + + delete (url: string, options?: RequestOptions) { + return httpRequest(this.backend, new Request(url, this.defaultOptions.merge(options) + .merge({method: RequestMethods.DELETE}))); + } + + patch(url: string, body: URLSearchParams | FormData | Blob | string, options?: RequestOptions) { + return httpRequest(this.backend, + new Request(url, this.defaultOptions.merge(options) + .merge({body: body, method: RequestMethods.PATCH}))); + } + + head(url: string, options?: RequestOptions) { + return httpRequest(this.backend, new Request(url, this.defaultOptions.merge(options) + .merge({method: RequestMethods.HEAD}))); + } } var Observable; @@ -56,13 +108,6 @@ if (Rx.hasOwnProperty('default')) { } export function HttpFactory(backend: XHRBackend, defaultOptions: BaseRequestOptions) { return function(url: string, options?: RequestOptions) { - return >(Observable.create(observer => { - var connection: Connection = backend.createConnection(new Request(url, options)); - var internalSubscription = connection.response.subscribe(observer); - return () => { - internalSubscription.dispose(); - connection.dispose(); - } - })) + return httpRequest(backend, new Request(url, defaultOptions.merge(options))); } } diff --git a/modules/angular2/src/http/interfaces.ts b/modules/angular2/src/http/interfaces.ts index c86e114eed..e082107505 100644 --- a/modules/angular2/src/http/interfaces.ts +++ b/modules/angular2/src/http/interfaces.ts @@ -14,7 +14,7 @@ import {URLSearchParams} from './url_search_params'; export interface RequestOptions { method?: RequestMethods; headers?: Headers; - body?: URLSearchParams | FormData | string; + body?: URLSearchParams | FormData | Blob | string; mode?: RequestModesOpts; credentials?: RequestCredentialsOpts; cache?: RequestCacheOpts; diff --git a/modules/angular2/test/http/base_request_options_spec.ts b/modules/angular2/test/http/base_request_options_spec.ts index a4915f05cc..fe24c0003c 100644 --- a/modules/angular2/test/http/base_request_options_spec.ts +++ b/modules/angular2/test/http/base_request_options_spec.ts @@ -24,8 +24,8 @@ export function main() { it('should retain previously merged values when merging again', () => { var options1 = new BaseRequestOptions(); var options2 = options1.merge({method: RequestMethods.DELETE}); - var options3 = options2.merge({mode: RequestModesOpts.NoCors}) expect(options3.mode) - .toBe(RequestModesOpts.NoCors); + var options3 = options2.merge({mode: RequestModesOpts.NoCors}); + expect(options3.mode).toBe(RequestModesOpts.NoCors); expect(options3.method).toBe(RequestMethods.DELETE); }); }); diff --git a/modules/angular2/test/http/http_spec.ts b/modules/angular2/test/http/http_spec.ts index e3af4b7e37..14153d9942 100644 --- a/modules/angular2/test/http/http_spec.ts +++ b/modules/angular2/test/http/http_spec.ts @@ -12,11 +12,11 @@ import { } from 'angular2/test_lib'; import {Http, HttpFactory} from 'angular2/src/http/http'; import {XHRBackend} from 'angular2/src/http/backends/xhr_backend'; -import {httpInjectables} from 'angular2/http'; import {Injector, bind} from 'angular2/di'; import {MockBackend} from 'angular2/src/http/backends/mock_backend'; import {Response} from 'angular2/src/http/static_response'; -import {ReadyStates} from 'angular2/src/http/enums'; +import {RequestMethods} from 'angular2/src/http/enums'; +import {BaseRequestOptions} from 'angular2/src/http/base_request_options'; class SpyObserver extends SpyObject { onNext: Function; @@ -38,60 +38,222 @@ export function main() { var backend: MockBackend; var baseResponse; var sampleObserver; + var httpFactory; beforeEach(() => { - injector = Injector.resolveAndCreate( - [MockBackend, bind(Http).toFactory(HttpFactory, [MockBackend])]); + injector = Injector.resolveAndCreate([ + BaseRequestOptions, + MockBackend, + bind(XHRBackend).toClass(MockBackend), + bind(HttpFactory).toFactory(HttpFactory, [MockBackend, BaseRequestOptions]), + bind(Http).toFactory( + function(backend: XHRBackend, defaultOptions: BaseRequestOptions) { + return new Http(backend, defaultOptions); + }, + [MockBackend, BaseRequestOptions]) + ]); http = injector.get(Http); + httpFactory = injector.get(HttpFactory); backend = injector.get(MockBackend); baseResponse = new Response('base response'); sampleObserver = new SpyObserver(); }); - afterEach(() => {/*backend.verifyNoPendingRequests();*/}); + afterEach(() => backend.verifyNoPendingRequests()); + + describe('HttpFactory', () => { + it('should return an Observable', () => { + expect(typeof httpFactory(url).subscribe).toBe('function'); + backend.resolveAllConnections(); + }); - it('should return an Observable', () => { - expect(typeof http(url).subscribe).toBe('function'); - backend.resolveAllConnections(); + it('should perform a get request for given url if only passed a string', + inject([AsyncTestCompleter], (async) => { + var connection; + backend.connections.subscribe((c) => connection = c); + var subscription = httpFactory('http://basic.connection') + .subscribe(res => { + expect(res.text()).toBe('base response'); + async.done(); + }); + connection.mockRespond(baseResponse) + })); + + + it('should perform a get request for given url if passed a ConnectionConfig instance', + inject([AsyncTestCompleter], async => { + var connection; + backend.connections.subscribe((c) => connection = c); + httpFactory('http://basic.connection', {method: RequestMethods.GET}) + .subscribe(res => { + expect(res.text()).toBe('base response'); + async.done(); + }); + connection.mockRespond(baseResponse) + })); + + + it('should perform a get request for given url if passed a dictionary', + inject([AsyncTestCompleter], async => { + var connection; + backend.connections.subscribe((c) => connection = c); + httpFactory(url, {method: RequestMethods.GET}) + .subscribe(res => { + expect(res.text()).toBe('base response'); + async.done(); + }); + connection.mockRespond(baseResponse) + })); }); - it('should perform a get request for given url if only passed a string', - inject([AsyncTestCompleter], (async) => { - var connection; - backend.connections.subscribe((c) => connection = c); - var subscription = http('http://basic.connection') - .subscribe(res => { - expect(res.text()).toBe('base response'); - async.done(); - }); - connection.mockRespond(baseResponse) - })); + describe('Http', () => { + it('should return an Observable', () => { + expect(typeof http.request(url).subscribe).toBe('function'); + backend.resolveAllConnections(); + }); - it('should perform a get request for given url if passed a ConnectionConfig instance', - inject([AsyncTestCompleter], async => { - var connection; - backend.connections.subscribe((c) => connection = c); - http('http://basic.connection', {method: ReadyStates.UNSENT}) - .subscribe(res => { - expect(res.text()).toBe('base response'); + it('should perform a get request for given url if only passed a string', + inject([AsyncTestCompleter], (async) => { + var connection; + backend.connections.subscribe((c) => connection = c); + var subscription = http.request('http://basic.connection') + .subscribe(res => { + expect(res.text()).toBe('base response'); + async.done(); + }); + connection.mockRespond(baseResponse) + })); + + + it('should perform a get request for given url if passed a ConnectionConfig instance', + inject([AsyncTestCompleter], async => { + var connection; + backend.connections.subscribe((c) => connection = c); + http.request('http://basic.connection', {method: RequestMethods.GET}) + .subscribe(res => { + expect(res.text()).toBe('base response'); + async.done(); + }); + connection.mockRespond(baseResponse); + })); + + + it('should perform a get request for given url if passed a dictionary', + inject([AsyncTestCompleter], async => { + var connection; + backend.connections.subscribe((c) => connection = c); + http.request(url, {method: RequestMethods.GET}) + .subscribe(res => { + expect(res.text()).toBe('base response'); + async.done(); + }); + connection.mockRespond(baseResponse); + })); + + + describe('.get()', () => { + it('should perform a get request for given url', inject([AsyncTestCompleter], async => { + backend.connections.subscribe((c) => { + expect(c.request.method).toBe(RequestMethods.GET); + backend.resolveAllConnections(); async.done(); }); - connection.mockRespond(baseResponse) - })); + http.get(url).subscribe(res => {}); + })); + }); - it('should perform a get request for given url if passed a dictionary', - inject([AsyncTestCompleter], async => { - var connection; - backend.connections.subscribe((c) => connection = c); - http(url, {method: ReadyStates.UNSENT}) - .subscribe(res => { - expect(res.text()).toBe('base response'); + describe('.post()', () => { + it('should perform a post request for given url', inject([AsyncTestCompleter], async => { + backend.connections.subscribe((c) => { + expect(c.request.method).toBe(RequestMethods.POST); + backend.resolveAllConnections(); async.done(); }); - connection.mockRespond(baseResponse) - })); + http.post(url).subscribe(res => {}); + })); + + + it('should attach the provided body to the request', inject([AsyncTestCompleter], async => { + var body = 'this is my put body'; + backend.connections.subscribe((c) => { + expect(c.request.text()).toBe(body); + backend.resolveAllConnections(); + async.done(); + }); + http.post(url, body).subscribe(res => {}); + })); + }); + + + describe('.put()', () => { + it('should perform a put request for given url', inject([AsyncTestCompleter], async => { + backend.connections.subscribe((c) => { + expect(c.request.method).toBe(RequestMethods.PUT); + backend.resolveAllConnections(); + async.done(); + }); + http.put(url).subscribe(res => {}); + })); + + it('should attach the provided body to the request', inject([AsyncTestCompleter], async => { + var body = 'this is my put body'; + backend.connections.subscribe((c) => { + expect(c.request.text()).toBe(body); + backend.resolveAllConnections(); + async.done(); + }); + http.put(url, body).subscribe(res => {}); + })); + }); + + + describe('.delete()', () => { + it('should perform a delete request for given url', inject([AsyncTestCompleter], async => { + backend.connections.subscribe((c) => { + expect(c.request.method).toBe(RequestMethods.DELETE); + backend.resolveAllConnections(); + async.done(); + }); + http.delete(url).subscribe(res => {}); + })); + }); + + + describe('.patch()', () => { + it('should perform a patch request for given url', inject([AsyncTestCompleter], async => { + backend.connections.subscribe((c) => { + expect(c.request.method).toBe(RequestMethods.PATCH); + backend.resolveAllConnections(); + async.done(); + }); + http.patch(url).subscribe(res => {}); + })); + + it('should attach the provided body to the request', inject([AsyncTestCompleter], async => { + var body = 'this is my put body'; + backend.connections.subscribe((c) => { + expect(c.request.text()).toBe(body); + backend.resolveAllConnections(); + async.done(); + }); + http.patch(url, body).subscribe(res => {}); + })); + }); + + + describe('.head()', () => { + it('should perform a head request for given url', inject([AsyncTestCompleter], async => { + backend.connections.subscribe((c) => { + expect(c.request.method).toBe(RequestMethods.HEAD); + backend.resolveAllConnections(); + async.done(); + }); + http.head(url).subscribe(res => {}); + })); + }); + }); }); } diff --git a/modules/examples/src/http/http_comp.ts b/modules/examples/src/http/http_comp.ts index 2461a99a4e..321fc06010 100644 --- a/modules/examples/src/http/http_comp.ts +++ b/modules/examples/src/http/http_comp.ts @@ -1,6 +1,6 @@ import {bootstrap, Component, View, NgFor, NgIf, Inject} from 'angular2/angular2'; import {httpInjectables} from 'angular2/http'; -import {Http} from 'angular2/src/http/http'; +import {HttpFactory} from 'angular2/src/http/http'; import {IHttp} from 'angular2/src/http/interfaces'; import {Response} from 'angular2/src/http/static_response'; import {LocalVariable} from './assign_local_directive'; @@ -24,7 +24,7 @@ import {LocalVariable} from './assign_local_directive'; }) export class HttpCmp { people: Rx.Observable; - constructor(@Inject(Http) http: IHttp) { + constructor(@Inject(HttpFactory) http: IHttp) { this.people = http('./people.json').map(res => res.json()); } } \ No newline at end of file