2015-11-18 2 views
1

Я довольно опытен с нокаутом, но это мой первый раз, когда я использую компоненты, поэтому я действительно надеюсь, что мне не хватает чего-то очевидного! Я попытаюсь немного упростить свой вариант использования, чтобы объяснить мою проблему.Async загружает шаблон в компонент нокаута

У меня есть HTML и JS-файл под названием Index. Index.html имеет привязку данных для компонента, а Index.js имеет вызов ko.components.register.

index.html

<div data-bind="component: { name: CurrentComponent }"></div> 

index.js

var vm = require("SectionViewModel"); 
var CurrentComponent = ko.observable("section"); 
ko.components.register("section", { 
    viewModel: vm.SectionViewModel, 
    template: "<h3>Loading...</h3>" 
}); 
ko.applyBindings(); 

Я тогда еще один HTML и JS файл - Section.html и SectionViewModel.js. Как вы можете видеть выше, SectionViewModel - это то, что я указываю в качестве модели представления для компонента.

Section.html

<div> 
    <span data-bind="text: Section().Name"></span> 
</div> 

SectionViewModel.js

var SectionViewModel = (function() { 
    function SectionViewModel() { 
     this.Section = ko.observable(); 
     $.get("http://apiurl").done(function (data) { 
      this.Section(new SectionModel(data.Model)); // my data used by the view model 
      ko.components.get("dashboard", function() { 
       component.template[0] = data.View; // my html from the api 
      }); 
     }); 
    } 
    return SectionViewModel; 
}); 
exports.SectionViewModel = SectionViewModel; 

В рамках конструктора в SectionViewModel, я делаю вызов моей API, чтобы получить все данные, необходимые для заполнить мою модель представления. Этот вызов API также возвращает HTML, который мне нужно использовать в моем шаблоне (который в основном читается из Section.html).

Очевидно, что этот конструктор не вызывается до тех пор, пока я не назову applyBindings, поэтому, когда я попадаю в обработчик успеха для вызова API, шаблон моего компонента уже настроен на мой текст по умолчанию.

Что мне нужно знать, возможно ли обновить этот шаблон? Я попытался следующим в моем обработчике успеха, как показано выше:

ko.components.get("section", function(component) { 
    component.template[0] = dataFromApi.Html; 
}); 

Это действительно заменить мой текст по умолчанию с HTML вернулся из API (как показано в инструментах отладки), но это обновление не отражаются в браузере.

Итак, в конце концов, все, о чем я действительно спрашиваю, есть способ обновить содержимое вашего шаблона компонентов после привязки?

Я знаю вариант для решения вышеизложенного, о котором вы могли подумать, - это потребовать шаблон, но я действительно упростил приведенное выше и в его полной реализации, я не могу этого сделать, поэтому, почему HTML возвращенный API.

Любая помощь очень ценится! В настоящее время у меня есть рабочее решение, но мне действительно не нравится, как мне пришлось структурировать JS-код, чтобы он работал, поэтому решение вышеизложенного было бы идеальным.

Спасибо.

+0

Просьба, сделайте MVCE http://stackoverflow.com/help/mcve: укажите минимальный код, необходимый для просмотра того, как выглядит ваш компонент, что вы хотите делать и что неисправен. Ваше Q - неопределенное объяснение проблемы, как указано. – JotaBe

+0

@JotaBe Извинения. Я не прибавил много, поскольку я не был уверен, что это было необходимо. Однако я добавил больше. Благодарю. – yellowbrickcode

ответ

1

Вы можете использовать template binding внутри вашего компонента.

Нормальное использование шаблона bindign как это:

<div data-bind="template: { name: tmplName, data: tmplData }"></div> 

Вы можете сделать оба tmplData и tmplName наблюдаемыми, так что вы можете обновить связанные данные и изменить шаблон. tmplName - это идентификатор элемента, содержимое которого будет использоваться в качестве шаблона. Если вы используете этот синтаксис, вам нужен элемент с требуемым id, поэтому в вашем обработчике succes вы можете использовать что-то вроде jQuery для создания нового элемента с соответствующим id, а затем обновить tmplname, чтобы содержимое шаблона обновилось.

* Это не будет работать: Другим вариантом является использование шаблона привязки по-другому:

