jwidget/Mapper

Consumption

import Mapper, {mapProperties} from "jwidget/Mapper";

Default export#

Hierarchy#

Description#

T
Target property value type.

Listens source Bindable instances modification and recreates target value via mapping function.

const count  = new Property(1);
const unit   = new Property("apples");
const target = new Property<string>();

const mapper = new Mapper([count, units], (count: number, units: string) => count + " " + units, {target});
expect(target.get()).toBe("1 apples");

count.set(2);
expect(target.get()).toBe("2 apples");

mapper.destroy(); // stops synchronization and resets target value to null
expect(target.get()).toBe(null);

If target is omitted in constructor, it is created automatically.

const source = new Property(1);
const mapper = new Mapper([source], (value: number) => value + " apples");
const target = mapper.target;
expect(target.get()).toBe("1 apples");
mapper.destroy(); // stops synchronization

In this case, it is optimal to use mapProperties function instead. It returns target property right away:

const source = new Property(1);
const target = mapProperties([source], (value: number) => value + " apples");
expect(target.get()).toBe("1 apples");
target.destroy(); // stops synchronization

And even better, if you have just one source property, simply call its map method:

const source = new Property(1);
const target = source.map(value => value + " apples");
expect(target.get()).toBe("1 apples");
target.destroy(); // stops synchronization

If target value is Destroyable instance, Mapper can destroy it for you. Just pass destroy config option:

const source = new Property(1);
const target = source.map(value => new View(value), {destroy});
source.set(2); // assigns target value to new View instance and destroys the previous one
target.destroy(); // stops synchronization and destroys the View
Flow#

On source property change, next flow takes place:

  1. Create a new value.
  2. Reassign target property.
  3. Destroy the old value.

You can override this behaviour by passing viaNull config option. Setting it to true changes the flow the next way:

  1. Set target value to null.
  2. Destroy the old value.
  3. Create a new value.
  4. Assign target property.

This can be useful if you want the old value to be destroyed before the new value is created.

View binding#

Common use case for mapper is bindable child component creation by data:

@template('<div><div jwid="avatar"></div></div>')
class ProfileView extends Component {
    constructor(private avatar: Bindable<Avatar>) {
        super();
    }

    protected renderAvatar() {
        return this.own(this.avatar.map(avatar => new AvatarView(avatar), {destroy}));
    }
}
Chaining#

Mapper allows you to chain property calculations. Assume that you have several folders and several files in each folder. One folder is selected, and each folder has a selected file inside. You want to create a file view by currently selected file in currently selected folder. You can do it the next way:

class File extends Class {
    // ... whatever
}

class Folder extends Class {
    readonly selectedFile = new Property<File>();
}

@template('<div><div jwid="file"></div></div>')
class AppView extends Component {
    constructor(private selectedFolder: Bindable<Folder>) {
        super();
    }

    protected renderFile() {
        const target = new Property<FileView>();
        this.own(this.selectedFolder.map(folder => (
            folder.selectedFile.map(file => new FileView(file), {target, destroy});
        ), {destroy}));
        return target;
    }
}

Constructor#

new Mapper<T>(sources: Bindable<any>[], create: Mapper.CreateCallback<T>, config?: Mapper.FullConfig<T>)

sources
Source bindables.
create
Mapping function. Signature: (...sourceValues: any[]) => T
config
Configuration.

Constructs Mapper instance. Computes target property value as result of create callback and synchronizes it to all sources. If target is omitted in config, creates it automatically.

Fields#

sources#

sources: Bindable<any>[]

Source bindables.

target#

target: Bindable<T>

Target property.

iid (inherited from Identifiable)#

readonly iid: number

Instance ID.

Auto-incrementing object unique ID. Extend Class to have such an identifier auto-assigned, or simply call newIid method to obtain one.

Methods#

listen#

(event: Listenable<any>): this

event
Event to listen.
returns
this

Listens specified event and issues target value recalculation on event triggering.

bind#

(property: Bindable<any>): this

property
Bindable to watch.
returns
this

Watches specified property and issues target value recalculation on its change.

update#

()

Updates target property forcibly.

destroy (inherited from Class)#

(): void

Class destructor invocation method. Destroys all aggregated objects and calls destroyObject method. You must call this method explicitly from outside, because JavaScript doesn't support automatic class destructor calling.

const object = new MyClass();

// ...

// Once object is not needed anymore, destroy it
object.destroy();

Alternatively (and optimally), you should use own method to aggregate this object inside another one.

Unlike the other Destroyable subclasses, Class subclasses are not recommended to override destroy method directly. Instead, please use destroyObject if you want the aggregated objects to be already destroyed.

You can override destroy method in a subclass to do some preliminary work before aggregated object destruction. For example, Component overrides this method to remove child components before their destruction, because child components are usually aggregated inside the component. However, in the majority of cases, you should override destroyObject method instead to customize destruction logic.

destroyObject (inherited from Class)#

protected (): void

Class destructor implementation. Called in destroy method after aggregated object destruction. The logic of class instance destruction should be implemented here. If you override this method, remember to call super.destroyObject() at the end of the method:

destroyObject() {
    // Release resources
    ...

    // Call superclass destructor
    super.destroyObject();
}
own (inherited from IClass)#

