Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

separate knockoutjs into separate files and use requirejs to inject the content

A view model has got rather large and I want to separate it out into different files. Here is the structure I want to use:

 |- knockout-2.2.0.debug.js
 |- require.js
 |- /App
    |- Property.js
    |- /Property
       |- Fields.js
       |- Model.js
       |- ViewModel.js

Now within my Property.js file I have:

require.config({
    paths: {
        ko: "../knockout-2.2.0.debug"
    },
    baseUrl: "/Scripts/App"
})

require(['ko', 'Property/ViewModel'], function (ko, viewModel) {
    var view = new viewModel.PropertyViewModel();
    console.log(view);
    ko.applyBindings(view);

})

Model.js

define(['ko'], function(ko) {

    return {
        PropertyModel: function(id, name, note) {
            var s = this;
            s.Id = ko.observable(id);
            s.Name = ko.observable(name);
            s.Note = ko.observable(note);
        }
    };
});

Now this is where I get stuck, if I have this within my ViewModel:

define(['ko', 'Property/Model', 'Property/Field'], function(ko, model, field) {
    return {
        PropertyViewModel: function () {
            var self = this;

            self.Property = ko.observable(new model.PropertyModel("123", "Test", "note"));
        }
    };
});

it works perfect but I really want to move the fields I use (self.Property, etc) into a separate file, so in my Field.Js file I have the following:

define(['ko'], function(ko) {
    return {
        Property: ko.observable()
    };
});

and update the following line:

self.Property = ko.observable(new model.PropertyModel("123", "Test", "note"));

to

field.Property(new model.PropertyModel("123", "Test", "Note"))

but when I try to console.log the view model, it's just empty. Is this something you cannot move out and inject into the ViewModel? If not, why?

like image 944
Matt Avatar asked Jan 26 '26 04:01

Matt


1 Answers

I think bert has it right in his comments. Here are the two issues I see:

The first issue: field.Property is a ko.observable, it doesn't return a ko.observable. By calling field.Property(someValue) you're setting the field.Property observable to a new value each time you call it, every time you call it. Said another way, the ko.observable at field.Property exists in only one place; as a property of field. Calling it from viewModel doesn't make it a property of viewModel.

The second issue: even if field.Property were to return a new ko.observable each time, such as

define(['ko'], function(ko) {
    return {
        Property: function() { return ko.observable(); }
    };
});

and you modified your call to be

//Note the extra parens
field.Property()(new model.PropertyModel('123','Test','Note'));

you're still not assigning that return value (the new ko.observable) to the viewModel. You'd have to do something like:

self.myProperty = field.Property()(new model.PropertyModel('123', 'Test','Note'));

which is not nearly as readable as simply

self.myProperty = ko.observable(new model.PropertyModel('123', 'Test', 'Note'));

A Different Approach

With a slight modification to your code, you can write your models (or modules) each in their own file and nest them into one parent viewModel to make your huge view model more manageable. Here's a simplification of a pattern that works well for me:

childModel.js

define(['ko'], function(ko){
    var childModel = function(data){
        var self = this;

        self.myProperty = ko.observable(data.firstValue);
        self.myOtherProperty = ko.observable(data.secondValue);
    };

    return childModel;
});

parentModel.js

define(['ko', 'childModel'], function(ko, childModel){
    var parentModel = function(){
        var self = this;

        self.childOne = new childModel({firstValue: 'Test', secondValue: '123'});
        self.childTwo = new childModel({firstValue: 'Two', secondValue: '456'});
    };

    return parentModel;
});

main.js

requre(['ko','parentModel'], function(ko, parentModel){
    var parent = new parentModel();

    ko.applyBindings(parent);
});

html

<div>
    <!-- ko with : childOne -->
        <span data-bind="text: myProperty"></span>
        <span data-bind="text: myOtherProperty"></span>
    <!-- /ko -->
    <!-- ko with : childTwo -->
        <span data-bind="text: myProperty"></span>
        <span data-bind="text: myOtherProperty"></span>
    <!-- /ko -->    
</div>

I hope this helps!

like image 64
Ryan Rahlf Avatar answered Jan 29 '26 12:01

Ryan Rahlf



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!