How to return value from Value Converter to the view-model

Hi everyone,

I’m using a search value converter to filter a list of elements, the value converter work perfectly.
Now, I want to return the result from the value converter to the view-model, so that when a user “toggles all” elements, only they elements which was returned to the view is toggled.

So my question is: “Is it possible to get the result of the value converter returned to the view-model and how?”.

Thanks in advance

1 Like

value converters are injectable just like all other stuff in aurelia.
so you can call “toView” in your VM, and store the result in a variable. and bind to that variable.
that way - you can access it later in your VM.

another option is to use the <let> element in your view, to “store” the result of the value converter in a variable that can be acceseed later in the view or VM.

2 Likes

Thanks @avrahamcool, I actually tried this earlier but for some reason I had overlooked a property that the value converter needed to perform the search.

1 Like

Can you update your question with code?

1 Like

@manks the feedback from @avrahamcool helped me realize that I had overlooked something in my code. So using the value converter worked as intended.

If you are asking me if I could share my solution with you then let me know.

1 Like

If you wouldn’t mind some example code. I’m sure it can benefit others as well. Thank you!

1 Like

Suppose we have

export class MyValueConverter {
  compute(value) {
    return 'Value is:' + value;
  }

  toView(value) {
    return this.compute(value);
  }
}

then we can inject the value converter as:

@inject(MyValueConverter)
export class MyElement {
  constructor(myValueConverter) {
    const value = myValueConverter.toView(someOtherValue);
  }
}
2 Likes

This is how I have implemented it. I hope it helps you and many others, who might come by and see this post.

export class MyList {
	private elements: MyElementDto[] = [];
	private columnsToSearch: string[] = ['colName1', 'colName2', 'colName3', 'colName4', 'colName5'];
	private selectedElements: MyElementDto[] = [];
	private allSelected: boolean = false;
	private searchTerm: string = "";
	private viewContext: string;
	private subscriptions: Subscription[];

	constructor(private events: EventAggregator, private listService: ListService) {
		this.viewContext = "someName";		
	}

	async activate(params, routeConfig, navigationInstruction) {
		const searchHandler = (data: any) => this.handleSearchEvent(data);
		this.subscriptions = [
			this.events.subscribe(this.viewContext + ":SearchTermChanged", searchHandler)
		];
	}

	detached() {
		this.subscriptions.forEach(subscription => (
			subscription.dispose()
		));
	}

    private handleSearchEvent(data) {
		this.searchTerm = data;
	}

	async toggleAll() {
		await this.listService.toggleAll(this.elements, this.searchTerm, this.columnsToSearch).then(result => {
			this.selectedElements = result.selected;
			this.allSelected = result.allSelected;
		});
	}
 }

Service

import { SearchValueConverter } from 'resources/value-converters/search';
import { autoinject } from 'aurelia-framework';

@autoinject
export class MyService {
    allSelected: boolean = false;
    selectedElements: any[] = [];

    constructor (private searchVC: SearchValueConverter) {}

    public async toggleAll(list: any[], searchTerm: string = "", searchColumns: string[] = []): Promise<any> {
        let listFilteredBySearch: any[] = [];
        if (Boolean(searchTerm)) {
            listFilteredBySearch = this.searchVC.toView(list, {
                searchTerm: searchTerm,
                columns: searchColumns
            });
        }

        if (listFilteredBySearch.length > 0) {
            if (listFilteredBySearch.every(x => x.selected)) {
                return this.deselectAll(listFilteredBySearch);
            }

            if (listFilteredBySearch.every(x => !x.selected) || listFilteredBySearch.some(x => x.selected)) {
                return this.selectAll(listFilteredBySearch, true);
            }
        }

        if (list.every(x => x.selected)) {
            return this.deselectAll(list);
        }

        if (list.every(x => !x.selected) || list.some(x => x.selected)) {
            return this.selectAll(list);
        }
    }

    private selectAll(list: any[], isListFiltered: boolean = false) {
        list.forEach(x => {
            x.selected = true;
        });

        this.selectedElements = Array.from(new Set(list));
        this.allSelected = isListFiltered ? false : true;
        return { selected: this.selectedElements, allSelected: this.allSelected };
    }

    private deselectAll(list: any[]) {
        list.forEach(x => {
            x.selected = false;
        });

        this.selectedElements = [];
        this.allSelected = false;
        return { selected: this.selectedElements, allSelected: this.allSelected };
    }
}

Value Converter

export class SearchValueConverter {
    toView(array, config) {     
        if (!Boolean(array) || config.searchTerm === "") return array;
        return array.filter(obj => 
            this.isFound(config, obj)
        );
    }

    private isFound(config: any, obj: any): Boolean {
        let found = false;
        for (let index = 0; index < config.columns.length; index++) {
            const x = this.resolve(config.columns[index], obj);
            if (x === undefined) continue;
            if (typeof(x) === 'string') {
                found = x.toLowerCase().includes(config.searchTerm.toLowerCase());
            }
            if (found) break;
        }
        return found;
    }

    private resolve(path, obj) {
        return path.split('.').reduce(function(prev, curr) {
            return prev ? prev[curr] : null
        }, obj || self)
    }
}
2 Likes

Thank you. It does help.