Frage

What I'm trying to achieve: A form with one text input field(Name), two select fields with some options(Type & Column) with a submit button that creates a Widget with it's title set to Name, data-type set to Type and Column - being it's position in the page.

Type in this case has a couple of different options defined in the view, it works just dandy so I won't go into how I got that working, for now.

As of now I have a page with three columns, each as it's own observableArray - like so:

self.col0 = ko.observableArray([]);
self.col0.id = 'col0'

The same goes for col1 and col2. My goal here is to get the select field to point to these arrays. Atleast that's what I think I need to do.

I have a createWidget function, that looks at the Name and Type values in the DOM (Correct me if I'm wrong here, this is the very first thing I'm creating with KnockOut) and creates a new Widget from that data - like so:

var Widget = function(name, type) {
  var self  = this;
  self.name = name;
  self.type = type;
}; 

var ViewModel = function() {
  var self = this;
  self.newWidgetName = ko.observable();
  self.newType       = ko.observable();

// Unrelated code in between

self.createWidget = function(target) {
  newName = self.newWidgetName();
  newType = self.newType();
  widget  = new Widget(newName, newType);
  target.unshift(widget)
  self.newWidgetName("");
};

The input/select elements in the DOM

input.widget-name type="text" placeholder="wName" data-bind="value: newWidgetName"

select data-bind="value: newType"
  option value="calendar" Calendar
  option value="article"  Article
  option value="document" Document

select.colPick data-bind="value: colPick"
  option value="col0" Column 1
  option value="col1" Column 2
  option value="col2" Column 3

And my click handler for the createWidget function - like so:

a.btn.create-widget data-bind="click: function(){ createWidget(col0); }"

Shazam, it works!

However, this will only ever output the new Widget into the first col (col0), it won't take the value of the column select field into account and unshift the new widget into the correct column. The errors I'm getting after much trial and error pretty much boils down to:

1) "Cannot call method unshift of undefined"

2) "Uncaught TypeError: Object function observable() .... has no method 'unshift'

So as of right now the code looks like the example above, It's not ideal by any means but with the different columns connected with knockout-sortable, it's not a huge deal if this functionality has to get scrapped. The user can still move the Widgets around from col0 to col2 and vice versa.

If anyone has a resource that would point me in the right direction, or the answer up their sleeve - feel free to share!

All the best, Kas.

Edit: Followup questions for Tyrsius

With the code you supplied I now have the three columns working in the select box, however I am struggling a bit when it comes to outputting the Widgets into view.

With my previous code, this is what the view looked like:

<div class="cols">
  <div class="col col-25">
    <ul data-bind="sortable: { template: 'widgetTmpl', data: col0, afterMove: $root.widgetData }">
    </ul>
  </div>
  <div class="col col-50">
    <ul data-bind="sortable: { template: 'widgetTmpl', data: col1, afterMove: $root.widgetData }">
    </ul>
  </div>
  <div class="col col-25">
    <ul data-bind="sortable: { template: 'widgetTmpl', data: col2, afterMove: $root.widgetData }">
    </ul>
  </div>
</div>

What I'm working with right now is this:

<div data-bind="foreach: columns">
  <div data-bind="foreach: items" class="col">
    <ul data-bind="sortable: { template: 'widgetTmpl', data: columns, afterMove: $root.widgetData }"></ul>
  </div>
</div>

My first question, I realised I wasn't thinking at the time so skip that one.

Question #2: How would I now get these Widgets bound to the col that I picked in the form? Would I do this?

<ul data-bind="sortable: { template: 'widgetTmpl', data: entryColumn, afterMove: $root.widgetData }"></ul>

Or am I way off? :)

War es hilfreich?

Lösung

I would keep a collection of the items on the column as a type, which would look like this:

var Column = function(header) {
    this.header = ko.observable(header);
    this.items = ko.observableArray([]);
};

The trick would be to bind the column select straight to the list of columns on your viewmodel:

<select data-bind="options: columns, optionsText: 'header', value: entryColumn"

Whats happening here is that the actual column object that is selected by the dropdown will be stored in the entryColumn property. Later we can put the item directly into its items list since we will have a reference to it. This also allows us to support any number of columns without changing the logic.

The add code would stay simple:

self.columns = ko.observableArray(columns);

self.entryName = ko.observable('');
self.entryType = ko.observable('');
self.entryColumn = ko.observable('');
self.types = ko.observableArray(['Small', 'Medium', 'Large']);

self.addWidget = function() {
        var widget = new Widget(self.entryName(), self.entryType());
        //Here is where we can directly add to the selected columns item list
        self.entryColumn().items.push(widget);
        self.resetForm();
    };

Here is the fiddle demonstrating these.


FollowUp Question

You're close, but the data is the items not the EntryColumn

<div data-bind="foreach: columns">
    <div data-bind="sortable: { template: 'widgetTmpl', data: items}" class="column">        
    </div>
</div>

<script type="text/html" id="widgetTmpl">
    <p data-bind="text: name() + ' - ' + type()"></p>
</script>

Here is the updated fiddle.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top