Skip to content

Commit

Permalink
Added angular integration library 'ng-aux-library'
Browse files Browse the repository at this point in the history
  • Loading branch information
arneg committed Oct 31, 2019
1 parent 0f3e0ea commit 6991d71
Show file tree
Hide file tree
Showing 9 changed files with 372 additions and 0 deletions.
1 change: 1 addition & 0 deletions angular/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
See `doc/docs/Angular.md` for information about how to use these directives.
34 changes: 34 additions & 0 deletions angular/aux-assign.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Directive, ElementRef, Inject, Input } from '@angular/core';
import { Observable, SubscriptionLike, isObservable } from 'rxjs';
import { AuxSubscriptions } from './aux-subscriptions.model';

@Directive({
selector: '[auxAssign]'
})
export class AuxAssignDirective extends AuxSubscriptions
{
protected install_binding(widget: any, name: string, b: any)
: SubscriptionLike
{
if (isObservable(b))
{
const o = b as Observable<any>;
return o.subscribe((v) => {
widget.set(name, v);
});
}
else
{
widget.set(name, b);

// nothing was subscribed
return null;
}
}

@Input('auxAssign')
set auxAssign(bindings: {[name: string]: any}|null)
{
this.update(bindings);
}
}
55 changes: 55 additions & 0 deletions angular/aux-bind.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Directive, ElementRef, Inject, Input } from '@angular/core';
import { Observable, SubscriptionLike, isObservable } from 'rxjs';
import { AuxSubscriptions } from './aux-subscriptions.model';
import { DebounceBinding } from '@deuso/aux';

@Directive({
selector: '[auxBind]'
})
export class AuxBindDirective extends AuxSubscriptions
{
private bindings = new Map<string,DebounceBinding>();

protected remove_binding(widget: any, name: string)
{
let binding = this.bindings.get(name);

if (binding)
{
this.bindings.delete(name);
binding.destroy();
}
}

protected install_binding(widget: any, name: string, b: any)
: SubscriptionLike
{
let binding = this.bindings.get(name);

if (!binding)
{
this.bindings.set(name, binding = new DebounceBinding(widget, name, 250));
}

if (isObservable(b))
{
const o = b as Observable<any>;
return o.subscribe((v) => {
binding.set(v);
});
}
else
{
binding.set(b);

// nothing was subscribed
return null;
}
}

@Input('auxBind')
set auxBind(bindings: {[name: string]: any}|null)
{
this.update(bindings);
}
}
29 changes: 29 additions & 0 deletions angular/aux-directives.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { NgModule } from '@angular/core';
import { AuxAssignDirective } from './aux-assign.directive';
import { AuxBindDirective } from './aux-bind.directive';
import { AuxInterceptDirective } from './aux-intercept.directive';
import { AuxObserveAllDirective } from './aux-observe-all.directive';
import { AuxObserveDirective } from './aux-observe.directive';
import { AuxSubscribeDirective } from './aux-subscribe.directive';


@NgModule({
declarations: [
AuxAssignDirective,
AuxBindDirective,
AuxInterceptDirective,
AuxObserveAllDirective,
AuxObserveDirective,
AuxSubscribeDirective
],
exports: [
AuxAssignDirective,
AuxBindDirective,
AuxInterceptDirective,
AuxObserveAllDirective,
AuxObserveDirective,
AuxSubscribeDirective
],
imports: [ ]
})
export class AuxDirectivesModule { }
37 changes: 37 additions & 0 deletions angular/aux-intercept.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Directive, ElementRef, Input } from '@angular/core';
import { Observable, SubscriptionLike } from 'rxjs';
import {
AuxSubscriptions, make_subscription
} from './aux-subscriptions.model';
import { intercept_option } from '@deuso/aux';

@Directive({
selector: '[auxIntercept]'
})
export class AuxInterceptDirective extends AuxSubscriptions
{
protected install_binding(widget: any, name: string, b: any)
: SubscriptionLike
{
if (typeof(b) === 'object' && b.next)
{
return make_subscription(intercept_option(widget, name, (v) => {
b.next(v);
}));
}
else if (typeof(b) === 'function')
{
return make_subscription(intercept_option(widget, name, b));
}
else
{
throw new Error("Unsupported binding type.");
}
}

@Input('auxIntercept')
set auxIntercept(bindings: {[name: string]: any}|null)
{
this.update(bindings);
}
}
36 changes: 36 additions & 0 deletions angular/aux-observe-all.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Directive, ElementRef, Input } from '@angular/core';
import { Observable, SubscriptionLike } from 'rxjs';
import {
AuxSubscriptions, make_subscription
} from './aux-subscriptions.model';
import { observe_option } from '@deuso/aux';

