Load tabs with Ajax

2019-08-12 16:22发布

I have a bootstrap nav-tab and I want to display dynamically content when I select a tab. Each tab must display a div with some text that is returned from ajax call at the controller's action GetSection().

<div class="tabbable">
    <ul class="nav nav-tabs" data-bind="foreach: sections">
        <li data-bind="css: { active: isSelected }">
            <a href="#" data-bind="click: $parent.selectedSection">
                <span data-bind="text: name" />
            </a>
        </li>
    </ul>

    <div class="tab-content" data-bind="foreach: sections">
        <div class="tab-pane" data-bind="css: { active: isSelected }">
            <span data-bind="text: 'In section: ' + retValue" />
        </div>
    </div>
</div>

Javascript code:

var Section = function (name, selected) {
    this.name = name;
    this.retValue = "";
    this.isSelected = ko.computed(function () {
        return this === selected();
    }, this);
}

var ViewModel = function () {
    var self = this;
    self.selectedSection = ko.observable();
    self.sections = ko.observableArray([
        new Section('Tab One', self.selectedSection),
        new Section('Tab Two', self.selectedSection),
        new Section('Tab Three', self.selectedSection)
    ]);
    self.selectedSection(self.sections()[0]);

    self.selectedSection.subscribe(function () {
        $.ajax({
            url: '@Url.Action("GetSection")',
            data: { name: self.selectedSection().name },
            type: 'GET',
            success: function (data) {
                self.selectedSection().retValue=data.text;
            }
        });

    });

}

ko.applyBindings(new ViewModel());

The problem is that retValue from ajax is not displayed. The controller action is this:

public JsonResult GetSection(string name)
{
    var ret = new { text = name + "abcd" };
    return Json(ret, JsonRequestBehavior.AllowGet);
}

2条回答
劫难
2楼-- · 2019-08-12 16:59

Instead of this self.selectedSection().retValue=data.text;

Do this self.selectedSection(data);

查看更多
姐就是有狂的资本
3楼-- · 2019-08-12 17:19

Knockout can only know to update the view for properties that are obsverable (hence the name), so you need to make retValue observable:

var Section = function (name, selected) {
    this.name = name;                                // <-- consider similar change here too
    this.retValue = ko.observable("");               // <-- change here
    this.isSelected = ko.computed(function () {
        return this === selected();
    }, this);
}

Then, you need to remember to set an obsverable's value by calling it as a method with the new value as its only argument, e.g.:

    $.ajax({
        url: '@Url.Action("GetSection")',
        data: { name: self.selectedSection().name },
        type: 'GET',
        success: function (data) {
            self.selectedSection().retValue(data.text);    // <-- change here
        }
    });

And finally, if you're binding to a complex expression in your view you need to invoke it as a function (with no arguments) to get its value:

<span data-bind="text: 'In section: ' + retValue()" />

As a side note, realize that you can leave off the parentheses (consider it syntactic sugar from knockout) if you bind straight to just the observable, e.g.:

<span data-bind="text: retValue" />

Which is effectively equivalent to:

<span data-bind="text: retValue()" />

On a foot note, I see you've used this syntax for a click binding:

<a href="#" data-bind="click: $parent.selectedSection">...</a>

This works... but only by coincidence. You should realize these things together:

  • $parent.selectedSection contains the result of ko.observable() which means it is in fact a function that can be invoked
  • the click data-binding will invoke the expression it gets as a function, passing the contextual data (in your case a Section) to that function

So bascially, when the click happens, this happens:

$parent.selectedSection($data) // where $data == the current Section

Which effectively selects the Section.

It would be more verbose though a lot clearer if the $parent had a function:

var self = this;
self.selectChild = function(section) {
  // Possibly handle other things here too, e.g. clean-up of the old selected tab
  self.selectedSection(section);
}

And then use the click binding in this clear way:

<a href="#" data-bind="click: $parent.selectChild">...</a>

On click the selectChild method will be called, again with the contextual data as the argument.

查看更多
登录 后发表回答