0

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("me@brentpabst.com");
            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.

Brent Pabst
  • 1,156
  • 1
  • 15
  • 37
  • I doubt you are "pushing the boundaries of Fiddle." When I try to run this in fiddle, I get errors. In knockout about missing 'nodeType'. – Kyeotic Jul 10 '12 at 19:38
  • @Tyrsius OK, well then any ideas what's wrong with it? – Brent Pabst Jul 10 '12 at 20:05
  • I didn't spend any more time on it. I was just pointing out that your fiddle failing has less to do with you"pushing the boundaries of fiddle", and more to do with syntax errors. – Kyeotic Jul 10 '12 at 23:00
  • @Tyrsius Yes, the syntax errors appear to come from two places, the global variables in use as well as the backslash line terminator methods. Running JSLint in Fiddle only shows those errors, which I can't get rid of. – Brent Pabst Jul 10 '12 at 23:05

1 Answers1

1

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

antishok
  • 2,910
  • 16
  • 21
  • That's pretty close but the sub-object "User" values aren't coming through in you're fiddle properly. Any idea why? – Brent Pabst Jul 11 '12 at 12:36
  • Because 1) `User` is an observableArray so you'd need `User()[0].FirstName`. 2) More importantly, you can't access sub-object properties using just one pair of brackets. if Value is "User.FirstName", then `$root.model[Value]` would be `$root.model["User.FirstName"]` but the model has no property called `User.FirstName`. you'd need to split up that string, or find another way to deal with your cross-object references – antishok Jul 11 '12 at 19:01