<T extends Destroyable>(obj: T): T

obj
Object to aggregate.
returns
obj

Aggregates the object. It means that the specified object is automatically destroyed on this object destruction. The aggregated objects are destroyed in reverse order. Returns obj object, which makes it easy to use in field definition:

private selected = this.own(new Property(false));
owning (inherited from IClass)#

(obj: Destroyable): this

obj
Object to aggregate.
returns
this

Aggregates the object. It means that the specified object is automatically destroyed on this object destruction. The aggregated objects are destroyed in reverse order. Returns this object, which makes it easy to use in object instantiation:

const items = new List();
return new Panel(items).owning(items);

Mapper.ByReducer#

Hierarchy#

Description#

T
Source bindable value type.
U
Target property value type.

Mapper by Reducer. Kind of mapper optimized for working with collections of similar properties.

const sources: Bindable<number>[] = [
    new Property(3),
    new Property(2),
    new Property(2)
];
const target = new Property<number>();

const mapper = new Mapper.ByReducer(sources, sum, target);
expect(target.get()).toBe(7);

sources[0].set(5);
expect(target.get()).toBe(9);

Constructor#

new Mapper.ByReducer<T, U>(sources: Bindable<T>[], reducer: Reducer<T, U>, target?: IProperty<U>)

sources
Source bindables.
reducer
Mapping reducer.
target
Target property.

Constructs Mapper.ByReducer instance. Computes target property value as result of reducer and synchronizes it to all sources. If target is omitted, creates it automatically.

Fields#

sources#

sources: Bindable<T>[]

Source bindables.
target#

target: Bindable<U>

Target property.
reducer#

reducer: Reducer<T, U>

Mapping reducer.
iid (inherited from Identifiable)#

readonly iid: number

Instance ID.

Auto-incrementing object unique ID. Extend Class to have such an identifier auto-assigned, or simply call newIid method to obtain one.

Methods#

destroy (inherited from Class)#

(): void

Class destructor invocation method. Destroys all aggregated objects and calls destroyObject method. You must call this method explicitly from outside, because JavaScript doesn't support automatic class destructor calling.

const object = new MyClass();

// ...

// Once object is not needed anymore, destroy it
object.destroy();

Alternatively (and optimally), you should use own method to aggregate this object inside another one.

Unlike the other Destroyable subclasses, Class subclasses are not recommended to override destroy method directly. Instead, please use destroyObject if you want the aggregated objects to be already destroyed.

You can override destroy method in a subclass to do some preliminary work before aggregated object destruction. For example, Component overrides this method to remove child components before their destruction, because child components are usually aggregated inside the component. However, in the majority of cases, you should override destroyObject method instead to customize destruction logic.

destroyObject (inherited from Class)#

protected (): void

Class destructor implementation. Called in destroy method after aggregated object destruction. The logic of class instance destruction should be implemented here. If you override this method, remember to call super.destroyObject() at the end of the method:

destroyObject() {
    // Release resources
    ...

    // Call superclass destructor
    super.destroyObject();
}
own (inherited from IClass)#

<T extends Destroyable>(obj: T): T

obj
Object to aggregate.
returns
obj

Aggregates the object. It means that the specified object is automatically destroyed on this object destruction. The aggregated objects are destroyed in reverse order. Returns obj object, which makes it easy to use in field definition:

private selected = this.own(new Property(false));
owning (inherited from IClass)#

(obj: Destroyable): this

obj
Object to aggregate.
returns
this

Aggregates the object. It means that the specified object is automatically destroyed on this object destruction. The aggregated objects are destroyed in reverse order. Returns this object, which makes it easy to use in object instantiation:

const items = new List();
return new Panel(items).owning(items);

mapProperties#

mapProperties<T>(sources: Bindable<any>[], reducer: Reducer<any, T>): DestroyableBindable<T> mapProperties<T>(sources: Bindable<any>[], create: Mapper.CreateCallback<T>, config?: Mapper.Config<T>): DestroyableBindable<T>

sources
Source bindables.
reducer
Mapping reducer.
create
Mapping function. Signature: (...sourceValues: any[]) => T
config
Configuration.

Optimized way to create a mapper with new target value. Returns target property. Destroy it to stop synchronization.

Mapper.Config#

T
Target property value type.

Configuration of mapProperties function. Partial configuration of Mapper.

destroy#

destroy?: Mapper.DestroyCallback<T>

Destroys target property value if specified.

Signature: (targetValue: T, ...sourceValues: any[]) => any

scope#

scope?: any

create and destroy call scope. Defaults to mapper itself.

viaNull#

viaNull?: boolean

Reverses mapper updating flow.

Mapper.FullConfig#

T
Target property value type.

Full configuration of Mapper.

target#

target?: IProperty<T>

Target property. By default, created automatically.

destroy (inherited from Mapper.Config)#

destroy?: Mapper.DestroyCallback<T>

Destroys target property value if specified.

Signature: (targetValue: T, ...sourceValues: any[]) => any

scope (inherited from Mapper.Config)#

scope?: any

create and destroy call scope. Defaults to mapper itself.

viaNull (inherited from Mapper.Config)#

viaNull?: boolean

Reverses mapper updating flow.