@Directive({
selector: '[auxObserveAll]'
})
export class AuxObserveAllDirective extends AuxSubscriptions {
protected install_binding(widget: any, name: string, b: any)
: SubscriptionLike
{
if (typeof(b) === 'object' && b.next)
{
return make_subscription(observe_option(widget, name, (v) => {
b.next(v);
}));
}
else if (typeof(b) === 'function')
{
return make_subscription(observe_option(widget, name, b));
}
else
{
throw new Error("Unsupported binding type.");
}
}

@Input('auxObserveAll')
set auxObserveAll(bindings: {[name: string]: any}|null)
{
this.update(bindings);
}
}
36 changes: 36 additions & 0 deletions angular/aux-observe.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Directive, ElementRef, Input } from '@angular/core';
import { Observable, SubscriptionLike } from 'rxjs';
import {
AuxSubscriptions, make_subscription
} from './aux-subscriptions.model';
import { observe_useraction } from '@deuso/aux';

@Directive({
selector: '[auxObserve]'
})
export class AuxObserveDirective extends AuxSubscriptions {
protected install_binding(widget: any, name: string, b: any)
: SubscriptionLike
{
if (typeof(b) === 'object' && b.next)
{
return make_subscription(observe_useraction(widget, name, (v) => {
b.next(v);
}));
}
else if (typeof(b) === 'function')
{
return make_subscription(observe_useraction(widget, name, b));
}
else
{
throw new Error("Unsupported binding type.");
}
}

@Input('auxObserve')
set auxObserve(bindings: {[name: string]: any}|null)
{
this.update(bindings);
}
}
35 changes: 35 additions & 0 deletions angular/aux-subscribe.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Directive, ElementRef, Input } from '@angular/core';
import { Observable, SubscriptionLike } from 'rxjs';
import {
AuxSubscriptions, aux_subscribe
} from './aux-subscriptions.model';

@Directive({
selector: '[auxSubscribe]'
})
export class AuxSubscribeDirective extends AuxSubscriptions {
protected install_binding(widget: any, name: string, b: any)
: SubscriptionLike
{
if (typeof(b) === 'object' && b.next)
{
return aux_subscribe(widget, name, (...args) => {
b.next(args);
});
}
else if (typeof(b) === 'function')
{
return aux_subscribe(widget, name, b);
}
else
{
throw new Error("Unsupported binding type.");
}
}

@Input('auxSubscribe')
set auxSubscribe(bindings: {[name: string]: any}|null)
{
this.update(bindings);
}
}
109 changes: 109 additions & 0 deletions angular/aux-subscriptions.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { ElementRef, Inject, OnDestroy } from '@angular/core';
import { SubscriptionLike } from 'rxjs';

export function make_subscription(subscription: Function): SubscriptionLike
{
return {
closed: false,
unsubscribe()
{
if (this.closed) return;
this.closed = true;
subscription();
},
};
}

export function aux_subscribe(widget: any, name: string,
_cb: any): SubscriptionLike
{
return make_subscription(widget.subscribe(name, (...args) => _cb(...args)));
}

export abstract class AuxSubscriptions implements OnDestroy
{
private last: {[name: string]: any}|null = null;
private subscriptions = new Map<string,SubscriptionLike>();

protected remove_binding(widget: any, name: string): void
{
}

protected abstract install_binding(widget: any, name: string, b: any)
: SubscriptionLike;

protected update(bindings: {[name: string]: any}|null)
{
const element = this.el.nativeElement;
const widget = element.auxWidget;

if (!widget)
{
if (element.tagName.startsWith('AUX-'))
{
throw new Error("The AUX WebComponent has not been upgraded, yet. "+
"Are you missing and import?");
}
throw new Error("The element is not an AUX WebComponent.");
}

const last = this.last || {};
const current = bindings || {};

for (let name in last)
{
// handled by loop below
if (name in current) continue;

// remove old subscriptions
let subscriptions = this.subscriptions.get(name);

if (subscriptions)
{
this.subscriptions.delete(name);
subscriptions.unsubscribe();
}

this.remove_binding(widget, name);
}

for (let name in current)
{
const value = current[name];

if (name in last)
{
const last_value = last[name];

if (last_value === value) continue;

// remove old subscriptions
const subscriptions = this.subscriptions.get(name);

if (subscriptions)
{
this.subscriptions.delete(name);
subscriptions.unsubscribe();
}
}

const subscription = this.install_binding(widget, name, value);

if (subscription)
{
this.subscriptions.set(name, subscription);
}
}

// we create a copy to make sure that our state is not
// modified
this.last = bindings ? Object.assign({}, bindings) : null;
}

constructor(@Inject(ElementRef) private el: ElementRef) { }

public ngOnDestroy()
{
this.update(null);
}
}

0 comments on commit 6991d71

Please sign in to comment.