Skip to content

Commit a858b6d

Browse files
committed
feat(Http): option to fetch local resources with Http using '~/' semantics like Image component
closes #249
1 parent a9f1637 commit a858b6d

File tree

14 files changed

+252
-7
lines changed

14 files changed

+252
-7
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ ng-sample/app/global.d.ts
2929
ng-sample/platforms
3030
ng-sample/lib
3131
ng-sample/node_modules
32+
ng-sample/app/nativescript-angular
3233

3334
startup-test/platforms
3435
startup-test/lib

Diff for: nativescript-angular/application.ts

+19-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import 'globals';
2-
import "zone.js/dist/zone-node"
2+
import "zone.js/dist/zone-node";
33

44
import 'reflect-metadata';
55
import './polyfills/array';
@@ -14,7 +14,10 @@ import {RootRenderer, Renderer} from '@angular/core/src/render/api';
1414
import {NativeScriptRootRenderer, NativeScriptRenderer} from './renderer';
1515
import {NativeScriptDomAdapter, NativeScriptElementSchemaRegistry, NativeScriptSanitizationService} from './dom-adapter';
1616
import {ElementSchemaRegistry, XHR, COMPILER_PROVIDERS, CompilerConfig} from '@angular/compiler';
17-
import {FileSystemXHR} from './xhr';
17+
import {Http, XHRBackend, BrowserXhr, RequestOptions, ResponseOptions, XSRFStrategy} from '@angular/http';
18+
import {FileSystemXHR} from './http/xhr';
19+
import {NSXSRFStrategy, NSHttp} from './http/ns-http';
20+
import {NSFileSystem} from './file-system/ns-file-system';
1821
import {Parse5DomAdapter} from '@angular/platform-server/src/parse5_adapter';
1922
import {ExceptionHandler} from '@angular/core/src/facade/exception_handler';
2023
import {APPLICATION_COMMON_PROVIDERS} from '@angular/core/src/application_common_providers';
@@ -87,15 +90,27 @@ export function bootstrap(appComponentType: any,
8790
provide(ElementSchemaRegistry, { useClass: NativeScriptElementSchemaRegistry }),
8891
NS_COMPILER_PROVIDERS,
8992
provide(ElementSchemaRegistry, { useClass: NativeScriptElementSchemaRegistry }),
90-
provide(XHR, { useClass: FileSystemXHR }),
93+
provide(XHR, { useClass: FileSystemXHR })
9194
]
9295

9396
var appProviders = [defaultAppProviders];
9497
if (isPresent(customProviders)) {
9598
appProviders.push(customProviders);
9699
}
97100

98-
var platform = getPlatform();
101+
// Http Setup
102+
// Since HTTP_PROVIDERS can be added with customProviders above, this must come after
103+
appProviders.push([
104+
provide(XSRFStrategy, { useValue: new NSXSRFStrategy()}),
105+
NSFileSystem,
106+
provide(Http, {
107+
useFactory: (backend, options, nsFileSystem) => {
108+
return new NSHttp(backend, options, nsFileSystem);
109+
}, deps: [XHRBackend, RequestOptions, NSFileSystem]
110+
})
111+
]);
112+
113+
var platform = getPlatform();
99114
if (!isPresent(platform)) {
100115
platform = createPlatform(ReflectiveInjector.resolveAndCreate(platformProviders));
101116
}

Diff for: nativescript-angular/file-system/ns-file-system.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import {Injectable} from '@angular/core';
2+
import {knownFolders, Folder} from 'file-system';
3+
4+
// Allows greater flexibility with `file-system` and Angular
5+
// Also provides a way for `file-system` to be mocked for testing
6+
7+
@Injectable()
8+
export class NSFileSystem {
9+
public currentApp(): Folder {
10+
return knownFolders.currentApp();
11+
}
12+
}

