title: One to many relationship on viewModels of Knockout.js
body: <p>I am newbie on KO. I try to design a simple application with knockout.js, but I couldn't figure out how to design modelViews and models.</p>
  
  <p>I try to load tasks when the a feature row clicked. But after once load, then I want to show it from memory, not from database. For example when all features clicked, then there will be no database query on current session.</p>
  
  <p>On KO, I don't know where to put my observableArrays (tasks,notes). </p>
  
  <p>Should they be a property of models or viewModels?</p>
  
  <p>Here my application structure.
  Feature(s) has many tasks, task(s) has many note.
  <a href="https://i.stack.imgur.com/RXoSe.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/RXoSe.png" alt="Feature, task,note"></a></p>
  
  <p>UI is like...
  <a href="https://i.stack.imgur.com/ZVxjQ.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/ZVxjQ.png" alt="enter image description here"></a></p>
  
  <p>ViewModels:</p>
  
  <pre><code>var taskViewModel = {
       notes: ko.observableArray(),
  
       addNote: function (newNote) {
           this.notes.push(newNote);
    }
  } 
  
  var featureViewModel = {
      tasks: ko.observableArray(),
  
      addTask: function(newTask){
               this.tasks.push(newTask);
  },
  
  taskClicked: function (selectedTask) {
          GetNotes(selectedTask.TaskId);
           }
  }
  
  var projectViewModel = new projectViewModelFunc();
  function projectViewModelFunc() {
      var self = this;
      self.features = ko.observableArray();
  
      self.addFeature = function (newFeature) {
           this.features.push(newFeature);
         };
  }
  
  ko.applyBindings(taskViewModel, $('#notes')[0]);
  ko.applyBindings(featureViewModel, $('#tasksPanel')[0]);
  ko.applyBindings(projectViewModel, $('#features')[0]);
  </code></pre>
  
  <p>Models:</p>
  
  <pre><code>function noteModel(data) {
     this.UserCreatedFullName= data.UserCreated.FullName;
     this.CreatedDate = data.CreatedDate;
     this.NoteId = data.NoteId;
     this.Content = data.Content;
     this.TaskId = data.TaskId;
  
     this.TaskHeader = function () { return this.UserCreatedFullName + " (" + this.CreatedDate + ") : "; };
  }
  
  function taskModel(data) {
      this.TaskId = data.TaskId;
      this.Title = data.Name;
      this.Status = data.Status.Name;
      this.CreatedDate = data.CreatedDate;
      this.UserCreatedFullName = data.UserCreated.FullName;
  }
  
  function featureModel(data, selected) {
      var self = this;
      self.FeatureId = data.FeatureId;
      self.Name = data.Name;
      self.Status = data.Status.Name;
      self.CreatedDate = data.CreatedDate;
      self.UserCreatedFullName = data.UserCreated.FullName;
  
      this.IsSelected = ko.computed(function () {
          return selected() === self;
      });
  }
  </code></pre>
  
  <p>DB context functions(just for more clearance)</p>
  
  <pre><code>function GetNotes(taskId) {
      var url = "/Note/GetNotes";
      taskViewModel.resetNotes();
      $.get(url, { "TaskId": taskId }, function (data) {
          $.each(JSON.parse(data), function (i, item) {
              taskViewModel.addNote(new noteModel(item));
          });
      });
  };
  
  function GetFeatures() {
      var url = "/Project/GetFeatures";
      taskViewModel.resetNotes();
      $.get(url, "", function (data) {
          $.each(JSON.parse(data), function (i, item) {
              projectViewModel.addFeature(new featureModel(item, projectViewModel.selectedFeature));
          });
      });
  };
  
  function GetTasks(FeatureId) {
      var url = "/Task/GetTaskList";
      $.get(url, { "FeatureId": FeatureId }, function (data) {
  
          $.each(JSON.parse(data), function (i, item) {
              featureViewModel.addTask(new taskModel(item));
          });
      });
  };
  </code></pre>