import Mapper, {mapProperties} from "jwidget/Mapper";
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
On source property change, next flow takes place:
You can override this behaviour by passing viaNull config option. Setting it to true changes the flow the next way:
This can be useful if you want the old value to be destroyed before the new value is created.
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})); } }
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; } }
new Mapper<T>(sources: Bindable<any>[], create: Mapper.CreateCallback<T>, config?: Mapper.FullConfig<T>)
(...sourceValues: any[]) => T
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.
sources: Bindable<any>[]
Source bindables.
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.
(event: Listenable<any>): this
Listens specified event and issues target value recalculation on event triggering.
(property: Bindable<any>): this
Watches specified property and issues target value recalculation on its change.
()
Updates target property forcibly.
(): 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.
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(); }
<T extends Destroyable>(obj: T): T
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));
(obj: Destroyable): 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 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);
new Mapper.ByReducer<T, U>(sources: Bindable<T>[], reducer: Reducer<T, U>, target?: IProperty<U>)
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.
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.
(): 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.
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(); }
<T extends Destroyable>(obj: T): T
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));
(obj: Destroyable): 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<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>
(...sourceValues: any[]) => T
Optimized way to create a mapper with new target value. Returns target property. Destroy it to stop synchronization.
Configuration of mapProperties function. Partial configuration of Mapper.
destroy?: Mapper.DestroyCallback<T>
Destroys target property value if specified.
Signature: (targetValue: T, ...sourceValues: any[]) => any
scope?: any
create and destroy call scope. Defaults to mapper itself.
viaNull?: boolean
Reverses mapper updating flow.
Full configuration of Mapper.
destroy?: Mapper.DestroyCallback<T>
Destroys target property value if specified.
Signature: (targetValue: T, ...sourceValues: any[]) => any
scope?: any
create and destroy call scope. Defaults to mapper itself.
viaNull?: boolean
Reverses mapper updating flow.