4
votes

I have a series options a user can choose, which I am able to track via an observable array. The array itself is fed from a Model which I bring in to knockout via the Knockout Mapping extension (ko.mapping.fromJS). Everything is working fine.

Explaining this will get a but wordy and may cause more confusion to see the diagram below:

Situation

Basically:

  • I have a web entry form (its a configurator of sorts)
  • The item list is pretty big, I'd say 10 or so possible items can be added to a configuration
  • When a user adds an item, I push a default "Item A" into the Array bound to the options and it is rendered just fine.
  • What I am attempting to do is remove Item A from being selectable after it has been added once. If it is deleted, it should be able to be re-added
  • The way all of this is happening is via KO observables - one to track the available options, and another to track the "Selected" options. As I said, everything is working fine, and I'm trying to tweak it based on a request.

Initially - I was thinking - I would just let the users add duplicates and handle dupes via validation - if this is the only option I'll likely fall back to it.

I discovered "Post-processing the generated options" but the example provided declares the array in-line, and I'm not sure how I can attach this type of call back to an observable array I auto map using the mapping extension.

In a nutshell, I'm wondering if anyone has an idea on how to disable a previous selection (remember ALL selections are in one observable array, and the SELECTED ones are in another one) - or whether this is not possible given the source of my data. So in the hot pink selected annotation in the image - I'd ideally like only "Item B and Item C" to show up - but if ITEM A can be disabled that would work too.

I don't know if jQuery manipulation of the DOM would be viable? It would have to occur after databinding, and may get messy.

Depending on the answer here - my next screen has TWO cascading dropdowns and I was thinking of applying this same unique selection approach - but to a combination.

Some code(simplified to protect the guilty)

public class ItemsModel
{
  public List<ItemTypes> ItemTypes{ get; set; }
  public List<SelectedItemTypes> SelectedItemTypes{ get; set; }
}

public class ItemTypes
{
   public int Id { get; set; }
   public string Description { get; set; }
}

public class SelectedItemTypes
{
    public int Id { get; set; }
    public decimal Amount { get; set; }
}

**Javascript/HTML (again snipped for pertinent parts) **

      self.worksheetitems = ko.mapping.fromJS(@Html.Raw(Model.ToJson()))
      /* Adds an Item */
         self.addItem= function () {
                self.worksheetitems
               .SelectedItemTypes.push({ 'Id': ko.observable(), 
               'Amount': ko.observable(0)});

Html Table that holds this stuff (notice the foreach through the selected items, and binding to the all items array):

    <!-- Items -->
<tbody data-bind=
      "visible: worksheetitems.SelectedItemTypes().length > 0, 
       foreach: worksheetitems.SelectedItemTypes">
          <tr>
              <td>
              <select data-bind=
                      "options: $root.worksheetitems.ItemTypes(), 
                      optionsText: 'Description', 
                      optionsValue: 'Id', value: Id"></select>
              </td>           
           <tr/>
    <!-- Snipped -->

    <button data-bind="click: $root.addItem">Add</button> 
    <!-- Add Another Item -->
1
Well explained question, but it would help if you'd add some code to the question help us make a minimal reproducible scenario. What you want is certainly possible (without any jQuery) but the details will depend on your context. - Jeroen
sure I will add some code - Ta01
I think you may be able to utilize the option to specify callbacks for when mapping creates items to tweak the generated observable. You could try to combine that with the setOptionsDisable post processing you mentioned. Would that help? - Jeroen
@Jeroen thanks for your reply, let me try that - Ta01

1 Answers

1
votes

Not sure if I understand correctly, but it sounds like you're looking for Computed Observables:

self.AvailableItemTypes = ko.computed(function() {
    var selectedTypeIds = this.SelectedItemTypes().map(function(el) {
        return el.Id; 
    });
    return this.ItemTypes().filter(function(el) {
        return selectedTypeIds.indexOf(el.Id) === -1;
    });
});