Как ввести / заменить часть вида и посмотреть модель в Аурелии
Я перехожу с KnockoutJS на Aurelia и с трудом пытаюсь понять, как я могу обменять некоторые HTML/JS из представления. У меня есть некоторый существующий код Knockout следующим образом:
$.ajax({
url: "/admin/configuration/settings/get-editor-ui/" + replaceAll(self.type(), ".", "-"),
type: "GET",
dataType: "json",
async: false
})
.done(function (json) {
// Clean up from previously injected html/scripts
if (typeof cleanUp == 'function') {
cleanUp(self);
}
// Remove Old Scripts
var oldScripts = $('script[data-settings-script="true"]');
if (oldScripts.length > 0) {
$.each(oldScripts, function () {
$(this).remove();
});
}
var elementToBind = $("#form-section")[0];
ko.cleanNode(elementToBind);
var result = $(json.content);
// Add new HTML
var content = $(result.filter('#settings-content')[0]);
var details = $('<div>').append(content.clone()).html();
$("#settings-details").html(details);
// Add new Scripts
var scripts = result.filter('script');
$.each(scripts, function () {
var script = $(this);
script.attr("data-settings-script", "true");//for some reason, .data("block-script", "true") doesn't work here
script.appendTo('body');
});
// Update Bindings
// Ensure the function exists before calling it...
if (typeof updateModel == 'function') {
var data = ko.toJS(ko.mapping.fromJSON(self.value()));
updateModel(self, data);
ko.applyBindings(self, elementToBind);
}
//self.validator.resetForm();
switchSection($("#form-section"));
})
.fail(function (jqXHR, textStatus, errorThrown) {
$.notify(self.translations.getRecordError, "error");
console.log(textStatus + ': ' + errorThrown);
});
В приведенном выше коде self.type()
Передача в URL для AJAX-запроса - это имя некоторых настроек. Вот пример некоторых настроек:
public class DateTimeSettings : ISettings
{
public string DefaultTimeZoneId { get; set; }
public bool AllowUsersToSetTimeZone { get; set; }
#region ISettings Members
public string Name => "Date/Time Settings";
public string EditorTemplatePath => "Framework.Web.Views.Shared.EditorTemplates.DateTimeSettings.cshtml";
#endregion ISettings Members
}
Я использую это EditorTemplatePath
свойство для отображения этого представления и возврата его в запросе AJAX. Пример настроек выглядит следующим образом:
@using Framework.Web
@using Framework.Web.Configuration
@inject Microsoft.Extensions.Localization.IStringLocalizer T
@model DateTimeSettings
<div id="settings-content">
<div class="form-group">
@Html.LabelFor(m => m.DefaultTimeZoneId)
@Html.TextBoxFor(m => m.DefaultTimeZoneId, new { @class = "form-control", data_bind = "value: defaultTimeZoneId" })
@Html.ValidationMessageFor(m => m.DefaultTimeZoneId)
</div>
<div class="checkbox">
<label>
@Html.CheckBoxFor(m => m.AllowUsersToSetTimeZone, new { data_bind = "checked: allowUsersToSetTimeZone" }) @T[FrameworkWebLocalizableStrings.Settings.DateTime.AllowUsersToSetTimeZone]
</label>
</div>
</div>
<script type="text/javascript">
function updateModel(viewModel, data) {
viewModel.defaultTimeZoneId = ko.observable("");
viewModel.allowUsersToSetTimeZone = ko.observable(false);
if (data) {
if (data.DefaultTimeZoneId) {
viewModel.defaultTimeZoneId(data.DefaultTimeZoneId);
}
if (data.AllowUsersToSetTimeZone) {
viewModel.allowUsersToSetTimeZone(data.AllowUsersToSetTimeZone);
}
}
};
function cleanUp(viewModel) {
delete viewModel.defaultTimeZoneId;
delete viewModel.allowUsersToSetTimeZone;
}
function onBeforeSave(viewModel) {
var data = {
DefaultTimeZoneId: viewModel.defaultTimeZoneId(),
AllowUsersToSetTimeZone: viewModel.allowUsersToSetTimeZone()
};
viewModel.value(ko.mapping.toJSON(data));
};
</script>
Теперь, если вы вернетесь к запросу AJAX и увидите, что я там делаю, это должно иметь больше смысла. E сть <div>
где я внедряю этот HTML следующим образом:
<div id="settings-details"></div>
Я пытаюсь понять, как это сделать в Аурелии. Я вижу, что могу использовать Аурелию templatingEngine.enhance({ element: elementToBind, bindingContext: this });
вместо нокаута ko.applyBindings(self, elementToBind);
поэтому я думаю, что это должно связать новые свойства с моделью представления. Однако я не знаю, что делать со скриптами из шаблонов редактора настроек. Я полагаю, что могу попытаться сохранить ту же логику, что у меня уже есть (используя jQuery для добавления / удаления скриптов и т. Д.)... но я надеюсь, что с Aurelia есть более чистое / более элегантное решение для этого. я смотрел на slots
, но я не думаю, что это применимо здесь, хотя я могу ошибаться.
1 ответ
Как уже говорилось в комментариях, мой ответ на ваш другой вопрос должен помочь. Учитывая это runtime-view
элемент:
Машинопись
import { bindingMode, createOverrideContext } from "aurelia-binding";
import { Container } from "aurelia-dependency-injection";
import { TaskQueue } from "aurelia-task-queue";
import { bindable, customElement, inlineView, ViewCompiler, ViewResources, ViewSlot } from "aurelia-templating";
@customElement("runtime-view")
@inlineView("<template><div></div></template>")
export class RuntimeView {
@bindable({ defaultBindingMode: bindingMode.toView })
public html: string;
@bindable({ defaultBindingMode: bindingMode.toView })
public context: any;
public el: HTMLElement;
public slot: ViewSlot;
public bindingContext: any;
public overrideContext: any;
public isAttached: boolean;
public isRendered: boolean;
public needsRender: boolean;
private tq: TaskQueue;
private container: Container;
private viewCompiler: ViewCompiler;
constructor(el: Element, tq: TaskQueue, container: Container, viewCompiler: ViewCompiler) {
this.el = el as HTMLElement;
this.tq = tq;
this.container = container;
this.viewCompiler = viewCompiler;
this.slot = this.bindingContext = this.overrideContext = null;
this.isAttached = this.isRendered = this.needsRender = false;
}
public bind(bindingContext: any, overrideContext: any): void {
this.bindingContext = this.context || bindingContext.context || bindingContext;
this.overrideContext = createOverrideContext(this.bindingContext, overrideContext);
this.htmlChanged();
}
public unbind(): void {
this.bindingContext = null;
this.overrideContext = null;
}
public attached(): void {
this.slot = new ViewSlot(this.el.firstElementChild || this.el, true);
this.isAttached = true;
this.tq.queueMicroTask(() => {
this.tryRender();
});
}
public detached(): void {
this.isAttached = false;
if (this.isRendered) {
this.cleanUp();
}
this.slot = null;
}
private htmlChanged(): void {
this.tq.queueMicroTask(() => {
this.tryRender();
});
}
private contextChanged(): void {
this.tq.queueMicroTask(() => {
this.tryRender();
});
}
private tryRender(): void {
if (this.isAttached) {
if (this.isRendered) {
this.cleanUp();
}
try {
this.tq.queueMicroTask(() => {
this.render();
});
} catch (e) {
this.tq.queueMicroTask(() => {
this.render(`<template>${e.message}</template>`);
});
}
}
}
private cleanUp(): void {
try {
this.slot.detached();
} catch (e) {}
try {
this.slot.unbind();
} catch (e) {}
try {
this.slot.removeAll();
} catch (e) {}
this.isRendered = false;
}
private render(message?: string): void {
if (this.isRendered) {
this.cleanUp();
}
const template = `<template>${message || this.html}</template>`;
const viewResources = this.container.get(ViewResources) as ViewResources;
const childContainer = this.container.createChild();
const factory = this.viewCompiler.compile(template, viewResources);
const view = factory.create(childContainer);
this.slot.add(view);
this.slot.bind(this.bindingContext, this.overrideContext);
this.slot.attached();
this.isRendered = true;
}
}
ES6
import { bindingMode, createOverrideContext } from "aurelia-binding";
import { Container } from "aurelia-dependency-injection";
import { TaskQueue } from "aurelia-task-queue";
import { DOM } from "aurelia-pal";
import { bindable, customElement, inlineView, ViewCompiler, ViewResources, ViewSlot } from "aurelia-templating";
@customElement("runtime-view")
@inlineView("<template><div></div></template>")
@inject(DOM.Element, TaskQueue, Container, ViewCompiler)
export class RuntimeView {
@bindable({ defaultBindingMode: bindingMode.toView }) html;
@bindable({ defaultBindingMode: bindingMode.toView }) context;
constructor(el, tq, container, viewCompiler) {
this.el = el;
this.tq = tq;
this.container = container;
this.viewCompiler = viewCompiler;
this.slot = this.bindingContext = this.overrideContext = null;
this.isAttached = this.isRendered = this.needsRender = false;
}
bind(bindingContext, overrideContext) {
this.bindingContext = this.context || bindingContext.context || bindingContext;
this.overrideContext = createOverrideContext(this.bindingContext, overrideContext);
this.htmlChanged();
}
unbind() {
this.bindingContext = null;
this.overrideContext = null;
}
attached() {
this.slot = new ViewSlot(this.el.firstElementChild || this.el, true);
this.isAttached = true;
this.tq.queueMicroTask(() => {
this.tryRender();
});
}
detached() {
this.isAttached = false;
if (this.isRendered) {
this.cleanUp();
}
this.slot = null;
}
htmlChanged() {
this.tq.queueMicroTask(() => {
this.tryRender();
});
}
contextChanged() {
this.tq.queueMicroTask(() => {
this.tryRender();
});
}
tryRender() {
if (this.isAttached) {
if (this.isRendered) {
this.cleanUp();
}
try {
this.tq.queueMicroTask(() => {
this.render();
});
} catch (e) {
this.tq.queueMicroTask(() => {
this.render(`<template>${e.message}</template>`);
});
}
}
}
cleanUp() {
try {
this.slot.detached();
} catch (e) {}
try {
this.slot.unbind();
} catch (e) {}
try {
this.slot.removeAll();
} catch (e) {}
this.isRendered = false;
}
render(message) {
if (this.isRendered) {
this.cleanUp();
}
const template = `<template>${message || this.html}</template>`;
const viewResources = this.container.get(ViewResources);
const childContainer = this.container.createChild();
const factory = this.viewCompiler.compile(template, viewResources);
const view = factory.create(childContainer);
this.slot.add(view);
this.slot.bind(this.bindingContext, this.overrideContext);
this.slot.attached();
this.isRendered = true;
}
}
Вот несколько способов, которыми вы можете использовать его:
dynamicHtml
это свойство в ViewModel, содержащее произвольно сгенерированный html с любым видом привязок, пользовательскими элементами и другим поведением aurelia в нем.
Он скомпилирует этот html-файл и свяжется с привязкой к нему, который он получает в bind()
- который будет viewModel представления, в котором вы его объявляете.
<runtime-view html.bind="dynamicHtml">
</runtime-view>
Учитывая someObject
в представлении модели:
this.someObject.foo = "bar";
И dynamicHtml
вот так:
this.dynamicHtml = "<div>${foo}</div>";
Это будет отображаться так, как вы ожидаете, в обычном представлении Aurelia:
<runtime-view html.bind="dynamicHtml" context.bind="someObject">
</runtime-view>
Переназначение либо html
или же context
вызовет его для повторной компиляции. Просто чтобы дать вам представление о возможных вариантах использования, я использую это в проекте с редактором monaco для динамического создания компонентов Aurelia из самого приложения Aurelia, и этот элемент даст предварительный просмотр в реальном времени (рядом с редактором) и также компилировать + визуализировать сохраненный html/js/json, когда я использую его в другом месте приложения.