<div data-bind="template: { nodes: tmplNodes, data: tmplData }"></div> 

В этом случае вы можете поставить непосредственно узлы к шаблону. То есть сделать наблюдаемый tmplNodes, который инициализируется вашим элементом <h3>Loading...</h3>. А затем измените его, чтобы удерживать узлы, полученные с сервера.

потомуnodes не поддерживает наблюдаемые:

узлов - непосредственно передать массив узлов DOM для использования в качестве шаблона. Это должен быть не наблюдаемый массив и обратите внимание, что элементы будут удалены из их текущего родителя, если они есть. Этот параметр игнорируется, если вы также передали непустое значение для имени.

Таким образом, вам необходимо использовать первый вариант: создать новый элемент, добавить его в документ DOM с известным идентификатором и использовать этот идентификатор в качестве имени шаблона. DEMO:

// Simulate service that return HTML 
 
var dynTemplNumber = 0; 
 
var getHtml = function() { 
 
    var deferred = $.Deferred(); 
 
\t var html = 
 
    '<div class="c"> \ 
 
\t <h3>Dynamic template ' + dynTemplNumber++ + '</h3> \ 
 
\t Name: <span data-bind="text: name"/> \ 
 
    </div>'; 
 
    setTimeout(deferred.resolve, 2000, html); 
 
    return deferred.promise(); 
 
}; 
 

 
var Vm = function() { 
 
    self = this; 
 
    self.tmplIdx = 0; 
 
    self.tmplName = ko.observable('tmplA'); 
 
    self.tmplData = ko.observable({ name: 'Helmut', surname: 'Kaufmann'}); 
 
    self.tmplNames = ko.observableArray(['tmplA','tmplB']); 
 
    self.loading = ko.observable(false); 
 
    self.createNewTemplate = function() { 
 
     // simulate AJAX call to service 
 
     self.loading(true); 
 
     getHtml().then(function(html) { 
 
      var tmplName = 'tmpl' + tmplIdx++; 
 
      var $new = $('<div>'); 
 
      $new.attr('id',tmplName); 
 
      $new.html(html); 
 
      $('#tmplContainer').append($new); 
 
      self.tmplNames.push(tmplName); 
 
\t  self.loading(false); 
 
      self.tmplName(tmplName); 
 
     }); 
 
    }; 
 
    return self; 
 
}; 
 

 
ko.applyBindings(Vm(), byName);
div.container { border: solid 1px black; margin: 20px 0;} 
 
div {padding: 5px; } 
 
.a { background-color: #FEE;} 
 
.b { background-color: #EFE;} 
 
.c { background-color: #EEF;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script> 
 
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
 
<div id="byName" class="container"> 
 

 
    Select template by name: 
 
    <select data-bind="{options: tmplNames, value: tmplName}"></select> 
 
    <input type="button" value="Add template" 
 
     data-bind="click: createNewTemplate"/> 
 
    <span data-bind="visible: loading">Loading new template...</span> 
 
    <div data-bind="template: {name: tmplName, data: tmplData}"></div> 
 

 
</div> 
 

 
<div id="tmplContainer" style="display:none"> 
 
    <div id="tmplA"> 
 
    <div class="a"> 
 
     <h3>Template A</h3> 
 
     <span data-bind="text: name"></span> <span data-bind="text: surname"></span> 
 
    </div> 
 
    </div> 
 
    <div id="tmplB"> 
 
    <div class="b"> 
 
     <h3>Template B</h3> 
 
     Name: <span data-bind="text: name"/> 
 
    </div> 
 
    </div> 
 
</div>

+0

Хорошая идея. Я не думал о том, как это сделать. Спасибо, собираюсь попробовать это сейчас. Я дам вам знать, как это происходит. – yellowbrickcode

+0

@SWilliams. Извините, второй вариант не сработает. Я включил демо-версию первого варианта. – JotaBe

+0

Я просто понимал, что второй тоже не работает :) Я дам ваше предложение для первого пути. Еще раз спасибо! – yellowbrickcode

1
component.template[0] = $(data)[0] 

Я знаю, что это старый, но я нашел, что это пытается сделать то же самое, и approcah помог мне придумать это в моем случае шаблон кажется быть элементом, а не только сырым html

Смежные вопросы