Asle G
Asle G

Reputation: 588

How to access knockout observables from different object scopes

My data model is consisting of two objects; project and task. I load my data from the db via json and MVC-services and map my observableArrays like this:

viewModel = function () {
    var self = this;

    // some code...

    // projects
    self.Projects = ko.observableArray();
    var mappedProjects = [];
    $.ajax({
        url: "myService/GetProjectsByUserId",
        data: "userID=" + meID,
        dataType: 'json',
        async: false,
        success: function (allData) {
            mappedProjects = $.map(allData, function (item) {
                return new Project(item);
            });
        }
    });
    self.Projects(mappedProjects);

    // tasks
    self.Tasks = ko.observableArray();
    var mappedTasks = [];
    $.ajax({
        url: "myService/GetTasksByUserID",
        data: "userid=" + meID,
        dataType: 'json',
        async: false,
        success: function (allData) {
            mappedTasks = $.map(allData, function (item) {
                return new Task(item, self.Projects);    // is there a smarter way to access self.Projects from the Scene prototype?
                //return new Task(item);
            });
        }
    });
    self.Tasks(mappedTasks);


    //some more code...

};

where

Project = function (data) {
    this.projectID = data.projectID;
    this.type = ko.observable(data.type);
};


Task = function (data, projects) {

    this.taskID = data.taskID;
    this.projectID = data.projectID;

    //this.projecttype = ??? simpler solution?

    this.projecttype = ko.computed(function () {   // Is there a simpler way to access 'viewModel.Projects' from within 'Task'?
        var project = ko.utils.arrayFirst(projects, function (p) {
            return p.projectID === self.projectID;
        });
        if (!project) {
            return null;
        }
        else {
            return project.headerType();
        }
    });

};

The thing is (as you see) I want to access the projectType inside the Task-object. Is there a simpler way to do this than instantiating the object with the self.Projects as input?

Could self.Projects be bound when defined in some way so I could access it via the DOM?

Upvotes: 1

Views: 131

Answers (1)

Dandy
Dandy

Reputation: 2177

From your comments, it looks like that you have multiple view models dependent on Task and Project objects. For decoupling between components, i would say to use ko.postbox plugin. You can easily have synchronization between viewmodels and non-knockout components using publishOn and subscribeTo extensions.

So your Task object will subscribe to Projects observableArray in viewModel like

Task = function (data) {

    this.taskID = data.taskID;
    this.projectID = data.projectID;
    var projects = ko.observableArray().subscribeTo("projectsLoaded",true);

    //this.projecttype = ??? simpler solution?

    this.projecttype = ko.computed(function () {   // Is there a simpler way to access 'viewModel.Projects' from within 'Task'?
        var project = ko.utils.arrayFirst(projects(), function (p) {
            return p.projectID === self.projectID;
        });
        if (!project) {
            return null;
        }
        else {
            return project.headerType();
        }
    });

};

and in your viewModel, you just have to make Projects observable array publish "projectsLoaded" topic/event. Like

viewModel = function () {
    var self = this;
    // some code...
    // projects
    self.Projects = ko.observableArray().publishOn("projectsLoaded");
    // ....
}

Whenever the projects array changes in viewModel, you will always have the latest value in Task project array.

JsFiddle: http://jsfiddle.net/newuserjs/wffug341/3/

Upvotes: 1

Related Questions