Diff for: nativescript-angular/http/ns-http.ts

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import {Injectable} from '@angular/core';
2+
import {Http, XHRConnection, ConnectionBackend, RequestOptions, RequestOptionsArgs, ResponseOptions, ResponseType, Response, Request, BrowserXhr} from '@angular/http';
3+
import {Observable} from 'rxjs/Observable';
4+
import 'rxjs/add/observable/fromPromise';
5+
import {NSFileSystem} from '../file-system/ns-file-system';
6+
7+
export class NSXSRFStrategy {
8+
public configureRequest(req: any) {
9+
// noop
10+
}
11+
}
12+
13+
@Injectable()
14+
export class NSHttp extends Http {
15+
constructor(backend: ConnectionBackend, defaultOptions: RequestOptions, private nsFileSystem: NSFileSystem) {
16+
super(backend, defaultOptions);
17+
}
18+
19+
/**
20+
* Performs a request with `get` http method.
21+
* Uses a local file if `~/` resource is requested.
22+
*/
23+
get(url: string, options?: RequestOptionsArgs): Observable<Response | any> {
24+
if (url.indexOf('~') === 0 || url.indexOf('/') === 0) {
25+
// normalize url
26+
url = url.replace('~', '').replace('/', '');
27+
// request from local app resources
28+
return Observable.fromPromise(new Promise((resolve, reject) => {
29+
let app = this.nsFileSystem.currentApp();
30+
let localFile = app.getFile(url);
31+
if (localFile) {
32+
localFile.readText().then((data) => {
33+
resolve(responseOptions(data, 200, url));
34+
}, (err: Object) => {
35+
reject(responseOptions(err, 400, url));
36+
});
37+
} else {
38+
reject(responseOptions('Not Found', 404, url));
39+
}
40+
}));
41+
} else {
42+
return super.get(url, options);
43+
}
44+
}
45+
}
46+
47+
function responseOptions(body: string | Object, status: number, url: string): Response {
48+
return new Response(new ResponseOptions({
49+
body: body,
50+
status: status,
51+
statusText: 'OK',
52+
type: status === 200 ? ResponseType.Default : ResponseType.Error,
53+
url: url
54+
}));
55+
}
56+
File renamed without changes.

Diff for: nativescript-angular/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"@angular/common": "2.0.0-rc.3",
2222
"@angular/compiler": "2.0.0-rc.3",
2323
"@angular/core": "2.0.0-rc.3",
24+
"@angular/http": "2.0.0-rc.3",
2425
"@angular/platform-browser": "2.0.0-rc.3",
2526
"@angular/platform-browser-dynamic": "2.0.0-rc.3",
2627
"@angular/platform-server": "2.0.0-rc.3",
@@ -39,4 +40,4 @@
3940
"typescript": "^1.8.10"
4041
},
4142
"nativescript": {}
42-
}
43+
}

Diff for: ng-sample/app/app.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import { nativeScriptBootstrap } from "nativescript-angular/application";
1010
import { NS_ROUTER_PROVIDERS as NS_ROUTER_PROVIDERS_DEPRECATED } from "nativescript-angular/router-deprecated";
1111
import { NS_ROUTER_PROVIDERS } from "nativescript-angular/router";
12+
import { HTTP_PROVIDERS } from "@angular/http";
1213
import { rendererTraceCategory, routerTraceCategory, listViewTraceCategory } from "nativescript-angular/trace";
1314

