Knockoutjs ; пользовательский компонент с внутренней разметкой, ссылающийся на вызывающий объект $ data и его $ parent

#knockout.js

#knockout.js

Вопрос:

Я пытаюсь создать пользовательский компонент, имеющий некоторую внутреннюю разметку, которая должна ссылаться на вызывающий объект BindingContext, поскольку внутренняя разметка ссылается на вызывающий объект $ data и $ parent, для $ data нет проблем из-за опции data при привязке шаблона, но как насчет его $ parent?

Я нашел дерьмовое решение, изменив функцию ko.BindingContext.prototype[‘createChildContext’], устанавливающую self [«$ parent»] таким образом :

self['$parent'] = parentContext['$parent'] === self["$data"] ? parentContext['$parents'][1]:parentContext['$data'];

понятия не имею, как обращаться с self[‘$ parents’] :/ что вы об этом думаете? есть ли чистый способ сделать это?

заранее благодарю колю ** пример jsfiddle :https://jsfiddle.net/koljagava/nyh565xp

Комментарии:

1. В вашей модели, которая представлена как $ data, можете ли вы добавить ссылку на ее $ parent? Затем вы бы сделали что-то вроде $data.parent.someObservable …

2. Да, каким-то хитрым способом это возможно. Я ищу чистую реализацию. Если это возможно

3. Я часто использовал этот метод в knockout viewmodels — но я еще не использовал компоненты, поэтому я не знаю, как вы могли бы изменить свой дизайн, чтобы передать ссылку на родительский объект при инициализации модели вашего дочернего компонента.

Ответ №1:

Я думаю, вы хотите $parentContext (обсуждалось здесь).

Узел в дочернем шаблоне становится

 <div data-bind="text:variable1   ' '   $parentContext.$parents[1].someVariable()">
  

 ko.components.register("parent-component", {
  viewModel: function(params) {
    this.params = params;
    this.someVariable = params.variable;
    this.innerContext = {
      variable1: "variable1",
      logIt: (f) => {
        console.log(f);
      }
    }
  },
  template: "<!-- ko template: { nodes: $componentTemplateNodes } --><!-- /ko -->"
});

ko.components.register("child-component", {
  viewModel: function(params) {
    this.params = params;
  },
  template: "<!-- ko template: { nodes: $componentTemplateNodes, data:$parent} --><!-- /ko -->"
});

ko.applyBindings({
  someVariable: ko.observable('someVariableWhoseValueIsHere')
});  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<parent-component params="variable: someVariable">
  <div data-bind="with:innerContext">
    <child-component>
      <div data-bind="text:variable1   ' '   $parentContext.$parents[1].someVariable()">
    </child-component>
    </div>
</parent-component>  

Ответ №2:

Я нашел решение: используя ko CustomBinding

     ko.bindingHandlers.withCorrectBinding = {
        init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
            var idx = bindingContext.$parents.indexOf(valueAccessor());
            //if databinding is not in parents lets ko do normal binding 
            if (idx === -1)
              return;

            var childBindingContext = bindingContext.createChildContext(
                valueAccessor);
            childBindingContext.$parent = null;
            childBindingContext.$parents = [];
            if (idx 1<bindingContext.$parents.length){
              //$parent
              childBindingContext.$parent = bindingContext.$parents[idx 1];
              //$parents
              childBindingContext.$parents = bindingContext.$parents.splice(0,idx);
              //$rawData ???
              //$parentContext ???
            }

            ko.applyBindingsToDescendants(childBindingContext, element);

            // Also tell KO *not* to bind the descendants itself, otherwise they will be bound twice
            return { controlsDescendantBindings: true };
        }
    };

    ko.applyBindings({
    	var1: "var1Value",
      innerContext:{
      			var2 : "var2Value",
            innerInnerContext : {
            	var3 : "var2Value"
            }
      	}
    });  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<div data-bind="with:innerContext">
  <div data-bind="with:innerInnerContext">
    <hr/>
    <div data-bind="with:$parent">
      Does not work<br>
      var2 : <div data-bind="text:var2"></div>
      var1 : <div data-bind="text:$parent.var1||'not found!'"></div>
    </div>
    <hr/>
    <div data-bind="withCorrectBinding:$parent">
      It works!<br>
      var2 : <div data-bind="text:var2"></div>
      var1 : <div data-bind="text:$parent.var1"||'not found!'></div>
    </div>
  </div>
</div>