Question

I am composing the same form multiple times in the same module view but when I change the data in one field it reflects on every forms.

Here is some cleaned up code. As you can see I made sure not to use the singleton pattern for the form I want to compose multiple times...

view.html

<button data-bind="click: append">Append</button>

<div data-bind="foreach: odbcForms">
    <div data-bind="compose: { model: $data, activationData: settings }"></div>
</div>

viewmodel.js

define(['knockout', 'forms/odbc'], function ( ko, odbcForm) {
    var odbcForms = ko.observableArray()

    var append = function () {
        odbcForms.push(new odbcForm({ hostname: 'v1', db: 'v2' }));
    };

    return {
        odbcForms: odbcForms,
        append: append
    }
}

forms/odbc.html

<div>
    <form class="form-horizontal" role="form">
        <fieldset>
            <div class="form-group" data-bind="validationElement: hostname">
                <label for="hostname" class="col-sm-2 control-label">ODBC Link Name</label>

                <div class="col-xs-4">
                    <input data-bind="value: hostname" type="text" class="form-control" id="hostname">
                </div>
            </div>

            <div class="form-group" data-bind="validationElement: db">
                <label for="db" class="col-sm-2 control-label">Database</label>

                <div class="col-xs-4">
                    <input data-bind="value: db" type="text" class="form-control" id="db">
                </div>
            </div>
        </fieldset>
    </form>
</div>

forms/odbc.js

define(['knockout'], function(ko) {
    var ctor = function (settings) {
        this.settings = settings;
    };

    ctor.prototype = {
        constructor: ctor,

        activate: function (settings) {
            this.hostname(this.settings.hostname);
            this.db(this.settings.db);
        },

        hostname: ko.observable().extend({
            required: true,
            minLength: 2
        }),

        db: ko.observable().extend({
            required: true,
            minLength: 2
        }),
    };

    return ctor;
}

Thank you in advance

Was it helpful?

Solution

Change your forms/odbc.js to the following:

define(['knockout'], function(ko) {
    var ctor = function () {
        this.settings = settings;
        this.hostname = ko.observable().extend({
            required: true,
            minLength: 2
        };
        this.db = ko.observable().extend({
            required: true,
            minLength: 2
        });
    };        

    ctor.prototype.activate = function (settings) {
        this.hostname(this.settings.hostname);
        this.db(this.settings.db);
    };

    return ctor;
});

It might be that Durandal is confused by your approach. Among other issues in your forms/odbc module, there should be no argument on the constructor (ctor function). Also, it is not a good idea to place observables on a prototype--you'll leak memory. If you really wish to establish hostname and db as some sort of global, create a separate config module, make it a singleton, and then inject it using RequireJS, like so:

define('config', ['knockout'], function(ko) {
    var 
        hostname = ko.observable(),
        db = ko.observable();

    return {
        hostname: hostname,
        db: db
    }
});

Then change your odbc.js to the following:

define(['knockout', 'config'], function(ko, config) {
    var ctor = function (settings) {
        this.settings = null;     
        this.config = null;
    };        

    ctor.prototype.activate = function (settings) {
        this.settings = settings;
        this.config = config;
        config.hostname(settings.hostname);
        config.db(settings.db);
    };

    return ctor;
});

Your view would need to be updated slightly: Instead of value: hostname, for example, you would need to change to value: config.hostname.

With this approach, you can build out your config module as your application grows without having to refactor every module. Also, you do know that you don't have to call it ctor, right? In order to facilitate debugging, give your module an explicit name, say OdbcForm, so that you would have:

var OdbcForm = function()
...
return OdbcForm;

It will show up in the debugger as OdbcForm instead of ctor.

OTHER TIPS

Prototypes are shared. You should not put property values on the prototype. property values should go on instances. Put only functions. All properties (observables) should go on the "this" object instance.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top