Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TemplateHost control #1109

Merged
merged 6 commits into from
Sep 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions src/Framework/Framework/Controls/TemplateHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DotVVM.Framework.Binding;
using DotVVM.Framework.Compilation.ControlTree;
using DotVVM.Framework.Hosting;
using DotVVM.Framework.Utils;

namespace DotVVM.Framework.Controls
{
Expand All @@ -18,13 +21,38 @@ public class TemplateHost : DotvvmControl
/// Gets or sets the template that will be rendered inside this control.
/// </summary>
[MarkupOptions(AllowBinding = false, MappingMode = MappingMode.Attribute, Required = true)]
public ITemplate? ContentTemplate { get; set; }
public ITemplate? Template
{
get { return (ITemplate?)GetValue(TemplateProperty); }
set { SetValue(TemplateProperty, value); }
}
public static readonly DotvvmProperty TemplateProperty
= DotvvmProperty.Register<ITemplate, TemplateHost>(c => c.Template, null);



protected internal override void OnLoad(IDotvvmRequestContext context)
{
ContentTemplate?.BuildContent(context, this);
var placeHolder = new PlaceHolder();
Template.NotNull("TemplateHost.Template is required").BuildContent(context, placeHolder);

// validate data context of the passed template
var myDataContext = this.GetDataContextType()!;
if (!CheckChildrenDataContextStackEquality(myDataContext, placeHolder.Children))
{
throw new DotvvmControlException(this, "Passing templates into markup controls or to controls which change the binding context, is not supported!");
}

Children.Add(placeHolder);

base.OnLoad(context);
}

private bool CheckChildrenDataContextStackEquality(DataContextStack desiredDataContext, DotvvmControlCollection children)
{
return children.Select(c => c.GetDataContextType())
.Where(t => t != null)
.All(t => Equals(t, desiredDataContext));
}
}
}
3 changes: 3 additions & 0 deletions src/Samples/Common/DotVVM.Samples.Common.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
<None Remove="Views\ControlSamples\Repeater\IndexInNestedRepeater.dothtml" />
<None Remove="Views\ControlSamples\Repeater\NamedTemplate.dothtml" />
<None Remove="Views\ControlSamples\RouteLink\RouteLinkQueryParameters.dothtml" />
<None Remove="Views\ControlSamples\TemplateHost\Basic.dothtml" />
<None Remove="Views\ControlSamples\TemplateHost\TemplatedListControl.dotcontrol" />
<None Remove="Views\ControlSamples\TemplateHost\TemplatedMarkupControl.dotcontrol" />
<None Remove="Views\ControlSamples\UpdateProgress\UpdateProgressQueues.dothtml" />
<None Remove="Views\ControlSamples\UpdateProgress\UpdateProgressRedirect.dotmaster" />
<None Remove="Views\ControlSamples\UpdateProgress\UpdateProgressRedirect1.dothtml" />
Expand Down
5 changes: 4 additions & 1 deletion src/Samples/Common/DotvvmStartup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
using DotVVM.Samples.Common.ViewModels.FeatureSamples.JavascriptTranslation;
using DotVVM.Samples.Common.Views.FeatureSamples.PostbackAbortSignal;
using DotVVM.Samples.Common.ViewModels.FeatureSamples.BindingVariables;
using DotVVM.Samples.Common.Views.ControlSamples.TemplateHost;

