Skip to content
This repository has been archived by the owner on Oct 24, 2024. It is now read-only.

Commit

Permalink
Reworked the way data and URLs are passed around.
Browse files Browse the repository at this point in the history
Closes #6 ApiLocation won't be reactive
Closes #9 Change ViewData to reactive pattern - added ActivetedView
Closes #21 Replace *resource-data directive with simple class
Reverts #16 Rename Navigable.go to Navigable.navigate

Centerpiece of internal navigation is now ResourceData class (might be renamed later)
  • Loading branch information
michadvorak-cen38289 committed Feb 6, 2018
1 parent 2f3afaf commit 3098d88
Show file tree
Hide file tree
Showing 25 changed files with 258 additions and 222 deletions.
2 changes: 1 addition & 1 deletion proxy.conf.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const TARGET = "http://private-anon-5c8d0c0da6-resourcerouterexample.apiary-mock.com";
const TARGET = "http://private-anon-39155185b1-resourcerouterexample.apiary-mock.com";

function replaceHost(data, req) {
const hostRegExp = new RegExp(TARGET.replace(/([^a-zA-Z0-9])/g, '\\$1') + '/', 'g');
Expand Down
13 changes: 9 additions & 4 deletions src/app/app.component.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
<p>See <a href="http://docs.resourcerouterexample.apiary.io/#">API definition</a></p>

<ng-template [resourceData] let-data [resourceDataOf]="apiLocation.url"
(urlChange)="apiLocation.url=$event">
<h1>{{data.url}}</h1>
<h1>ROOT</h1>
<h2>{{resource.url}}</h2>
<resource-view [data]="resource.data"></resource-view>

<h1>*resourceData</h1>
<div *resourceData="let data of apiLocation.url">
<h2>{{data.url}}</h2>
<resource-view [data]="data"></resource-view>
</ng-template>
</div>

26 changes: 21 additions & 5 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
import { Component } from '@angular/core';
import { ApiLocation } from '../lib/public_api';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { ApiLocation, bindUrl, ResourceData } from '../lib/src';
import { Subscription } from 'rxjs/Subscription';

// TODO asi by to chtelo prejmenovat ResourceData, protoze to nepopisuje, co trida dela - drzi location a aktualni data,
// TODO tedy takovy container - pak se da prejmenovat i ViewData.target

@Component({
selector: 'app-root',
templateUrl: './app.component.html'
templateUrl: './app.component.html',
providers: [ResourceData]
})
export class AppComponent {
export class AppComponent implements OnInit, OnDestroy {

private urlSubscription = Subscription.EMPTY;

constructor(public apiLocation: ApiLocation,
public resource: ResourceData) {
}

ngOnInit() {
this.urlSubscription = bindUrl(this.apiLocation, this.resource);
}

constructor(public apiLocation: ApiLocation) {
ngOnDestroy() {
this.urlSubscription.unsubscribe();
}
}
11 changes: 10 additions & 1 deletion src/lib/src/activated-view.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Navigable } from './navigation';
import { Navigable } from './navigable';
import { Observable } from 'rxjs/Observable';
import { ViewData } from './view-data';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
Expand All @@ -15,4 +15,13 @@ export class ActivatedView<T> {
get snapshot() {
return this._data.getValue();
}

/**
* Forces reload of the data (remaining on the current URL).
*
* This is identical to calling {@code activatedView.navigation.go(activatedView.snapshot.url)}.
*/
reload(): void {
this.navigation.go(this.snapshot.url);
}
}
57 changes: 18 additions & 39 deletions src/lib/src/api-location.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Injectable } from '@angular/core';
import { Location } from '@angular/common';
import { ApiMapper } from './api-mapper';
import { Navigable, NavigationSource } from './navigation';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import { LocationReference } from './location-reference';

// TODO normalize URL, using possibly LocationStrategy (that means remove trailing slash directly in the browser if its present)

Expand All @@ -12,17 +12,16 @@ import { Subject } from 'rxjs/Subject';
* Provides bindable `url` property, to be used with `resource-outlet` component.
*/
@Injectable()
export class ApiLocation implements Navigable, NavigationSource {
export class ApiLocation implements LocationReference {

readonly navigate: Observable<string>;

private navigateSubject = new Subject<string>();
readonly urlChange: Observable<string>;
private readonly urlChangeSubject = new Subject<string>();
private urlValue = '';

constructor(private readonly apiMapper: ApiMapper,
private readonly location: Location) {
// Initialize
this.navigate = this.navigateSubject.asObservable();
this.urlChange = this.urlChangeSubject.asObservable();
this.urlValue = this.mapLocationUrlToApi();

// Listen to Location changes
Expand All @@ -49,44 +48,24 @@ export class ApiLocation implements Navigable, NavigationSource {
* @param url API url. The navigation in browser is performed to the `view URL`, that is, without API prefix.
*/
set url(url: string) {
if (typeof url !== 'string') {
throw new Error('url must be a string');
}

// Normalize
url = this.location.normalize(url);

// Navigate only on change
if (url !== this.urlValue) {
// Note: This also sets urlValue to correct value
this.go(url);
}
}

/**
* Navigates to given URL in the API.
* URL should be absolute, and provided by server (not constructed locally).
*
* @param url API url to navigate to. Cannot be null.
*/
go(url: string): void {
if (typeof url !== 'string') {
throw new Error('url must be a string');
}

// Map API url to View form
const path = this.apiMapper.mapApiToView(url);
if (!path) {
throw new Error(`Cannot navigate to URL '${url}', it cannot be mapped to known API prefix`);
// Map API url to View form
const path = this.apiMapper.mapApiToView(url);
if (!path) {
throw new Error(`Cannot navigate to URL '${url}', it cannot be mapped to known API prefix`);
}

// Navigate
this.doNavigate(path);
}

// Navigate
this.doNavigate(path);
}

// noinspection JSUnusedGlobalSymbols
/**
* Navigates to root of the API, that is '/' view path.
*/
home(): void {
// We don't need API url here, / leads to root of the api, always
this.doNavigate('/');
}

/**
Expand Down Expand Up @@ -130,6 +109,6 @@ export class ApiLocation implements Navigable, NavigationSource {
*/
protected onLocationChanged() {
this.urlValue = this.mapLocationUrlToApi();
this.navigateSubject.next(this.urlValue);
this.urlChangeSubject.next(this.urlValue);
}
}
31 changes: 10 additions & 21 deletions src/lib/src/directives/resource-data-of.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,35 +11,29 @@ import {
} from '@angular/core';
import { ViewData } from '../view-data';
import { ResourceData } from '../resource-data';
import { ISubscription } from 'rxjs/Subscription';
import { Subscription } from 'rxjs/Subscription';


/**
* @deprecated
*/
export class ResourceDataOfContext {
constructor(private resource: ResourceData) {
}

// noinspection JSUnusedGlobalSymbols
get $implicit(): ViewData<any> {
return this.resource.data;
}
}


/**
* @deprecated
*/
@Directive({
selector: '[resourceDataOf]',
selector: '[resourceData][resourceDataOf]',
providers: [ResourceData]
})
export class ResourceDataOfDirective implements OnInit, OnDestroy {

@Output()
readonly urlChange = new EventEmitter<string>();
private context: ResourceDataOfContext;
private subscription: ISubscription;
private urlSubscription = Subscription.EMPTY;
private readonly context: ResourceDataOfContext;

constructor(private viewContainer: ViewContainerRef,
private templateRef: TemplateRef<ResourceDataOfContext>,
Expand All @@ -49,23 +43,18 @@ export class ResourceDataOfDirective implements OnInit, OnDestroy {

ngOnInit(): void {
this.viewContainer.createEmbeddedView(this.templateRef, this.context);
this.subscription = this.resource.urlChange.subscribe((value: string) => this.urlChange.emit(value));
}

ngOnDestroy(): void {
this.subscription.unsubscribe();
// Note: We don't need listen to this.urlChange, since we don't store the url, we just propagate event
this.urlSubscription = this.resource.urlChange.subscribe((value: string) => this.urlChange.emit(value));
}

/**
* @deprecated
*/
@Input()
set resourceData(value: any) {
ngOnDestroy(): void {
this.urlSubscription.unsubscribe();
}

@Input()
set resourceDataOf(value: string) {
this.url = value;
this.resource.url = value;
}

get url(): string {
Expand Down
Loading

0 comments on commit 3098d88

Please sign in to comment.