The road to DotVVM 2.0: Part 2 - Static Command Services
Published: 1/13/2018 8:23:18 PMAs mentioned in the previous blog post, optimization of data transfers and making the viewmodels smaller was a priority for DotVVM 2.0. This time, we’ll talk about static commands.
Static commands were present in DotVVM since the first version. It was a light-weight version of the classic command binding, which didn’t require to transfer the entire viewmodel to the server and back. Instead, you were able to call a static method and optionally use its return value to update some property in the viewmodel. For security reasons, any method called from static command must be decorated with AllowStaticCommand
attribute.
For example, if you want to create a tree menu which can load its children, it may look like this:
<!-- DotVVM 1.x -->
<dot:Repeater DataSource="{value: Menu}" WrapperTagName="ul">
<li>
<dot:LinkButton Click="{staticCommand: Children = DefaultViewModel.LoadMenu(_this)}" Text="{value: Title}" />
</li>
<dot:Repeater DataSource="{value: Children}" WrapperTagName="ul">
<li>{{value: Title}}</li>
</dot:Repeater>
</dot:Repeater>
In this case, LoadMenu
is a static method. This was a limitation of DotVVM 1.x and it makes using static commands quite inconvenient. For example, you cannot use dependency injection in static methods, and the code is not testable.
// DotVVM 1.x
[AllowStaticCommand]
public static List<MenuItemData> LoadMenu(MenuItemData item)
{
var facade = HttpContext.Current.GetOwinContext().GetDotvvmContext().Services.GetService<MenuFacade>();
return facade.LoadMenu(item);
}
Static Command Services
DotVVM 2.0 brings a concept of static command services. You can “inject” a service in the view and use it in the static command binding expressions. The instance of the service will be resolved from the IServiceProvider
, so you need to register it in the IServiceCollection
when the application starts, same as you register any other services.
<!-- DotVVM 2.0 -->
@service menuFacade = ClassLibrary1.MenuFacade
<dot:Repeater DataSource="{value: Menu}" WrapperTagName="ul">
<li>
<dot:LinkButton Click="{staticCommand: Children = menuFacade.LoadMenu(_this)}" Text="{value: Title}" />
</li>
<dot:Repeater DataSource="{value: Children}" WrapperTagName="ul">
<li>{{value: Title}}</li>
</dot:Repeater>
</dot:Repeater>
The MenuFacade
is then a usual class with an instance method. It can sit in your business layer for example. And it is created by the dependency injection container - if it needs any dependencies, it can just declare them as constructor parameters.
// DotVVM 2.0
public class MenuFacade
{
public MenuFacade(MyDbContext db)
{
// we are using dependency injection here
...
}
[AllowStaticCommand]
public List<MenuItemData> LoadMenu(MenuItemData item)
{
...
}
}
In both cases, the viewmodel is not transferred from the server to the client and back. Only the current item is sent to the server and the items that are assigned in the Children collection are returned. Nice, isn’t it?
I am the CEO of RIGANTI, a small software development company located in Prague, Czech Republic.
I am Microsoft Most Valuable Professional and the founder of DotVVM project.