namespace DotVVM.Samples.BasicSamples
{
Expand Down Expand Up @@ -195,8 +196,10 @@ private static void AddControls(DotvvmConfiguration config)
config.Markup.AddMarkupControl("cc", "RecursiveTextRepeater2", "Views/FeatureSamples/PostBack/RecursiveTextRepeater2.dotcontrol");
config.Markup.AddMarkupControl("cc", "ModuleControl", "Views/FeatureSamples/ViewModules/ModuleControl.dotcontrol");
config.Markup.AddMarkupControl("cc", "Incrementer", "Views/FeatureSamples/ViewModules/Incrementer.dotcontrol");
config.Markup.AddMarkupControl("cc", "TemplatedListControl", "Views/ControlSamples/TemplateHost/TemplatedListControl.dotcontrol");
config.Markup.AddMarkupControl("cc", "TemplatedMarkupControl", "Views/ControlSamples/TemplateHost/TemplatedMarkupControl.dotcontrol");
config.Markup.AddCodeControls("cc", typeof(CompositeControlWithTemplate));
config.Markup.AddCodeControls("cc", typeof(Loader));

config.Markup.AddMarkupControl("sample", "EmbeddedResourceControls_Button", "embedded://EmbeddedResourceControls/Button.dotcontrol");

config.Markup.AutoDiscoverControls(new DefaultControlRegistrationStrategy(config, "sample", "Views/"));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DotVVM.Framework.ViewModel;

namespace DotVVM.Samples.Common.ViewModels.ControlSamples.TemplateHost
{
public class BasicViewModel : DotvvmViewModelBase
{

public List<IntValue> ObjectList { get; set; } = new List<IntValue>()
{
new IntValue() { Value = 1 },
new IntValue() { Value = 2 },
new IntValue() { Value = 3 }
};

public IntValue CreateObject()
{
return new IntValue() { Value = 1 };
}
}

public class IntValue
{
public int Value { get; set; }
}
}

44 changes: 44 additions & 0 deletions src/Samples/Common/Views/ControlSamples/TemplateHost/Basic.dothtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
@viewModel DotVVM.Samples.Common.ViewModels.ControlSamples.TemplateHost.BasicViewModel, DotVVM.Samples.Common

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>

<h1>TemplateHost</h1>

<cc:CompositeControlWithTemplate HeaderText="Form 1">
<ContentTemplate>
<p>hello from template</p>
</ContentTemplate>
</cc:CompositeControlWithTemplate>

<cc:CompositeListControlWithTemplate DataSource="{value: ObjectList}" OnCreateItem="{command: _root.CreateObject}">
<ItemTemplate>
<big>{{value: Value}}</big>
<dot:Button Text="+" Click="{staticCommand: Value = Value + 1}" />
<dot:Button Text="-" Click="{staticCommand: Value = Value - 1}" />
</ItemTemplate>
</cc:CompositeListControlWithTemplate>

<%--<cc:TemplatedMarkupControl HeaderText="Form 1">
<ContentTemplate>
<p>hello from template</p>
</ContentTemplate>
</cc:TemplatedMarkupControl>

<cc:TemplatedListControl DataSource="{value: ObjectList}" OnCreateItem="{command: _root.CreateObject}">
<ItemTemplate>
<big>{{value: Value}}</big>
<dot:Button Text="+" Click="{staticCommand: Value = Value + 1}" />
<dot:Button Text="-" Click="{staticCommand: Value = Value - 1}" />
</ItemTemplate>
</cc:TemplatedListControl>--%>
</body>
</html>


Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;
using System.Text;
using DotVVM.Framework.Binding;
using DotVVM.Framework.Controls;

namespace DotVVM.Samples.Common.Views.ControlSamples.TemplateHost
{
public class CompositeControlWithTemplate : CompositeControl
{

public static DotvvmControl GetContents(
ValueOrBinding<string> headerText,
ITemplate contentTemplate
)
{
return new HtmlGenericControl("fieldset")
.AppendChildren(
new HtmlGenericControl("legend", new TextOrContentCapability() { Text = headerText }),
new Framework.Controls.TemplateHost() { Template = contentTemplate }
);
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DotVVM.Framework.Binding;
using DotVVM.Framework.Binding.Expressions;
using DotVVM.Framework.Controls;
using DotVVM.Framework.Utils;

namespace DotVVM.Samples.Common.Views.ControlSamples.TemplateHost
{
public class CompositeListControlWithTemplate : CompositeControl
{
private readonly BindingCompilationService bindingCompilationService;

public CompositeListControlWithTemplate(BindingCompilationService bindingCompilationService)
{
this.bindingCompilationService = bindingCompilationService;
}

public IEnumerable<DotvvmControl> GetContents(
IValueBinding<IEnumerable> dataSource,

[ControlPropertyBindingDataContextChange("DataSource", order: 0)]
[CollectionElementDataContextChange(order: 1)]
ITemplate itemTemplate,

ICommandBinding<object> onCreateItem
)
{
yield return new Framework.Controls.Repeater() {
ItemTemplate = new DelegateTemplate(_ => new HtmlGenericControl("div")
.AppendChildren(
new Framework.Controls.TemplateHost() { Template = itemTemplate },
new HtmlGenericControl("p")
.AppendChildren(
new LinkButton() { Text = "Remove" }
.SetProperty(
ButtonBase.ClickProperty,
new CommandBindingExpression(bindingCompilationService, contexts => {
((dynamic)dataSource.GetBindingValue(this)).Remove((dynamic)contexts[0]);
}, "564787DE-E882-4C2D-BA39-482D1AB8F0CD"))
)
)
),
SeparatorTemplate = new DelegateTemplate(_ => new HtmlGenericControl("hr"))
}
.SetAttribute("class", "templated-list")
.SetProperty(ItemsControl.DataSourceProperty, dataSource);

yield return new HtmlGenericControl("p")
.AppendChildren(new Button() { Text = "Add item" }
.SetProperty(
ButtonBase.ClickProperty,
new CommandBindingExpression(bindingCompilationService, contexts => {
var item = onCreateItem.BindingDelegate(this.GetDataContexts().ToArray(), this);
((dynamic)dataSource.GetBindingValue(this)).Add(((dynamic)item)());
}, "38921DE7-936D-4862-921A-5051DA0CAEB1")));
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DotVVM.Framework.Binding;
using DotVVM.Framework.Binding.Expressions;
using DotVVM.Framework.Controls;

namespace DotVVM.Samples.Common.Views.ControlSamples.TemplateHost
{
public class TemplatedListControl : DotvvmMarkupControl
{

[MarkupOptions(AllowHardCodedValue = false, Required = true)]
public IEnumerable DataSource
{
get { return (IEnumerable)GetValue(DataSourceProperty); }
set { SetValue(DataSourceProperty, value); }
}
public static readonly DotvvmProperty DataSourceProperty
= DotvvmProperty.Register<IEnumerable, TemplatedListControl>(c => c.DataSource, null);

[ControlPropertyBindingDataContextChange(nameof(DataSource), order: 0)]
[CollectionElementDataContextChange(order: 1)]
[MarkupOptions(AllowBinding = false, Required = true, MappingMode = MappingMode.InnerElement)]
public ITemplate ItemTemplate
{
get { return (ITemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
public static readonly DotvvmProperty ItemTemplateProperty
= DotvvmProperty.Register<ITemplate, TemplatedListControl>(c => c.ItemTemplate, null);

[MarkupOptions(AllowHardCodedValue = false, Required = true)]
public ICommandBinding<object> OnCreateItem
{
get { return (ICommandBinding<object>)GetValue(OnCreateItemProperty); }
set { SetValue(OnCreateItemProperty, value); }
}
public static readonly DotvvmProperty OnCreateItemProperty
= DotvvmProperty.Register<ICommandBinding<object>, TemplatedListControl>(c => c.OnCreateItem, null);


public void AddItem()
{
var item = OnCreateItem.BindingDelegate(this.GetDataContexts().ToArray(), this);
((dynamic)DataSource).Add(((dynamic)item)());
}

public void RemoveItem(object item)
{
((dynamic)DataSource).Remove((dynamic)item);
}

}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@viewModel System.Object, mscorlib
@baseType DotVVM.Samples.Common.Views.ControlSamples.TemplateHost.TemplatedListControl, DotVVM.Samples.Common

<dot:Repeater DataSource="{value: _control.DataSource}" class="templated-list">
<ItemTemplate>
<div>
<dot:TemplateHost Template="{resource: _control.ItemTemplate}" />

<p>
<dot:LinkButton Text="Remove" Click="{command: _control.RemoveItem(_this)}" />
</p>
</div>
</ItemTemplate>
<SeparatorTemplate>
<hr />
</SeparatorTemplate>
</dot:Repeater>

<p>
<dot:Button Text="Add item" Click="{command: _control.AddItem()}" />
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DotVVM.Framework.Binding;
using DotVVM.Framework.Controls;

namespace DotVVM.Samples.Common.Views.ControlSamples.TemplateHost
{
public class TemplatedMarkupControl : DotvvmMarkupControl
{

public string HeaderText
{
get { return (string)GetValue(HeaderTextProperty); }
set { SetValue(HeaderTextProperty, value); }
}
public static readonly DotvvmProperty HeaderTextProperty
= DotvvmProperty.Register<string, TemplatedMarkupControl>(c => c.HeaderText, null);

[MarkupOptions(AllowBinding = false, MappingMode = MappingMode.InnerElement, Required = true)]
public ITemplate ContentTemplate
{
get { return (ITemplate)GetValue(ContentTemplateProperty); }
set { SetValue(ContentTemplateProperty, value); }
}
public static readonly DotvvmProperty ContentTemplateProperty
= DotvvmProperty.Register<ITemplate, TemplatedMarkupControl>(c => c.ContentTemplate, null);


}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@viewModel System.Object, mscorlib
@baseType DotVVM.Samples.Common.Views.ControlSamples.TemplateHost.TemplatedMarkupControl, DotVVM.Samples.Common

<fieldset>
<legend>{{value: _control.HeaderText}}</legend>
<dot:TemplateHost Template="{resource: _control.ContentTemplate}" />
</fieldset>

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading