0

I have a bootstrap nav-tab and I want to display dynamically content when I select a tab. Ajax returns an array of Results. Each result has Price,Logo,Companyname and an array of Covers. Each cover has Price,MaxCover,Optional and Description. Rest of html code is here link but now I want to return a more complex type.

<script type='text/javascript'>

var cover = new
    {
        Price: ko.observable(),
        MaxCover: ko.observable(),
        Optional: ko.observable(),
        Description: ko.observable(),
    }
var result = new
    {
        Price: ko.observable(),
        InsLogo: ko.observable(),
        CompanyName: ko.observable(),
        Covers: ko.observableArray()
    };

var tab = function (Id, name, selected) {
    this.Id = Id;
    this.name = name;
    this.Results = ko.observableArray();
    this.isSelected = ko.computed(function () {
        return this === selected();
    }, this);
}

var ViewModel = function () {
    var self = this;
    self.selectedTab = ko.observable();
    self.tabs = ko.observableArray([
        new tab(0, 'Tab1', self.selectedTab),
        new tab(1, 'Tab2', self.selectedTab),
        new tab(2, 'Tab3', self.selectedTab)
    ]);
    self.selectedTab(self.tabs()[0]);

    self.selectedTab.subscribe(function () {
        $.ajax({
            url: '@Url.Action("GetSection")',
            data: { Id: self.selectedTab().Id },
            type: 'GET',
            success: function (data) {
                self.selectedTab().Results(data.Results); //Here I want to fill the results!!!!!!
            }
        });

    });

}
ko.applyBindings(new ViewModel());

2
  • 1
    any issue in particular you facing ? as far i see data isn't that complex . add a fiddle so i can help u with . cheers Commented Jan 2, 2016 at 12:09
  • 1
    You don't need to use new with object literals {} - in fact, that's an error. Using {} is enough. Commented Jan 2, 2016 at 12:27

1 Answer 1

3

Your approach is okay with a few small glitches. Some suggestions to improve it:

  • Make your viewmodels so that they initialize themselves from a parameters object.
  • Don't introduce dependencies between viewmodels when you don't have have to. I'm thinking of the isSelected property here, this can be taken care of in the view. For example, when inside a foreach: tabs: data-bind="css: {selected: $data === $parent.selectedTab()}"
  • You have a timing issue: Subscribe to selectedTab first, then initialize it with self.selectedTab(self.tabs()[0]);. It should be obvious why. (Generally it's useful to split viewmodel creation into a "setup" and an "init" phase.)
  • Only send an Ajax request for tab details when tab details are still empty.
  • Subscribtions receive the new value of the observable as an argument, use it.
  • Observables are functions:
    • If you want to store an Ajax response in them you can use them as a callback directly.
    • In the same way you can use them as an event handler:
      data-bind="click: $parent.selectedTab".
  • JS convention is to use PascalCase for constructor names (like in viewmodels) and camelCase for all other identifiers.

With all that, we get:

function Tab(data) {
    this.Id = data.Id;
    this.name = data.name;
    this.Results = ko.observableArray();
}
function ViewModel(data) {
    var self = this;

    // setup
    self.selectedTab = ko.observable();
    self.selectedTab.subscribe(function (selectedTab) {
        if (selectedTab.Results()) return;
        $.get('@Url.Action("GetSection")', {Id: selectedTab.Id}).done(selectedTab.Results);
    });

    // init
    self.tabs = ko.observableArray(ko.utils.arrayMap(data.tabs, function (tabData) {
        return new Tab(tabData);
    }));
    self.selectedTab(self.tabs()[0]);
}
ko.applyBindings(new ViewModel({
    tabs: [
        {Id: 0, name: 'Tab1'},
        {Id: 1, name: 'Tab2'},
        {Id: 2, name: 'Tab3'}
    ]
}));

To convert plain data structures that come from the server (like your array results and covers) into a structure of viewmodels, observables and observable arrays I recommend looking into the mapping plugin.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.