Can't bind observable inside component to an I

2019-09-11 23:30发布

问题:

I am trying to load a component conditionally using an if-binding. However, I can't change the value of the observable that is assigned to the if-binding using the construct below since, I think, the functions that should change the observable are outside the view model. I initially did this because this is what's done in the sample in KnockoutJS's site:

define(["jquery", "knockout", "ko-postbox", 
    "text!./parent.html"], function($, ko, kopost, template) {

    ko.postbox.subscribe("child-click", function(newValue) {
        ParentViewModel.prototype.displayChildContent(newValue);
    }, this);

    function ParentViewModel() {
        //
        // I NEED TO CHANGE THIS FROM OUTSIDE THE VIEW MODEL
        //
        this.childClicked = ko.observable(false); 
    }

    ParentViewModel.prototype.displayParentContent = function(value) { 
        switch (value.toLowerCase()) {
            default:
                break;
        }
    };

    ParentViewModel.prototype.displayChildContent = function(value) { 
        switch (value.toLowerCase()) {
            case "child1":
                alert();
                //
                // I NEED TO CHANGE OBSERVABLE HERE
                //
                break;
            default:
                break;
        }
    };

    return { viewModel: ParentViewModel, template: template };
});

Then I tried doing it this way: I put the functions inside the view model and then assign the view model in a variable. Notice the return value: I used "instance" instead of the usual "viewModel" because when I use "viewModel", an error appears "Uncaught Error: Component 'parent': Unknown viewModel value: [object Object]" and also, it is constructed twice.

define(["jquery", "knockout", "ko-postbox", 
    "text!./parent.html"], function($, ko, kopost, template) {

    ko.postbox.subscribe("child click", function(newValue) {
        model.displayChildContent(newValue);
    }, this);

    function ParentViewModel() {
        var self = this;

        self.childClicked = ko.observable(false);

        self.displayChildContent = function(value) { 
            switch (value.toLowerCase()) {
                case "child1":
                    alert();
                    self.childClicked(true);
                    break;
                default:
                    break;
            }
        };
    }

    var model = new ParentViewModel();

    return { instance: model, template: template };
});

When I do this, the if-binding does not work. The error is "Uncaught ReferenceError: Unable to process binding "if: function (){return childClicked }" Message: childClicked is not defined"

Here is the parent HTML:

<div id="container">

    <!-- ko if: childClicked -->
    <!-- ko component: {name: "parent"} --><!-- /ko -->
    <!-- /ko -->

</div>

回答1:

If I understand you correctly, you want to be able to control the rendering of a component from inside it as well as outside it. The trick is to use params to pass the controlling observable to the component.

In this example, the checkbox in the parent controls the rendering of the component, but the component also has a Hide Me button that triggers the un-rendering via the same controlling observable.

As the snippet runner seems to be down right now, here's the same thing as a fiddle: http://jsfiddle.net/tje9e2yy/

function ChildViewModel(params) {
  this.hideMe = function() {
    params.show(false);
  };
}

ko.components.register('child-component', {
  viewModel: ChildViewModel,
  template: {
    element: 'child-template'
  }
});

var vm = {
  childClicked: ko.observable(false)
};

ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<template id="child-template">
  <button data-bind="click:hideMe">Hide Me</button>
</template>
<div id="container">
  <input type="checkbox" data-bind="checked:childClicked" />
  <!-- ko if: childClicked -->
  <!-- ko component: {name: "child-component", params:{show:childClicked}} -->
  <!-- /ko -->
  <!-- /ko -->
</div>