This is a migrated thread and some comments may be shown as answers.

PRISM like InteractionRequest

0 Answers 67 Views
MVVM
This is a migrated thread and some comments may be shown as answers.
Remco
Top achievements
Rank 1
Remco asked on 13 Nov 2012, 12:41 PM
I was looking for a PRISM like InteractionRequest to use modal dialogs with Kendo UI MVVM. I could not find one and ended up creating one. I'll share it here as it would be great if it could be improved on or even adopted in the framework. It's usage is as follows:
<div style="display: none;"
    data-role="window"
    data-bind='interactionRequest: { path: editOpportunityInteractionRequest, template: editOpportunityTemplate }'
    data-title="Edit"
    data-modal="true"
    data-visible="false">
</div>
I created a custom binding called interactionRequest. You bind it to an InterationRequest object in your view model and you need to provide the id of a template that will be rendered in the window. An InteractionRequest has a raise method that you pass a view model to be bound to the window and returns a promise that will be resolved when a result is set on the view model. I'm using TypeScript for this.
import App = module("./DataContext");
import Interactivity = module("./InteractionRequest");
import Models = module("./Models");
import ViewModels = module("./EditOpportunityViewModel");
 
export class OpportunitiesViewModel extends kendo.data.ObservableObject {
 
    editOpportunityInteractionRequest = new Interactivity.InteractionRequest();
    dataContext: App.DataContext;
 
    constructor (dataContext: App.DataContext) {
        super();
        this.set("dataContext", dataContext);
    }
 
    onCustomCreate(e: JQueryEventObject) {
        e.preventDefault();
        var opportunity = new Models.Opportunity();
        this.dataContext.opportunities.insert(0, opportunity);
        var viewModel = new ViewModels.EditOpportunityViewModel(opportunity, this.dataContext);
        this.editOpportunityInteractionRequest.raise(viewModel).done((viewModel: ViewModels.EditOpportunityViewModel) => {
            if (!viewModel.result) {
                this.dataContext.opportunities.remove(viewModel.opportunity);
            }
        });
    }
 
    onCustomEdit(e: JQueryEventObject) {
        e.preventDefault();
        var opportunity = <Models.IOpportunityModel>e.data;
        var viewModel = new ViewModels.EditOpportunityViewModel(opportunity, this.dataContext);
        this.editOpportunityInteractionRequest.raise(viewModel).done(viewModel => {
            if (!viewModel.result) {
                this.dataContext.cancelChanges();
            }
        });
    }
}


The InteractionRequest itself is very simple:
/// <reference path="..\lib\jquery.d.ts"/>
/// <reference path="..\lib\kendo.web.d.ts"/>
 
export class InteractionRequest extends kendo.data.ObservableObject {
    promise: JQueryPromise;
     
    raise(viewModel: { result: any; }) {
        this.trigger("raise", viewModel);
        return this.promise;
    }
}
The raise method expects that the raise event it triggers is handled by the interactionRequest custom binding that will set the promise on the InteractionRequest so that it may be returned by the raise method.

This is the custom binding code (which is still in JavaScript)
kendo.data.binders.widget.interactionRequest = kendo.data.Binder.extend({
    init: function (element, bindings, options) {
        kendo.data.Binder.fn.init.call(this, element, bindings, options);
        var that = this,
            binding = bindings.interactionRequest;
        that.template = kendo.template($("#" + binding.path.template).html());
        that.resultHandler = function (e) {
            if (e.field === "result") {
                that.element.close();
            }
        };
        that.closeHandler = function () {
            that.viewModel.unbind("change", that.resultHandler);
            var widget = that.element,
                container = widget.element.find(".k-edit-form-container");
            kendo.destroy(container);
            that.deferred.resolve(that.viewModel);
        };
        that.element.bind("close", that.closeHandler);
    },
    refresh: function (attribute) {
        var that = this,
            binding = that.bindings.interactionRequest,
            source = binding.source.get(binding.path.path);
        if (that.previousSource) {
            that.previousSource.unbind("change", that.raiseHandler);
        }
        that.raiseHandler = function (e) {           
            that.deferred = new $.Deferred();
            source.promise = that.deferred.promise();
            if (e instanceof kendo.data.ObservableObject) {
                that.viewModel = e;
            } else {
                that.viewModel = kendo.observable(e);
            }
            that.viewModel.bind("change", that.resultHandler);
            var widget = that.element;
            widget.content('<div class="k-edit-form-container"></div');
            var container = widget.element.find(".k-edit-form-container");
            container.html(that.template(that.viewModel));
            that.viewModel.validator = container.kendoValidator().data("kendoValidator");
            kendo.bind(container, that.viewModel);
            widget.center().open();           
        };
        source.bind("raise", that.raiseHandler);
        that.previousSource = source;
    },
    destroy: function () {
        var that = this,
            binding = that.bindings.interactionRequest,
            source = binding.source.get(binding.path);
        that.element.unbind("close", that.closeHandler);
        if (that.raiseHandler) {
            source.unbind("raise", that.raiseHandler);
        }
    }
});

It will open the window when the InteractionRequest triggers its raise event. It will bind the view model passed to the raise method of the InteractionRequest to the template rendered in the window. When a result is set on this view model, the window is closed and the deferred is resolved. In my case I set the result to true when an update button is clicked and to false when a cancel button is clicked.
import App = module("./DataContext");
import Models = module("./Models");
 
export class EditOpportunityViewModel extends kendo.data.ObservableObject {
 
    result: bool;
    validator: kendo.ui.Validator;
 
    constructor (public opportunity: Models.IOpportunityModel, public dataContext: App.DataContext) {
        super();       
    }
 
    update(e: JQueryEventObject) {
        e.preventDefault();
 
        if (!this.validator.validate()) {
            return;
        }
 
        this.dataContext.sync().done(() => {
            this.set("result", true);
        });       
    }
 
    cancel(e: JQueryEventObject) {
        e.preventDefault();
        this.set("result", false);
    }
}


I specifically think the custom binding could be improved with regards to the specification of the template to be rendered in the window. Preferably I set a custom data-template option on the div, but this does not currently get  passed into the custom binding. I will also need to figure out how to trigger validation on the dialog before I set the dialog result to true. 
EDIT: added validation

No answers yet. Maybe you can help?

Tags
MVVM
Asked by
Remco
Top achievements
Rank 1
Share this question
or