Warm tip: This article is reproduced from stackoverflow.com, please click
arrays javascript knockout.js

How do I toggle a knockout foreach div individually without adding more html?

发布于 2020-04-23 12:22:10

I used the foreach method to create markup foreach item in an observable array to create a treeview. output example

category name1 content content category name 2 content content

when I click on the category name I want just its content to show/hide, currently when I click on the category name it shows and hides all the categories.

 var reportFilters = [
        { Text: "Campaign", Value: primaryCategories.Campaign },
        { Text: "Team", Value: primaryCategories.Team },
        { Text: "Agent", Value: primaryCategories.Agent },
        { Text: "List", Value: primaryCategories.List },
        { Text: "Inbound", Value: primaryCategories.Inbound },
        { Text: "Daily", Value: primaryCategories.Daily },
        { Text: "Services", Value: primaryCategories.Services },
        { Text: "Occupancy", Value: primaryCategories.Occupancy },
        { Text: "Data", Value: primaryCategories.Data }
    ];


  self.showCategory = ko.observable(false);      
    self.toggleVisibility = function (report) {
        var categoryName = report.PrimaryReportCategory;
        var categoryContent = report.ID;
        if (categoryName == categoryContent ) {
            self.showCategory(!self.showCategory());
        };
    }
  <div class="report-category-treeview"  data-bind="foreach: $root.categories, mCustomScrollBar:true">
            <ul class="column-list" >
                <li class="report-category-heading" data-bind="click: $root.toggleVisibility"><span class="margin-top10" ><i class="fas fa-chevron-down"></i> <span class="report-category-name" data-bind="text: categoryName"></span></span></li>
                <li id="panel" class="report-category-container" data-bind="foreach: reports, visible: $root.showCategory">
                    <div class="column-list-item" data-bind="click: $root.report_click, css: { 'selected': typeof $root.selectedReport() != 'undefined' && $data == $root.selectedReport() }">
                        <span class="column-list-text" data-bind="text: ReportName"></span>
                    </div>
                </li>
            </ul>
        </div>
Questioner
codeflower
Viewed
31
HeyJude 2020-02-10 06:50

currently, when I click on the category name, it shows and hides all the categories.

It's because showCategory is your single observable responsible for showing\hiding. What you really want is one show\hide observable per category.

I'm not sure how your entire data model looks like, but since you specifically asked about categories, then you should create a category view model, and probably some container view model, which I'll name here master:

var categoryVM = function (name) {
    var self = this;
    self.name = ko.observable(name);
    self.isVisible = ko.observable(false);
    self.toggleVisibility = function () {
        self.isVisible(!self.isVisible());
    }
    // ... add here your other observables ...
}

// name 'masterVM' whatever you like
var masterVM = function () {
    var self = this;
    self.categories = ko.observables([]);
    // ... probably add here other observables, e.g. 'reports' ...
    self.init = function (rawCategories) {
        rawCategories.forEach(function (item) {
            categories.push(new categoryVM(item.name)); // replace 'name' with your property
        }
    }
}

var master = new masterVM();
master.init(getCategories()); // pass in your categories from wherever they come from
ko.applyBindings(master);

Then, in your html, this would be your outer foreach:

<div class="report-category-treeview" data-bind="foreach: categories ... />

and your lis (for brevity, I'm ommiting nested tags under your lis):

<li class="report-category-heading" 
    data-bind="click: toggleVisibility">
<li id="panel" class="report-category-container" 
    data-bind="foreach: $root.reports, visible: isVisible">