1415
import trace = require("trace");
@@ -23,6 +24,7 @@ import {Benchmark} from './performance/benchmark';
2324
import {ListTest} from './examples/list/list-test';
2425
import {ListTestAsync, ListTestFilterAsync} from "./examples/list/list-test-async";
2526
import {ImageTest} from "./examples/image/image-test";
27+
import {HttpTest} from "./examples/http/http-test";
2628
import {ActionBarTest} from "./examples/action-bar/action-bar-test";
2729
import {ModalTest} from "./examples/modal/modal-test";
2830
import {PlatfromDirectivesTest} from "./examples/platform-directives/platform-directives-test";
@@ -43,6 +45,7 @@ import { PageRouterOutletNestedAppComponent, PageRouterOutletNestedRouterProvide
4345
// nativeScriptBootstrap(ListTest);
4446
// nativeScriptBootstrap(ListTestAsync);
4547
//nativeScriptBootstrap(ImageTest);
48+
nativeScriptBootstrap(HttpTest, [HTTP_PROVIDERS]);
4649
//nativeScriptBootstrap(ActionBarTest, [NS_ROUTER_PROVIDERS_DEPRECATED], { startPageActionBarHidden: false });
4750
//nativeScriptBootstrap(ActionBarTest, [NS_ROUTER_PROVIDERS_DEPRECATED]);
4851
//nativeScriptBootstrap(ModalTest);
@@ -51,7 +54,7 @@ import { PageRouterOutletNestedAppComponent, PageRouterOutletNestedRouterProvide
5154
// new router
5255
// nativeScriptBootstrap(RouterOutletAppComponent, [RouterOutletRouterProviders]);
5356
// nativeScriptBootstrap(PageRouterOutletAppComponent, [PageRouterOutletRouterProviders]);
54-
nativeScriptBootstrap(PageRouterOutletNestedAppComponent, [PageRouterOutletNestedRouterProviders]);
57+
// nativeScriptBootstrap(PageRouterOutletNestedAppComponent, [PageRouterOutletNestedRouterProviders]);
5558

5659
// router-deprecated
5760
// nativeScriptBootstrap(NavigationTest, [NS_ROUTER_PROVIDERS_DEPRECATED]);

Diff for: ng-sample/app/examples/http/data.json

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"results": [
3+
{
4+
"title": "Test",
5+
"description": "Testing Http local and remote."
6+
}
7+
]
8+
}

Diff for: ng-sample/app/examples/http/http-test.ts

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import {Component} from '@angular/core';
2+
import {Http} from '@angular/http';
3+
import 'rxjs/add/operator/map';
4+
5+
/* IMPORTANT
6+
In order to test out the full image example, to fix the App Transport Security error in iOS 9, you will need to follow this after adding the iOS platform:
7+
8+
https://blog.nraboy.com/2015/12/fix-ios-9-app-transport-security-issues-in-nativescript/
9+
*/
10+
11+
@Component({
12+
selector: 'http-test',
13+
template: `
14+
<StackLayout horizontalAlignment="center">
15+
<Button text="Load Local File with Http" (tap)='loadLocal()' cssClass="btn-primary"></Button>
16+
<Button text="Load Remote File with Http" (tap)='loadRemote()' cssClass="btn-primary"></Button>
17+
<Label [text]="title" textWrap="true"></Label>
18+
<Label [text]="description" textWrap="true"></Label>
19+
</StackLayout>
20+
`,
21+
styles: [
22+
`Button {
23+
margin-bottom:20;
24+
}`
25+
]
26+
})
27+
export class HttpTest {
28+
public title: string;
29+
public description: string;
30+
31+
constructor(private http: Http) {
32+
33+
}
34+
35+
public loadLocal() {
36+
this.http.get('~/examples/http/data.json').map(res => res.json()).subscribe((response: any) => {
37+
let user = response.results[0];
38+
this.title = user.title;
39+
this.description = user.description;
40+
});
41+
}
42+
43+
public loadRemote() {
44+
this.http.get(`https://randomuser.me/api/?results=1&nat=us`).map(res => res.json()).subscribe((response: any) => {
45+
let user = response.results[0];
46+
this.title = user.name.first;
47+
this.description = user.email;
48+
});
49+
}
50+
}

Diff for: ng-sample/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"@angular/common": "2.0.0-rc.3",
3030
"@angular/compiler": "2.0.0-rc.3",
3131
"@angular/core": "2.0.0-rc.3",
32+
"@angular/http": "2.0.0-rc.3",
3233
"@angular/platform-browser": "2.0.0-rc.3",
3334
"@angular/platform-browser-dynamic": "2.0.0-rc.3",
3435
"@angular/platform-server": "2.0.0-rc.3",

Diff for: tests/app/tests/http.ts

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//make sure you import mocha-config before @angular/core
2+
import {assert} from "./test-config";
3+
import {
4+
async,
5+
inject,
6+
beforeEach,
7+
beforeEachProviders
8+
} from '@angular/core/testing';
9+
import {provide, ReflectiveInjector} from '@angular/core';
10+
import {BaseRequestOptions, ConnectionBackend, Http, HTTP_PROVIDERS, Response, ResponseOptions} from '@angular/http';
11+
import 'rxjs/add/operator/map';
12+
import {MockBackend} from '@angular/http/testing';
13+
import {NSHttp} from "nativescript-angular/http/ns-http";
14+
import {NSFileSystem} from "nativescript-angular/file-system/ns-file-system";
15+
import {NSFileSystemMock, FileResponses} from './mocks/ns-file-system.mock';
16+
17+
describe("Http", () => {
18+
let http: Http;
19+
let backend: MockBackend;
20+
21+
beforeEach(() => {
22+
let injector = ReflectiveInjector.resolveAndCreate([
23+
HTTP_PROVIDERS,
24+
BaseRequestOptions,
25+
MockBackend,
26+
provide(NSFileSystem, { useClass: NSFileSystemMock }),
27+
provide(Http, {
28+
useFactory: function (backend: ConnectionBackend, defaultOptions: BaseRequestOptions, nsFileSystem: NSFileSystem) {
29+
return new NSHttp(backend, defaultOptions, nsFileSystem);
30+
},
31+
deps: [MockBackend, BaseRequestOptions, NSFileSystem]
32+
})
33+
]);
34+
35+
backend = injector.get(MockBackend);
36+
http = injector.get(Http);
37+
});
38+
39+
it("should work with local files prefixed with '~'", () => {
40+
http.get('~/test.json').map(res => res.json()).subscribe((response: any) => {
41+
assert.strictEqual(3, response.length);
42+
assert.strictEqual('Alex', response[0].name);
43+
});
44+
});
45+
46+
it("should work with local files prefixed with '/'", () => {
47+
http.get('/test.json').map(res => res.json()).subscribe((response: any) => {
48+
assert.strictEqual(3, response.length);
49+
assert.strictEqual('Panayot', response[2].name);
50+
});
51+
});
52+
53+
it("should work with remote files", () => {
54+
let connection: any;
55+
backend.connections.subscribe((c: any) => connection = c);
56+
http.get('http://www.nativescript.org/test.json').map(res => res.json()).subscribe((response: any) => {
57+
assert.strictEqual(3, response.length);
58+
assert.strictEqual('Rosen', response[1].name);
59+
});
60+
connection.mockRespond(new Response(new ResponseOptions({ body: FileResponses.AWESOME_TEAM })));
61+
});
62+
});

Diff for: tests/app/tests/mocks/ns-file-system.mock.ts

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import {Injectable} from '@angular/core';
2+
import {ResponseType, Response, ResponseOptions} from '@angular/http';
3+
4+
export class FileResponses {
5+
public static AWESOME_TEAM: string = '[{"name":"Alex"}, {"name":"Rosen"}, {"name":"Panayot"}]';
6+
}
7+
8+
// Folder mock
9+
class Folder {
10+
public getFile(url: string): any {
11+
let data;
12+
switch (url) {
13+
case 'test.json':
14+
data = FileResponses.AWESOME_TEAM;
15+
break;
16+
default:
17+
throw (new Error('Unsupported file for the testing mock - ns-file-system-mock'));
18+
}
19+
return {
20+
readText: () => {
21+
return new Promise((resolve) => {
22+
resolve(data);
23+
});
24+
}
25+
}
26+
}
27+
}
28+
29+
// Filesystem mock
30+
@Injectable()
31+
export class NSFileSystemMock {
32+
public currentApp(): Folder {
33+
return new Folder();
34+
}
35+
}

Diff for: tests/app/tests/xhr-paths.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//make sure you import mocha-config before @angular/core
22
import {assert} from "./test-config";
3-
import {FileSystemXHR} from "nativescript-angular/xhr";
3+
import {FileSystemXHR} from "nativescript-angular/http/xhr";
44

55
describe("XHR name resolution", () => {
66
it("resolves relative paths from app root", () => {

Diff for: tests/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"@angular/common": "2.0.0-rc.3",
3333
"@angular/compiler": "2.0.0-rc.3",
3434
"@angular/core": "2.0.0-rc.3",
35+
"@angular/http": "2.0.0-rc.3",
3536
"@angular/platform-browser": "2.0.0-rc.3",
3637
"@angular/platform-browser-dynamic": "2.0.0-rc.3",
3738
"@angular/platform-server": "2.0.0-rc.3",

0 commit comments

Comments
 (0)