Brent Pabst
Brent Pabst

Reputation: 1176

Pass a ViewModel to Reusable Knockout Control

I have a resusable control/custom binding & template designed to build a dynamic wizard based on a set of passed in config options and the ViewModel that is ultimately bound to the page. I've got the wizard building itself properly and displaying all of the required fields but I have been unable, so far, to get the actual input field values to display.

Any ideas?

ViewModel

var user = function () {
            var self = this;
            self.FirstName = ko.observable("Brent");
            self.LastName = ko.observable("Pabst");
            self.Email = ko.observable("[email protected]");
            self.FullName = ko.computed(function () {
                return self.FirstName() + " " + self.LastName();
            });
        };

        var tenant = function () {
            var self = this;

            self.Name = ko.observable("Tenant Name");
            self.SubDomain = ko.observable("Sub Domain");
            self.User = ko.observableArray([new user]);

            self.wizardModel = new merlin.wizard({
                title: "Add a Tenant",
                model: self,
                steps: [
                    { Title: "Tenant Information",
                        Fields: [
                            { Name: "Name", Label: "Organization Name", Value: "Name" },
                            { Name: "SubDomain", Label: "Login Page", Value: "SubDomain" }
                        ]
                    },
                    { Title: "Administrator Information",
                        Fields: [
                            { Name: "FirstName", Label: "First Name", Value: "User.FirstName" },
                            { Name: "LastName", Label: "Last Name", Value: "User.LastName" },
                            { Name: "Email", Label: "E-Mail Address", Value: "User.Email" }
                        ]
                    }
                ]
            });

            self.save = function () {
                alert(ko.toJSON(self));
            };
        };

        ko.applyBindings(tenant());

Wizard Model

    merlin.wizard = function (config) {
    var self = this;
    self.steps = config.steps;
    self.title = config.title;
    self.model = config.model;
    self.currentStep = ko.observable(0);
};

Template & Binding

var templateEngine = new ko.nativeTemplateEngine();

templateEngine.addTemplate = function (templateName, templateMarkup) {
    document.write("<script type=\"text/html\" id='" + templateName + "'>" + templateMarkup + "<" + "/script>");
};

templateEngine.addTemplate("merlin_wizard", "\
    <form class=\"m-ui-wizard\">\
        <h1 data-bind=\"text: title\" />\
        <h2 data-bind=\"text: title\" />\
        <div class=\"m-ui-wizard-steps\" data-bind=\"foreach: steps\">\
            <div class=\"m-ui-wizard-step\">\
                <!-- ko if: $data.Fields -->\
                    <!-- ko foreach: Fields -->\
                        <label data-bind=\"text: Label, attr: { for: Name }\"></label>\
                        <input type=\"text\" data-bind=\"attr: { name: Name }, Value: typeof value === 'function' ? Value($root.model) : $root.model[Value]\" />\
                    <!-- /ko -->\
                <!-- /ko -->\
            </div>\
        </div>\
    </form>");

ko.bindingHandlers.wizard = {
    init: function () {
        return { controlsDescendantBindings: true };
    },
    update: function (element, viewModelAccessor, allBindingsAccessor) {
        var viewModel = viewModelAccessor(), allBindings = allBindingsAccessor();

        while (element.firstChild)
            ko.removeNode(element.firstChild);

        var wizTemplateName = allBindings.gridTemplate || "merlin_wizard";

        var wizContainer = element.appendChild(document.createElement("DIV"));
        ko.renderTemplate(wizTemplateName, viewModel, { templateEngine: templateEngine }, wizContainer, "replaceNode");
    }
};

HTML Snippet

<div data-bind="wizard: wizardModel"></div>

Note: I have been unable to get this to work at all in Fiddle, not really sure why other than the fact that I may be pushing the boundaries of Fiddle with this setup.

Upvotes: 0

Views: 1199

Answers (1)

antishok
antishok

Reputation: 2910

Your value binding string for the <input> is incorrect. It should be more like:

value: typeof Value === 'function' ? Value($root.model) : $root.model[Value]

Here's a fiddle

Upvotes: 1

Related Questions