Referencing properties of object constructor outsi

2019-09-22 04:09发布

问题:

So, I'm not entirely sure how to phrase this question as it's sort of two in one. I'm having a weird issue where I have an object constructor to create new 'projects' from an HTML form which are then pushed into an observableArray when the form is submitted. Everything works fine but to reference the related observables I have to use 'value: Project.title' or 'value: Project.whatever'. I haven't seen 'value: NameOfConstructor.property' used in any of the examples I've seen. I assume this works this way because the constructor is outside of my view model.

My question is this: Is there a better way to assign the value of a property in a constructor that is not in my view model? In other words, is there a better or more correct way than 'Project.title', ect? I ask partially because one thing in my code doesn't work currently; the knockout enable property doesn't work on my "New Project" button, it stays disabled even if there is something written in the 'title' input box. I have the feeling it's because it's written as data-bind='enable: Project.title' but I can't figure how else to write it.

I've included a jsfiddle for reference though it obviously isn't working because of external dependencies. https://jsfiddle.net/bmLh0vf1/1/

My HTML:

<form id='addBox' action='#' method='post'>
    <label for='pTitle'> Title: </label>
    <input id='pTitle' data-bind='value: Project.title' />
    <br/>
    <label for='pPriority'> Priority </label>
    <select id='pPriority' data-bind='options: priorityOptions, value: Project.priority'></select>
    <br/>
    <button data-bind='enable: Project.title, click: newProject'>New Project</button>
  </form>

And my Javascript:

function Project(title, priority) {
  this.title = ko.observable(title);
  this.priority = ko.observable(priority);
};


function ProjectViewModel() {
  var self = this;
  this.priorityOptions = ko.observableArray(['High', 'Medium', 'Low'])
  this.projectList = ko.observableArray([
    new Project('Create App', 'High')
  ]);
  this.newProject = function() {
    var np = new Project(Project.title, Project.priority);
    self.projectList.push(new Project(Project.title, Project.priority));
    console.log(self.projectList().length);
    if (self.projectList().length > 1) {
      console.log(self.projectList()[1].title());
    };
  }
};

var viewModel = new ProjectViewModel();
$(document).ready(function() {
  ko.applyBindings(viewModel);
});

Lastly, I apologize if I've missed any posting conventions or if my code is especially bad. I'm very new and still teaching myself.

回答1:

Your code is setting title and priority properties on the object created by new Project, but then later you're expecting to see those properties on Project itself. It doesn't have them; Project is the function, not the object created by new Project. So Project.title and Project.priority will give you undefined (and not an observable, and so not useful targets for the value binding).

Instead, have an "editing" Project instance that you use, binding the value of the inputs to the editing' instances title and priority, and then in newProject grab that instance and replace it with a new, fresh one.

Roughly speaking, in ProjectViewModel's constructor:

this.editing = ko.observable(new Project());

Update Project to default title and priority:

function Project(title, priority) {
  this.title = ko.observable(title || "");
  this.priority = ko.observable(priority || "Medium");
}

And in the bindings:

<input id='pTitle' data-bind='value: editing().title' />
<select id='pPriority' data-bind='options: priorityOptions, value: editing().priority'></select>

And in newProject:

var np = this.editing();
this.editing(new Project());

Then use np (instead of another new Project) when adding to the array.

Here's a simplified example:

function Project(title, priority) {
  this.title = ko.observable(title || "");
  this.priority = ko.observable(priority || "Medium");
}


function ProjectViewModel() {
  var self = this;
  this.priorityOptions = ko.observableArray(["High", "Medium", "Low"]);
  this.projects = ko.observableArray();
  this.editing = ko.observable(new Project());
  this.addProject = function() {
    this.projects.push(this.editing());
    this.editing(new Project());
  };
}

ko.applyBindings(new ProjectViewModel(), document.body);
<div>
  <div>
    <label>
      Title:
      <input type="text" data-bind="value: editing().title, valueUpdate: 'input'">
    </label>
  </div>
  <div>
    <label>
      Priority:
      <select data-bind='options: priorityOptions, value: editing().priority'></select>
    </label>
  </div>
  <div>
    <button type="button" data-bind="click: addProject, enable: editing().title">Add Project</button>
  </div>
  <hr>
  <div>Projects:</div>
  <div data-bind="foreach: projects">
    <div>
      <span data-bind="text: title"></span>
      (<span data-bind="text: priority"></span>)
    </div>
  </div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>