Skip to content

Releases: riganti/dotvvm

DotVVM 4.2

11 Nov 13:37
767804b
Compare
Choose a tag to compare

Potential breaking changes

DotVVM 4.2 should generally be source compatible with the previous 4.1 version. We did not preserve binary compatibility, so please make sure all other DotVVM packages are at 4.2 version (especially if you see a MissingMethodException or similar error). Please let us know if we forgot to update some of our packages. We also had to change the behavior in ways which could in theory break something

  • The type hashes (the client-side type metadata id) might be different. You might need to adjust if you have it hardcoded somewhere. (see #1614, should only affect generic types)
  • HtmlGenericControl and RadioButton now attempts to render both the server-side value and client-side binding regardless of RenderSettings.Mode. If you relied on the value not being pre-rendered on the server, please explicitly use _page.EvaluatingOnServer ? null : OriginalExpression. To render value server-side only, switch to a resource binding.
  • Using a non-DotvvmProperty defined on a markup control is now a compile-time error (in value bindings). It used to only fail client-side
  • DotVVM does not reload dothtml files when run in Production environment (when DotvvmConfiguration.Debug=false).
  • JsComponent is always initialized asynchronously (see #1684)
  • View Modules are always initialized after dotvvm is initialized
  • Operator precedence of && and || now behaves the same as in C#
  • Unopened closing tag used to close all currently open elements -- for example in <a></b> the b tag closed the a tag. This glitch caused all sorts of problems, so we changed it to not close any elements.
  • RadioButton.Checked is deprecated, please use CheckedValue and CheckedItem properties (the property never worked, but now it might fail compile-time if used incorrectly)

staticCommand validation

Server-side validation now works in staticCommand methods. The validation has to be enabled using [AllowStaticCommand(StaticCommandValidation.Manual)] or [AllowStaticCommand(StaticCommandValidation.Automatic)] attribute. In the Automatic case, all arguments will be recursively validated using DataAnnotations attributes and IValidatableObject.Validate will be called. In both cases, additional validation errors can be returned using the StaticCommandModelState class:

[AllowStaticCommand(StaticCommandValidation.Manual)]
public string MyMethod(MyModel model)
{
    var modelState = new StaticCommandModelState();
    modelState.AddArgumentError(() => model.Property, "Property is invalid");
    modelState.FailOnInvalidModelState();
}

Enabling the validation will make the staticCommand invocation clear all validation errors currently being shown client-side. Note that client-side validation isn't currently implemented, so all invocation will round-trip to the server.

See related forum post for more examples.

Custom primitive types

In order to better support typed IDs, DotVVM now has support for custom primitive types. These types are always represented as a string client-side, which makes them allowed in places only for primitive types - notably SelectedValue property on selector components, but also in route parameters, and html attributes.

The custom primitive type must have ToString() and static TryParse(string, out T) methods. It must also implement the IDotvvmPrimitiveType interface. It's only a marker interface, since ToString method present on all types, and TryParse method is static and we can't use interface static methods due to support for the old framework. The type can be a class, a struct or a record.

A simple typed ID type could look like the following:

public struct OrderId: IDotvvmPrimitiveType
{
    public int Value { get; }
    public OrderId(int value)
    {
        this.Value = value;
    }
    public override string ToString() => Value.ToString();
    public static bool TryParse(string value, out OrderId result)
    {
         if (int.TryParse(value, out var resultValue))
         {
             result = new OrderId(resultValue);
             return true;
         }
         result = default;
         return false;
    }
}

WebForms adapters

We have made a new package which helps in the process of migrating Web Forms apps to the new .NET using DotVVM - DotVVM.Adapters.WebForms. It currently contains HybridRouteLink control and RedirectToRouteHybrid extension method.

  • The <webforms:HybridRouteLink> control has the same functionality as the RouteLink control, but it falls back to Web Forms routes when a requested DotVVM route doesn't exist (which means that the page hasn't been migrated yet).
  • The RedirectToRouteHybrid extension method of the IDotvvmRequestContext also falls back to the Web Forms route in case the DotVVM route doesn't exist.

The package also works on .NET Core, where these fallbacks are disabled (because of the absence of System.Web). This allows to keep using the API even after the migration is finished.

state, patchState, setState, updateState in JS View Modules

We have added the state property and the following methods to the global dotvvm object, viewmodel knockout observables and JS module context (DotvvmModuleContext)

  • state - returns the current value of the view model
  • setState(newValue) - replaces the current value
  • patchState(newValues) - replaces only the specified properties, for example patchState({ UserName: "new-user" })
  • updateState(currentValue => computeNewValue(currentValue)) - applies the specified function onto the current view model

See simplified view model API in JS Modules on forum.dotvvm.com for more details and some context.

Knockout deferred updates (experimental feature)

DotVVM uses knockout.js to immediately synchronize the view model into the HTML DOM. Knockout essentially does this by subscribing to an update event on each view model variable which is used in the binding. When any value inside the view model is changed, all bindings using the value immediately get a notification and update the bound DOM element.

This normally works well, but if the binding uses very many different variables it can get equally many notification if large part of the view model gets changed. For example, you can have a binding which enumerates an array by counting number of certain elements in an array ({value: MyArray.Count(a => a.IsSpecial)}). Each array element is a separate viewmodel value, so the binding with get up to MyArray.Length update notifications, each time evaluating the binding and updating the DOM element. This mean we have got O(n^2) worst case complexity for updating the view model, which can be really slow (in some cases).

Knockout has got an ko.options.deferUpdates = true option, which makes the updates asynchronous and deduplicates the notifications. Updating all array elements will then lead to just one DOM update (with a slight delay). It was always possible to enable this option, in 4.2 we made it easier to enable and fixed a few bugs in DotVVM which occurred when delayed updates were enabled. The feature is still experimental, since we didn't thoroughly test it with all our components. You can enable/disable it for certain pages, if you encounter issues.

config.ExperimentalFeatures.KnockoutDeferUpdates.Enable().ExcludeRoute("SomePageThatOtherwiseBreaks");

Metrics

We instrumented DotVVM with System.Diagnostics.Metrics. All implemented metrics are listed in the DotvvmMetrics.cs file. We'd like to point out some of the important or interesting metrics:

  • dotvvm_viewmodel_size_bytes histogram - Size of the returned viewmodel JSON in bytes.
    • Labels route=RouteName and request_type=Navigate/SpaNavigate/Command/StaticCommand
  • dotvvm_request_duration_seconds - Time it took to stringify the resulting JSON view model.
    • Labels route, request_type and dothtml_file
    • You can use ASP.NET Core statistics about request duration, this metric makes it possible to split the measurement by DotVVM route name.
  • dotvvm_viewmodel_serialization_seconds histogram - Time it took to serialize view model to JSON objects.
    • Labels route and request_type
  • dotvvm_control_lifecycle_seconds histogram - Time it took to process a request on the specific route
    • Labels route and lifecycle_type=PreInit/Init/Load/PreRender/PreRenderComplete
  • dotvvm_command_invocation_seconds - Time it took to invoke a specific command method. Compared to request_duration_seconds this only includes the time spent in the command method, and is labeled by the executed binding (command)
    • Labels command and result=Ok/Exception/UnhandledException
  • dotvvm_staticcommand_invocation_seconds - Similar to command_invocation_seconds, but for staticCommand invocations
    • Labels command and result=Ok/Exception/UnhandledException
  • dotvvm_viewmodel_validation_errors_total histogram - Number view model validation errors returned to the client.
    • Labels route and request_type
  • dotvvm_uploaded_file_bytes - Total size of user-uploaded files
  • dotvvm_returned_file_bytes - Total size of returned files. Measured when the file is returned, not when downloaded by the client

If you are using Server-side viewmodel cache you might be also ...

Read more

DotVVM 4.1

06 Feb 15:45
Compare
Choose a tag to compare

Potentially breaking changes

While we try to keep "minor" releases free of major breaking changes, it's sometimes impossible to avoid. In general, you should not have problems updating to 4.1. If you encounter a breakage not on this list, please submit a bug report to this repository, or contact us on gitter.

  • In general, we aim to be source-compatible, not binary-compatible. If you are using other DotVVM packages, make sure all are on 4.1 version; otherwise MissingMethodExceptions (or similar) might occur.
  • 4.0 had a bug that allowed using private members from binding. This is fixed in 4.1, so you will now get an error when a private member is used in binding expressions.
  • 4.0 had a bug which treated { get; init; } properties as if they had normal setter. DotVVM 4.1 will always clone the object before setting an init-only property.
  • We made the ReflectionUtils.PrimitiveTypes collection private. Please use the IsPrimitiveType method. In case it doesn't fulfill your requirement, please contact us. We plan to add support for custom primitive types in the next release.

We recommend using the view compilation page at _dotvvm/diagnostics/compilation after updating to check that all views still compile.

Record serialization (#1246 #1525)

DotVVM can (finally) correctly serialize and deserialize value types and records with constructor properties. F# records and single-case unions are also supported.

record UserInfo(string Name,
                string Id,
                string Email);

In general classes, the non-default constructor has to be marked with the constructor [JsonConstructor] attribute. Without it, DotVVM will not automatically deserialize properties into constructor arguments, even when no other constructor is available (to avoid breaking existing code)

class C {
    public string Name { get; }

    [JsonConstructor]
    public C(string name) {
        this.Name = name;
    }
}

HierarchyRepeater (#1206)

DotVVM now has a generic control for displaying hierarchies that support both client-side and server-side rendering. The following code will create a hierarchy of ul / li tags from a collection of Roots. The ItemWrapperTag (li) only contains a template for a specific item, while LevelWrapperTag (ul) also includes all child items.

<dot:HierarchyRepeater DataSource={value: Roots}
                       ItemChildrenBinding={value: Children}
                       LevelWrapperTagName=ul
                       ItemWrapperTagName=li >
     {{value: Name}}
</dot:HierarchyRepeater>

Note that you can set any other attributes or classes on the wrapper tags using the Level or Item prefix, for example LevelClass-highlighted={value: Name == _root.HighlightedName}.

AutoUI (#1293)

DotVVM Dynamic Data is a library that generates forms and grids automatically from your model classes. We have rewritten it using precompiled CompositeControls and server-side styles. This makes it much easier to extend and customize, and also eliminates performance issues the old DynamicData library had.

We renamed this new implementation as DotVVM AutoUI, as it's... an automatic UI generator, not data and not dynamic :)
The old DynamicData library still works as before, although we don't plan to develop it further.

Since there is a lot to cover, please refer to the dedicated page for more information. In short, this how the usage looks like:

<!-- create from with all the fields -->
<auto:Form DataContext="{value: EditedEmployee}" />

<!-- a table with automatic columns (we support inline editing too) -->
<dot:GridView DataSource="{value: Employees}">
    <auto:GridViewColumns />
</dot:GridView>

<!-- basic form component based on data type and attributes. Includes validation -->
<auto:Editor Property="{value: Age}" />

Markup Declared Properties (#1231)

Properties of markup controls can now be defined directly in the control .dothtml file:

@property string Name

<div class="name">
   {{value: _control.Name}}
</div>

For each property, you can specify:

  • A default value, for example
    • @property string Width = "100%"
    • @property string[] Classes = [ "something" ]
    • @property Func<int, int> GetNextId = (int i) => i + 1
  • Markup options, for example @property string Name, MarkupOptionsAttribute.Required=true

Note that @baseType and @property directives cannot be combined, as it could lead to ambiguities where the property is defined.

CompositeControl precompilation (#1261)

DotVVM 4.0 introduced Composite controls and made Server-Side Styles much more powerful.

DotVVM 4.1 builds on this foundation and introduces Composite control which is only evaluated once - when the page is being compiled.
This is very useful when the component needs to create and compile new bindings (like Auto UI) since that is a bit too slow to do on each request.
It also allows other server-side styles to match the generated content and customize it.

The usage is fairly simple:

[ControlMarkupOptions(Precompile = ControlPrecompilationMode.IfPossible)]
public class MyControl : CompositeControl
{
    public DotvvmControl GetContents(
        ValueOrBinding<string> myProperty,
        string otherProperty
    )
    {
       return ...;
    }
}

This will try to create the control during page compilation, invoke the GetContents method and replace the component with the generated control tree. The component instance won't even be created at runtime.

However, if otherProperty contains a resource binding, it's not possible to call GetContents before the binding is evaluated. This is fine, the control will be evaluated at runtime normally. You might decide that for certain combinations of arguments, the precompilation is not possible and throw a SkipPrecompilationException. Please try not to depend on cases where DotVVM automatically decides precompilation isn't possible, since we might want to add support for them in future versions. If you want to make sure the control never falls back on runtime evaluation, you can use the ControlPrecompilationMode.Always setting.

A special mode is ControlPrecompilationMode.InServerSideStyles which is used by AutoUI. This instructs DotVVM to build the control while server-side styles are running which allows other styles to modify the children. Normal precompiled controls are evaluated after styles are finished, in order to behave similarly to runtime controls which are also unaffected by styles.

JsComponent is stable now

DotVVM 4.0 included experimental support for integrating components written in JavaScript; as of DotVVM 4.1 we consider the React integration stable. To include a React component into a DotVVM view:

  • Install the dotvvm-jscomponent-react npm package
  • Setup your favorite JS bundler (and optionally a typescript compiler) to produce ES module bundle (let's say it's called scripts/mycomponents.js)
  • Register the script for DotVVM: config.Resources.RegisterScriptModuleFile("mycomponents-js", "script/mycomponents.js")

The module should export the components in the $controls field

import { registerReactControl } from 'dotvvm-jscomponent-react';
import * as React from 'react';

function SampleComponent(props) {
   return <span>{props.text}</span>
}

export default (context) => ({
    $controls: {
        SampleComponent: registerReactControl(SampleComponent),
    }
})

Then you can import it in any DotVVM view or markup control using the @js directive and use it with the <js: tag prefix.

@js mycomponents-js

<js:SampleComponent text={value: Text} />

For more information, see the documentation page, our sample project or the sample we use as UI test.

We'd like to thank @lorencjan for thoroughly testing the dotvvm-jscomponent-react package, many improvements have been made based on his suggestions.

We also have working Svelte integration. However, it's not published as a npm package at the moment - if you want to try it out, you can copy the integration function from our repository (yes the link correct, all the code really fits on one screen). If you'd like to try any other JS web framework, it should be quite easy to wire it into the DotVVM API. We'll definitely welcome any pull requests, but feel free to also submit an issue requesting it. If you can "only" help with testing, that's also very valuable.

Other smaller changes

  • DotVVM now implements `IHeathCheck on ASP.NET Core. It checks whether all pages can be compiled. #1209
  • control.AddCssClass and control.AddCssStyle extension methods now also support ValueOrBinding as input. It will now also work when it's called multiple times with a binding. (#1274, #1354)
  • Control property can now be IEnumerable<DotvvmControl> or IEnumerable<IMyControl>, previously it had to be List<DotvvmControl> (#1325, #1355)
  • `BindingCompilationService.Cache.Creat...
Read more

DotVVM 4.0

23 Mar 18:41
Compare
Choose a tag to compare

DotVVM 4.0 Release notes

Breaking Changes

ordered by severity:

  • We dropped support for IE 11. There is no workaround, except for using some better browser
  • Validation API has changed, see the Validation section for more details. TL;DR: use context.AddModelError(this, vm => vm.EditViewModel.MyProperty)
  • netstandard2.1 or net472 platform is required
  • Some HTTP security headers are enabled by default. See HTTP Security Headers section
  • [Authorize] attribute is now deprecated, please use the context.Authorize() extension method instead. The Authorize attribute still works and we don't plan to remove it, but it's marked obsolete for a reason (see Authorization)
  • Control properties not using the DotvvmProperty.Register or DotvvmPropertyGroup.Register are no longer supported and exception will be thrown.
  • DotvvmControl.SetValue method used to write into the value binding if it was possible. However, this behavior was unreliable so it now always replaces the binding with the new value. If you want the old behavior - to set the value to the binding, use the .SetValueToSource method.
  • Some typos in the public API were fixed (see #1051)
  • Method in server-side styles .SetControlProperty<MyControl>(TheProperty) is replaced by .SetControlProperty(TheProperty, new MyControl()). Also additional using may be required for extension method to be found.
  • DotVVM Error page now only displays errors from DotVVM pages, not from subsequent middlewares. If you are using both DotVVM and other technology in your backend, add the ASP.NET Error Page too.
  • Client-side Repeater uses a separate <template id=...> element instead of placing the template inside. If it poses a problem, it can be configured by setting RenderAsNamedTemplate=false.
  • RouteLink renders the link even in client-side mode.
  • Literal renders the binding even in server-side rendering mode. Use a resource binding to force it to render the value server-side without generating the Knockout binding expression.
  • DotVVM.Diagnostics.StatusPage is now deprecated, the page is now included in DotVVM.Framework package (at _dotvvm/diagnostics/compilation). Please uninstall the package from your project.

New Authorization API

TL;DR: Use context.Authorize(...) instead of [Authorize(...)]. Place it in the OnInit method of your view model or into the respective command/staticCommand. The IDotvvmRequestContext can be injected from DI into your static command service constructor or directly imported in DotHTML using @service context=IDotvvmRequestContext and put into staticCommand argument.

We deprecated the [Authorize] attribute, because it was not called in some unexpected cases (when the command is called from another command, or when the view model class is placed inside another view model). We could fix those few issues, but there would probably always remain some weird cases, so we decided to deprecate the auto-magical invocation ofr the attribute in favor of calling the Authorize method explicitly. It's much easier to debug why your code does not invoke the method than to debug why DotVVM won't call it. Also, when the .Authorize call is placed right next to the logic it's supposed to guard, there is almost no way it wasn't in fact authorized. Since developers rarely write UI tests checking whether auth works properly, we decided calling a method is much safer option for such a critical task.

HTTP Security Headers

Firstly, this DOES NOT magically protect your application from all kinds of attacks. Most common attacks are related to wrongly configured authentication and insuficient user input validation, and none of these headers can help with that. Please remember that everything in your viewmodel which is not marked by the [Protect] attribute is suddently a user input in a command or static command.

Also, we only set headers which are not likely to cause any problems to your application... which, incidentally, are the headers which are less effective 🙃. Please don't forget to set the Strict-Transport-Security yourself and check your application using Mozilla Observatory.

  • X-Frame-Options: DENY is used by default. It should pose no problem, since DotVVM did not work with iframes anyway due to cookie SameSite policy. When CrossSite frames are enabled, the cookie will have SameSite=None

    • Configured in config.Security.FrameOptionsSameOrigin and config.Security.FrameOptionsSameOrigin. Please enable the cross-origin iframes only for routes where you really need it.
  • We also check Sec-Fetch-Dest - if it's an iframe, we validate on server that iframes are allowed. This mostly allows us to print a nicer error message, but may also in theory prevent some timing attacks using iframes

  • X-XSS-Protection: 1; mode=block blocks some kinds of XSS attacks. Probably not super useful, but also not harmful.

    • Configured by config.Security.XssProtectionHeader
  • X-Content-Type-Options: nosniff disables inference of content-type based on content, this could prevent some XSS attacks. Probably also not super useful, but very unlikely to cause any problems.

    • Configured by config.Security.ContentTypeOptionsHeader
  • When on https, we set the session cookie with __Host- prefix. This prevents it being used by subdomains. Can help only in obscure cases - for example when a system on the subdomain is compromised, the attacked can not pivot to the parent domain so easily.

We also check the Sec-Fetch-* headers - that tells us what the browsers intends to do with the page and whether it is a cross-origin or same-origin request. Basically, we don't allow cross-origin POST and SPA requests. We also don't allow JS initiated GET requests to DotVVM pages, use the config.Security.VerifySecFetchForPages option to disable this, if you rely on it.

If the browser does not send these Sec-Fetch-* headers, we don't check anything. You can enable strict checking by config.Security.RequireSecFetchHeaders option. By default it's enabled on compilation page to prevent SSRF and it does not matter too much if it does not work for someone with an obsolete or broken browser.

Improved server-side styles

Server-side styles is a powerful metaprogramming feature available since the early preview versions of DotVVM. However, we have significantly refactored and improved the API in this version to allow access to strongly typed properties.

Most of previous code should remain compatible, except for the SetControlProperty<TControl> method which should be replaced by .SetControlProperty(property, new TControl()).

Styles.Tag DotHTML property is introduced to allow easier matching to specific controls. For example, it may be used like this:

c.Styles.RegisterAnyControl(c => c.HasTag("prepend-icon"))
        .Prepend(new HtmlGenericControl("img").SetAttribute("href", "myicon.png"));

// or this rule applies to any descenant of a tagged control
// it will use <span data-bind="text: ..."></span> instead of knockout virtual element for each literal

c.Styles.Register<Literal>(c => c.HasAncestorWithTag("literal-spans"))
        .SetProperty(c => c.RenderSpanElement, true);

It is now possible to create controls in normal C# code and then put them into children, control properties or append/prepend them to the matched control. Use .PrependContent, .AppendContent, .SetControlProperty, .Append and .Prepend. You can also replace the entire control with a different one using .ReplaceWith which will also copy all properties onto the new control. Last but not least, you can use .WrapWith to place a wrapper control around it.

In previous version, it was only possible to set a constant value into the properties, now it's possible to compute the value for each control using a supplied lambda function:

// this will apply a confirm postback handler to all commands and staticCommands on this control
c.Styles.RegisterAnyControl(c => c.HasTag("confirm"))
    .AppendDotvvmProperty(
        PostBack.HandlersProperty,
        c => new ConfirmPostBackHandler(c.GetHtmlAttribute(
            "data-confirm-msg") ?? "This is a default confirm message")
        );

It is also possible to process a binding in the API. For example, this transforms every usage of the Visible property into a CSS class regardless whether it's a static value or a value / resource binding.

c.Styles.Register<HtmlGenericControl>(c => c.HasProperty(c => c.Visible))
        .SetPropertyGroupMember("Class-", "hide", c =>
            c.Property(c => c.Visible).Negate())
        .SetProperty(c => c.Visible, true); // reset the Visible property

The .Negate() is a extension method defined on the new ValueOrBinding<bool> type. There are many others including .Select(t => ...) where you can supply any lambda function which can be translated to a JavaScript binding.

Validation

We completely reworked the way validation works internally. Validation property paths are now always expressed as absolute paths from the root viewmodel to the affected property. Therefore, property paths always begin with the '/' character (expressing they are starting at the root viewmodel) and this character is also used to delimit individual path segments. Examples:

  • /Customer/Id
    • this means that there is a property Customer in the root viewmodel
    • the error is attached to the property Id on the Customer.
  • /Items/0/Price
    • this means that there is a collection Items in the root viewmodel
    • the error is attached to the element with index 0 o...
Read more

DotVVM 3.2

30 Aug 09:53
Compare
Choose a tag to compare

New features

Added more JavaScript translations

#1059, #1062, #1100
We continued with our effort to add JavaScript translations for common .NET methods and some custom extensions. You can use these methods inside data-binding to express more complex operations and have them evaluated on the client's side.

  • DateTime property getters (e.g. Year, Month, Day etc.)
  • WebUtility methods (UrlEncode(str), UrlDecode(str))
  • Numerous new string methods
    • IsNullOrWhiteSpace(str)
    • Many overloads for comparing strings with the StringComparison parameter
    • Many trimming and padding method overloads

Improvements and fixes

Changed type of viewmodel to any

#1069
The type of dotvvm.state is now any. This allows for an easier access to the underlying state of viewmodels without the need of repeated casting.

Prefer to use state in static commands

#1078, #1113
We fixed a synchronization issue that occurred with state, which could have been observed using a staticCommand. Changes made to state in user code were not always correctly reflected. As a result it was possible to read old data using staticCommand.

Fixed OnPreInit invocation on DotvvmMarkupControl

#1082
Usage of the parameterless constructor on DotvvmMarkupControl may have previously caused weird issues in connection with the @js directive. This was caused due to the fact that the necessary resource was not loaded.

Fixed NRE when using NamedCommand without the @js directive

#1083
DotVVM no longer throws a NullReferenceException in case user specifies a NamedCommand without the @js directive. This use case is still not supported, however, DotVVM now throws a better and more descriptive exception that tells user what is wrong.

Improved error reporting when a function from viewmodule module fails

#1085
DotVVM now provides a better error whenever a function from a viewmodule (also known as a JavaScript module) fails.

Fixed issue with not loading assemblies with custom controls

#1087
Some users and components register controls using the config.Markup.Controls.Add(...) method call. However, this does not update the assemblies list in config.Markup.Assemblies and as a result DotVVM may have previously missed to load some necessary assemblies.

Fixed issue with type inferer

#1093
Type inferer for lambda parameter types no longer fails on some of the supported and more complex .NET methods.

Fixed REST API bindings generation

#1098
We fixed many issues in connection with REST API bindings generation. Generated code should now be correct and not dependent on the global Newtonsoft.Json settings. See also notes about breaking changes regarding this feature.

Fixed registration of viewmodules in markup controls

#1103
Viewmodules are now registered only for pages that have the @js directive. Previously the property that indicates whether a module is referenced could have been inherited by a markup control.

Fixed issue with indexing expression evaluating to null

#1120
DotVVM now generates expressions that are less strict whenever a null gets passed as an indexing expression, for example as a key to a dictionary. Instead of a compilation exception, it now propagates the null value as a result of the whole expression.

Fixed indexer's get method translation for dictionaries

#1121
We fixed an issue in the JavaScript translation for the indexer's get method for dictionaries.

Breaking changes

Behavior change after regenerating REST API bindings

Newly generated and regenerated bindings will have some changes. This is due to the fact that we had to update underlying Nswag version that changed the way API clients are generated. Most notably, there are the following differences:

  • HttpClient is now cached and passed as an argument for the API client's constructor
  • Generated code uses an ApiException instead of the previously used SwaggerException.

Users that do not regenerate their bindings are not affected by this change.

Tooling

Azure pipelines

#1067
Continuous integration system is now capable to run DotVVM tests both on Windows and Linux using newly defined azure pipelines.

DotVVM 3.1

07 Jul 11:48
Compare
Choose a tag to compare

New features

Added automatic detection for lambda parameter types

#936
DotVVM is now able to infer types of lambda parameters from the context the lambda function was specified in. This makes it possible to specify more concise lambda expressions. For example instead of a Customers.Where((CustomerDto c) => c.Id > 0) it is now possible to write Customers.Where(c => c.Id > 0).

Added support for custom extension methods

#946
We have extended the capabilities of the @import directive to also search for available extension methods within provided namespaces. This feature allows users to use their custom extension methods inside data-binding.

Added more JavaScript translations

#965, #1009, #1029, #1039
We added a lot of new JavaScript translations for common .NET methods and some custom extensions. You can use these methods inside data-binding to express more complex operations and have them evaluated on the client's side.

  • Enumerable methods, string methods and Math methods
  • List<T> methods
  • Dictionary<K,V> methods

For a full list of supported .NET methods inside data-binding, see the following page from the documentation: Supported expressions.

Added support for string interpolation

#987
String interpolation is a well-known feature from C# that allows specifying a string prefixed with a '$' sign together with some interpolated expressions to be evaluated during runtime. Additionally, you can alter the way your interpolated expressions are converted into strings by providing optional formatting options. In DotVVM this is always a syntax sugar for calling string.Format(...).

Added support for dictionaries in viewmodels

#1014
It is now possible to use dictionaries in viewmodels. DotVVM now supports the usage of indexer in data-binding so that you can read values, change existing values, but also create new key-value pairs using the indexer on dictionaries.

Added support for passing AbortSignal into the postback

#1021
It is now possible to abort an in-progress postback. This can be useful to signalize if you already know that the result of postback is no longer needed.

HtmlGenericControl supports numeric and boolean values for attributes

#1028
Resource bindings previously supported only string values and collections of string in attributes. We extended this by supporting also: numeric types, bools, enums and Guids. DotVVM interprets booleans as indications whether a specific attribute exists or not. This means that an attribute is omitted when it is false.

Improvements and fixes

Fixed issues with assigning lambda functions to some known delegates

#936
Lambda functions can now be assigned to System.Actions and System.Predicates alongside with already supported System.Funcs.

Improved error reporting when a master page is corrupted

#976
Whenever a user makes a mistake while specifying a viewmodel for a specific master page, the error page no longer shows its type to be an UnknownTypeSentinel. Users now get a more descriptive error message that provides hints on how to resolve this issue.

Fixed generics resolving and method overloads search

#978
It is now possible to call more complex generic methods. Previously there were issues with matching generic parameters owned by types instead of methods, but also applying implicit conversions in some places.

FileUpload control does not need an iframe

#988
We have got rid of the iframe for FileUpload control. This makes it possible to use this control under more environments as we are aware that iframes can be discouraged by certain configurations.

Added helper functions for working with type metadata

#1013
We introduced a few utility functions to improve the experience when working with type metadata. You can find these functions under dotvvm.metadata when working with JS/TS.

Fixed serialization of byte[]

#1015
Byte arrays are no longer serialized as base64-encoded strings as this caused issues with coercing during deserialization.

Fixed RouteLink control with targets in SPA

#1016
SPA-routing logic is no longer applied on RouteLinks with targets that are specified within a SpaContentPlaceHolder. This makes it possible to use, for example target="_blank" to open a new tab from a SPA.

Updating non-existing array element should do nothing

#1020
Updating an array element from staticCommand after it was removed from viewmodel now does nothing. Previously it caused various issues when updating array elements with undefined values.

Warn when an uppercase attribute is used

#1022
Usage of uppercase attributes now generates a warning as it is likely a reference to a non-existing property. To silence these warnings, make sure to use all lowercase letters for standard HTML attributes.

Fixed issue with array and nullable types in lambda parameters

#1031
DotVVM compiler no longer throws whenever a user tries to create a lambda function with either a nullable type or an array type as one of its parameters.

Fixed compiler deadlock when building controls

#1035
We fixed an issue that made it possible to deadlock DotVVM compiler under special circumstances when building controls.

Better error messages for invalid resource name in @js directive

#1041
DotVVM no longer throws a NullReferenceException when the specified JS module is not found. We also improved error message for the case when the referenced module is not of ScriptModuleResource type.

Fixed issue with assigning to arrays and lists

#1052
DotVVM now correctly translates staticCommands that used indexer to change an element under a specific index.

Removed usage of culture-sensitive string operations

#1054
We replaced all occurrences of .ToUpper() and ToLower() calls with their culture-invariant alternatives. This previously caused some issues with, for example Turkish language.

Fixed compilation issues with extension methods

#1058
Repeated importing of the same namespace no longer ends with a compilation exception "Found ambiguous overloads of method ...". Additionally, DotVVM now correctly compiles extension methods that use default parameter values.

Fixed issue with assigning values to nullable types

#1071
Nullable properties can be assigned using either a nullable value or a non-nullable value (an implicit conversion should be used). We added the missing conversion that fixes compilation of the second case.

Tooling

Command-line interface

#872
Major rewrite of command-line interface and the stand-alone compiler executable. The compiler executable is no longer embedded in DotVVM NuGet package, but instead we introduced the DotVVM.CommandLine NuGet package.

DotVVM 3.0

03 Apr 12:54
Compare
Choose a tag to compare

New features

Calling JavaScript from DotVVM and vice versa

#935
We have added the new @js directive which allows to import a ECMAScript module in a page, master page or a markup control, and call its methods using _js.Invoke<TResult>("method", args...). Also, there is a new NamedCommand control which allows exposing DotVVM command or static command to the JavaScript code, so it can be called (including passing arguments). This allows easy interactions between DotVVM components and the world of JavaScript.

Variables are supported in binding expressions

#869
You can now declare variables in binding expression, which enhances the abilities especially in static command bindings — e.g. {staticCommand: var result = _myService.GetItems(); Items = result.Items; TotalCount = result.TotalCount}. The variable must be assigned immediately, and cannot be re-assigned to a different value (behaves similar to const in JavaScript).

Lambda support in binding expressions

#903
You can use lambdas in data-bindings, which opens many new possibilities, for example calling LINQ methods or expose command bindings to JavaScript code that can get custom parameter values.

New client-side event initCompleted

#915
This event is called after the viewmodel is applied to the page DOM.

Additional metadata can be returned with the command result

#916
This allows returning additional information together with results of command or static command calls. This information can be read e.g. in the afterPostback event or in a postback handler, and used to perform any client-side action (displaying an alert, custom validation, and so on).

Extension methods are supported in binding expressions

#931
Right now, we only support few white-listed extension methods from System.Linq namespace, but we'll allow to call any extension method based on @import directives in DotVVM 3.1. This is already implemented in #946, but the PR is still being reviewed so it didn't make it into 3.0.

NPM package with TypeScript definitions

#952 #956
If you want to write TypeScript code that interacts with DotVVM, you can now use the dotvvm-types package.

DotVVM uses non-minified version of scripts when config.Debug is true

#970
This helps with debugging script errors by peeking into non-minified DotVVM code.
Minified version of DotVVM has now about 48 kB, plus 69 kB for Knockout JS. With HTTP compression enabled, the total download size of DotVVM and Knockout together is ~55 kB. Make sure you don't deploy your apps to production with the debug flag — the debug scripts are significantly larger.

Verbose client-side logging flag

#971
If you want to see more detailed logs in the dev console, create a dotvvm-loglevel entry in the local storage of the site and set it to verbose. DotVVM will emit information about client-side events, and more.

Improvements and fixes

Refactoring of the client-side part, migrated to the modern TypeScript, rollup bundler.

#834
All client-side code of DotVVM was refactored and now takes advantage of the newest TypeScript language features (ES modules and so on). We are using rollup to create bundles for modern browsers as well as for Internet Explorer 11. We also have different bundles for SPA and non-SPA pages (non-SPA are slightly less).

Client-side events and postback pipeline

#885
We have reviewed and thoughtfully refactored the postback processing pipeline, fixed many issues, and resolved several inconsistencies (for example: some client-side events didn't get all arguments that one would expect, and the internal implementation of error handling was not very straight-forward and maintainable). See the Breaking changes section — there were some API changes which may affect you.

JavaScript tests migrated to jest

#883
We now have much more client-side tests as well as stress test which help us detect race conditions in postback concurrency handling.

Multiple SpaContentPlaceHolder controls can be used on the same page

#847
We have also removed the old format of SPA with the hashbang (#!) construct in the URL in favor of History API which is supported even in IE 11. The DefaultRouteName, PrefixRouteName and UseHistoryApi properties are deprecated — History API is used by default, and it's the only option. The links in the old format will still work — DotVVM will redirect to the new URL format.

Viewmodel state is represented by an immutable JavaScript object

#884
The state of the viewmodel is not kept in the hierarchy of Knockout observables, but in a new state manager's immutable object. The hierarchy of Knockout observables is still there, but it is “virtual”. Every change via Knockout observables will patch this immutable state, and the changes propagate asynchronously to all affected places in the Knockout observable tree. It helps to create a similar effect as deferred option in Knockout. The state manager offers new API which lets you access the viewmodel without messing with observables (state property, setState and patchState functions).

Improved resolving of generic methods and overloads for custom JS translations.

#892 #937
From version 2.0, DotVVM offers to supply custom C# to JS method translations. We have fixed some issues and enhanced the method matching to support generic arguments, param arrays, and more.

Type-safe viewmodels

#905
DotVVM passes type metadata alongside the viewmodel, so we can make sure the viewmodel won't allow assigning e.g. array to a property of string. DotVVM checks all assignments to the viewmodel properties against this type definition, and throws errors in case of type mismatch. In some situations, it can coerce the value to the correct type (e.g. number to string).

Static commands can be passed to markup controls

#928 #943
Markup controls did allow passing value and command bindings from the outside, but it didn't work for static commands.

Route redirection helpers enhancements

#938
You can modify query string arguments when you use AddRouteRedirection feature introduced in DotVVM 2.5.

Markup files can be referenced from a directory outside the application directory

#908

Fixed bug in static commands with multiple server calls

#910

Progress in our effort to use non-nullable reference types

#912

Fixed URL-encoding problems in RouteLink parameters

#922

Customizable active and disabled CSS classes in DataPager

#923

Fixed issue with async command execution

#933

Progress in making our UI tests more reliable

#949
We have introduced retry logic into our Selenium test helpers which will help us clean explicit waits in our tests. Thanks to this, the tests will hopefully be more readable, easily maintainable, and reliable. Right now, it is common that from the hundreds of UI tests, one of two just fail randomly — that's why the build status of DotVVM is often shown as failing.

Fixed culture-dependent letter case conversion to invariant in the serializer

#951
This was a community-authored PR which helped us realize that we have to consider the culture invariancy every time when ToUpper/ToLower is used. "I".ToLower() resolves to "i" in most cases, but in Turkish it is "ı". Many thanks!

Fixed bug in concurrency handler in static commands

#957

Breaking changes

Null propagation now passes nulls into methods

#817
Previously, any binding expression which evaluated to null caused to evaluate the parent expression to null as well. If there was a method call in the expression, and any of the arguments evaluated to null, the entire method call was skipped and evaluated to null. We changed this behavior so the null is passed in the method.

Script resources use defer by default

#930
DotVVM resources was marked as deferred, which means that all other scripts that depend on DotVVM need to be deffered too (otherwise, they would load before DotVVM and fail). We have changed the default behavior of ScriptResource so the Defer property is true by default.

Async IUploadedFileStorage and IReturnedFileStorage

#981
We changed these interfaces to use async methods everywhere. The behavior hasn't changed, you'll only need to add await to the GetFile or ReturnFile calls.

Type-safe viewmodels

#905

  • Objects in the viewmodel now always have the $type property which contains unique ID of the concrete type. When object without the $typeannotation is added in the viewmodel, DotVVM will infer the type automatically.
  • Properties of type DateTime are now validated (even if there is noRequired attribute). From DotVVM 3.0, empty date values won't be sent to the server.
  • If you call dotvvm.serialization.deserialize or dotvvmserialization.serialize methods in the client-side DotVVM, consider replacing them with the new state manager API (state property,setState and patchState functions on the observables). You can also assign plain JS objects to the viewmodel observables — they will be coerced automatically. We plan to remove the serialize anddeserialize methods from the public API in the future versions.

Client-side events and postback pipeline

#885
Due to the refactoring and cleanup, most of the client-side events had some changes in the structure of event arguments (new properties were added).

  • error — we have removed the isSpaNavigationError flag, and we have added a new event spaNavigationFailed that is fired in case of SPA navigation failure (network error, exception on page load etc.)
  • afterPostback — we have removed redirectPromise and handled parameters
  • spaNavigating — the newUrl parameter was re...
Read more

DotVVM 2.5

31 Oct 19:50
Compare
Choose a tag to compare

This release includes all changes from DotVVM 2.5 Preview 2, as well as a few additional improvements:

Features

  • #880 - Inline editing is now supported in nested GridView controls
  • #890 - DotVVM uses its own JsonSerializerSettings and doesn't rely on DefaultSettings
  • #877 - Control developers now may create attached property groups (e. g. <element MyExtensions.SomeGroup-SomeKey="something" />)
  • #896 - The AddDotVVM and UseDotVVM methods now allow passing a custom instance of the DotvvmStartup class so it is possible to set its properties and so on. If you plan to use this feature, we recommend keeping a default constructor - otherwise, the DotVVM Compiler (used by the Visual Studio extension) will be unable to create the instance of this class.

Fixes

  • Fixed references to netstandard in view compilation - these didn't work on older versions of .NET Framework.
  • Fixed deadlock occurring on the first HTTP request on OWIN.
  • Fixed incorrect invocation of the beforePostback event on static commands.

Enhancements for framework developers

  • #874 - Infrastructure for end-to-end testing without the need of using Selenium.
  • All paths are using forward slashes so everything in the repo should work on non-Windows machines.

DotVVM 2.5 Preview 2

23 Aug 20:14
Compare
Choose a tag to compare
DotVVM 2.5 Preview 2 Pre-release
Pre-release

New Features

Experimental: Explicit assembly loading

Until now, DotVVM was searching all assemblies referenced by the application (including transitive references) to be able to find attached properties of controls (which may be declared in a different assembly than the controls), @import namespaces and other types. This was causing longer startup times as all assemblies were being searched.

We've introduced the explicit assembly loading mode that loads and searches only the assemblies that you list explicitly in DotvvmStartup:

config.Markup.Assemblies.Add("assemblyName");

This feature is experimental and must be turned on:

config.ExperimentalFeatures.ExplicitAssemblyLoading.Enable();

We expect this feature to help a lot especially in the Web Forms modernization story - many legacy applications have unused references that are not even present in the bin directory. Without this feature, DotVVM was forcing the developers to fix all these issues first, which hurts the experience with getting DotVVM running.

Redirection Helpers in the Route table

(#858)

We've added AddUrlRedirection and AddRouteRedirection in the DotVVM route table to simplify creating URLs that redirect to another place.

It can be used e. g. for preserving old links when you need to change the URL for your pages.
You can now register these old routes like this:

// redirect from "article/15" to "content/article/15"
config.RouteTable.AddUrlRedirection("ArticleRedirection", "article/{id}", c => "content/article/" + c.Parameters["id"]);

// redirect from "calendar" to "calendar/{DateTime.Now.Year}/{DateTime.Now.Month}"
config.RouteTable.Add("Calendar", "calendar/{year}/{month}", "Views/Calendar.dothtml");
config.RouteTable.AddRouteRedirection("CalendarRedirection", "calendar", "Calendar", parametersProvider: (context) => {
    var now = DateTime.Now;
    return new Dictionary<string, object>() 
    {
        { "year", now.Year },
        { "month", now.Month },
    };
});

View compilation on startup or on background

(#841 and #846)

Until now, all DotHTML files (views, master pages, and user controls) were compiled to their C# representation on the first HTTP request that needed them. Because of this, the first load of every DotVVM page took longer.

We've added two new options:

  • DuringApplicationStart - this mode compiles all views and delays the startup of the application until the compilation is finished. The first HTTP request will be handled after all views have been compiled. This option is meant for production scenarios where you are using deployment slots (which will be switched once the application starts responding), or where downtime of a minute or two is not an issue.
  • AfterApplicationStart - this mode doesn't delay the startup of the application but compiles all views on a background thread. It is possible to set a delay (configuration.Markup.ViewCompilation.BackgroundCompilationDelay) so it doesn't stress the CPU when other app initialization (populating caches etc.) tasks may be in progress. This option is great for applications that prioritize the page load time and have enough compute power so the background compilation won't affect the overall server performance.

For development scenarios, we recommend sticking to the default Lazy mode as it doesn't slow down the application startup and compiles only the views that are actually needed (in the typical dev loop, the developer is not working with all pages of the app at the same time).

REST API bindings upgraded to latest Swashbuckle.AspNetCore

(#855)

ASP.NET Core 3.0 introduced the new System.Text.Json serializer that replaced Newtonsoft.Json. This, and other changes, affected our Swashbuckle extensions for REST API bindings that helped DotVVM to generate better API clients based on Swagger / Open API metadata.

The DotVVM.Api.Swashbuckle.AspNetCore package now references Swashbuckle.AspNetCore 5.4.1 which works with both ASP.NET Core 2.x and 3.x.

If the API consumed by DotVVM app is hosted in ASP.NET Core 2.x, or if it explicitly uses the Newtonsoft.Json serializer in 3.x, you'll need to install Swashbuckle.AspNetCore.NewtonsoftJson package as well - otherwise the Open API metadata are not generated correctly.
See the Swashbuckle 5.x release notes for more details.

ValidationSummary improvement

  • #838 The ValidationSummary control has now a new property HideWhenValid that can hide the entire <ul> element when no errors are present. It helps when you need to apply styles like padding or border on the <ul> element.

CancellationToken

  • #857 We've added the Context.GetCancellationToken() which returns the request cancellation token. You can pass this token to async methods if you expect they'll take long - it will allow them to cancel the operation if the user cancels the HTTP request (e. g. by refreshing the page or closing the browser).

Startup performance tool

  • #851 We've created a command-line tool that helps with measuring the startup performance of (not only DotVVM) ASP.NET web apps. It is not very sophisticated, but it is easy to use.

It can be used like this:

DotVVM.Tools.StartupPerfTester.exe Path/To/The/MyApp.csproj -t owin|aspnetcore --verbose --repeat 10

The tool will publish the project in Release mode and try to run it 10 times while measuring the time until the app responds to the first HTTP request.

Other small improvements

  • #819 Added AddRequiredResource method to ResourceManager to allow adding any kinds of resources to the current page.
  • #789 and #826 We've done some improvements of build and testing scenarios for developers who work with the DotVVM repo on Linux.

Fixes

ViewModel Serialization

  • #871 Until now, DotVVM didn't support nesting [Protect] attributes in the viewmodels. If you used the [Protect] attribute on a complex object or array in the viewmodel, and the attribute was used inside too, DotVVM didn't serialize the viewmodel correctly. We've redesigned parts of the serializer so the nesting is now supported. There are some restrictions - basically, the protection is additive: if you protect the parent object e.g. by SignData, the child cannot use None on any of its members. Also, if you use EncryptData, the child object cannot use just SignData.
  • #853 We've fixed serialization of KeyValuePair in viewmodels.

Race conditions and problems in the postback pipeline

  • #816 We've fixed several bugs in the postback pipeline that caused freezing of the page or skipping some postbacks, especially when using PostBack.Concurrency="Queue". The issue was happening when multiple postbacks were invoked quickly (e. g. triggered by the incoming SignalR messages).
  • #816 When the client received the Redirect response from the server, the next postbacks from the queue were still being sent which could lead to unexpected behavior. We've fixed the issue so now when the client gets redirect response, subsequent postbacks in the queue are discarded. It was causing issues, especially in SPAs when the postback responses could arrive even after another page was loaded.
  • #860 Another PR related to cleaning up the postback queues in SPA apps.
  • #876 When the user navigated to another page in SPA and then triggered a postback, it canceled the SPA navigation and the page may stop working. We've fixed this so all postbacks during SPA navigation are ignored. In case the SPA navigation fails, the postbacks start working again so the user is able to repeat the action and continue using the old page.

DotVVM for Visual Studio compatibility issues

  • #818 Cleaned the DotvvmConfiguration JSON representation (getting rid of freezable collection wrappers) to fix the behavior of the VS extension.
  • #822 We've fixed several issues in the binding expression parser so IntelliSense now works better in the VS extension.
  • #824 Added the [PropertyGroup] attribute (for properties like class-* or Param-*) that easies the discovery of the property groups for the VS extension IntelliSense.

Binding expression cache on OWIN

  • #805 We've implemented a custom LRU (least recently used) cache for binding expressions that replaces the System.Web.Caching.Cache on OWIN. This fixes a memory leak that occurred in applications that generated a lot of binding expressions dynamically (e. g. by using DotVVM Dynamic Data). A temporary solution was to use ConcurrentDictionary-based cache, but this approach couldn't release unused bindings that weren't needed anymore.

Other issues

  • #856 Fixed NullReferenceException when calling task-based methods without async/await statement.
  • #804 We've fixed passing non-primitive command arguments to methods (it was affecting mostly the staticCommand calls).
  • #859 Fixed the dependency cycle detection in the registration of resources in DotvvmStartup.

DotVVM 2.4

28 Dec 14:41
Compare
Choose a tag to compare

Server side caching of view models (experimental)

This feature can dramatically reduce data transferred between the server and the client.

To enable this feature, add the following code snippet in DotvvmStartup.cs:

config.ExperimentalFeatures.ServerSideViewModelCache.EnableForAllRoutes();

When the feature is enabled, the viewmodels are cached on the server. When the client needs to do a postback, DotVVM can send only the diff of the viewmodel instead of serializing and transmitting the entire object. Thanks to this, we can save a lot of network traffic – almost 100% in extreme cases. Imagine a page with a GridView and a Delete button in every row – when you click on the button, the viewmodel on the client hasn’t changed at all. Thus the diff will be empty and the postback will transfer only a tiny JSON object with an identification of the button that was clicked – no viewmodel data at all. There were other ways to save this traffic, but this one is very convenient to use.

The viewmodels are stored in memory by default, but the mechanism is extensible so you can use any storage you need – just implement the IViewModelServerStore interface. They are also deduplicated – we are using a hash of the viewmodel as a cache key, so when you use this feature on a public facing site which has the same “static” viewmodel for many users, the viewmodel will be kept in memory only once. And if the item is not in the cache any more, DotVVM will signal it to the client which will immediately issue a new postback with a full viewmodel.

The feature can be enabled for all routes, or only for specific routes in the application. We recommend to enable it on less frequently used pages at first, and watch the memory consumption on the server – the cache should be cleaned up when the server gets low on memory, but still – we encourage to start using this feature carefully.

We are still working on providing diagnostic tools that will help you identify how much this feature actually helped - how much traffic it saved and how much memory on the server it consumed.

see #704 for more info

Public API is using C# 8 nullable reference

Most of the DotVVM API is now annotated by C#8 nullable reference types. If you are already using this C# feature, this may create bunch of new warnings when you upgrade DotVVM, but we recommend you do that as it may reveal new issues. If you are not using it, we suggest you give it a try, we have already discovered few glitches in DotVVM caused by nulls :)

Frozen config

To prevent strange behavior the entire DotvvmConfiguration is now frozen at runtime - it can not be modified after DotVVM is initialized.

Unfortunately, this may break some of your code if you have attempted to modify some option at runtime. On the bright side, it will fail early rather than running into race conditions (or what could happen).

If you been modifying configuration just after the UseDotvvm function, you'll have to move the config modification into a lambda argument modifyConfiguration. For example, this works fine - the configuration is modified just before it gets frozen.

app.UseDotVVM<DotvvmStartup>(..., modifyConfiguration: c => {
    c.RouteTable.Add(...);
});

#747

Combining css classes

When binding is assigned to both class attribute and Class-my-class property it used to behave in quite a strange way. With this release, the classes are combined.

This will render a div with class="a b"

<div Class-a="{value: true}" class="{value: true ? 'a' : 'x'}" />

see #783 for more info

Validation of grid view cells

We have added properties to add validators to grid view cells (for the inline-edit mode). The validator can be added by setting the property ValidatorPlacement="AttachToControl" or ValidatorPlacement="Standalone" (or both when separated by comma). Validator placement AttachToControl will add the Validator.Value to the rendered input control, placement Standalone will place separate element after the input. To control what the validator does, you can place Validator.InvalidCssClass and similar properties to the GridView.

Added InlineStylesheetResource

Like the InlineScriptResource, you can now add inline CSS (in the <style> tag) using the InlineStylesheetResource. Unlike the script, style is placed in the head, so be careful not to add styles when rendering the page.

ItemValueBinding (and friends) must return primitive types

We have decided to fix one design debt. Controls based on SelectorBase control (which is ComboBox, ListBox, and MultiSelect) have ItemValueBinding. You could put any value in ItemValueBinding - there was no validation and it caused unpredictable behavior. When the result of ItemValueBinding was primitive type everything was fine. When the result type was a complex type the behavior got strange and there is unfortunately no way how to guarantee consistent behavior. So we have added a validation that the result type is primitive to make sure everything works fine.

"Primitive" in this case means anything that is not translated to object on client-side - which are numbers, string and enums.

Redirects from static commands

You can now safely use IDotvvmRequestContext.RedirectToUrl and IDotvvmRequestContext.RedirectToRoute from static commands. Note that you can get the IDotvvmRequestContext from the standard dependency injection.

#757

API for runtime warnings

We have added infrastructure that will allow DotVVM to warn you at runtime when something suspicious happens. It will be reported to Asp.Net Core (that you'll probably see in the output of the server) and it will be propagated to JavaScript console.

For example, it will warn you if you put something inside elements that must be self-closing in HTML. We will add more of these diagnostics in future releases. It is only enabled in debug mode.

see #750 for more details (the API is described there)

StyleBuilder.SetControlProperty polishing

The SetControlProperty method used to add new controls to properties using Server Side Styles got polished. It should now work correctly in all three mode (Overwrite, Append, Ignore) regardless if the target is a control collection, a template or a single control.

You can for example add a postback handler to all buttons that satisfy some conditions:

config.Styles
      .Register<LinkButton>(m => m.HasHtmlAttribute("data-hashandler"))
      .SetControlProperty<ConfirmPostBackHandler>(PostBack.HandlersProperty, s => s.SetProperty(c => c.Message, "you sure?"), StyleOverrideOptions.Append)

You can also use SetHtmlControlProperty and SetLiteralControlProperty to easily create a html element or a simple literal.

#737

Deserialization on client

@Mylan719 has rewritten one of the most feared piece of code in our code base - the dotvvm.serialization.deserialize that copies a JavaScript object into the view model and wraps everything with knockout observables. Number of bugs got fixed in it, if you have encountered a strange behavior especially with static commands, it should be gone.

Specifically, you can now copy object from one property to another using static commands {staticCommand: DataSet = FetchData(SelectedCategory); Metadata = DataSet.Metadata }.

#772

Binding may be specified without quotes

Bindings in DotHTML files may be specified without the quotes around them (as you may be used to from React). This is now valid syntax:

<Button Click={command: MyMethod()} Text={value: MyLabel} />

Just note that if you are also using Razor/MVC, don't get too much used to this, it behaves very strangely there.

Bugfixes

  • RouteLink with binding in Query-something property now works as expected (#779)
  • Redirect now works as expected when Dictionary<string, object> is used as query. This fixes some issues with LocalizablePresenter. (#777)
  • Error are propagated correctly (i.e. dotvvm.events.error is called) when network is unreachable (#770 #768)

DotVVM 2.4 Preview 2

28 Dec 14:40
Compare
Choose a tag to compare
DotVVM 2.4 Preview 2 Pre-release
Pre-release

DotVVM 2.4 Preview 2

Support presenter factories on route groups #753

If you have several routes that use different presenter that DotvvmPresenter, you can now easily specify the presenter for all routes in a route group.

config.RouteTable.AddGroup("LocalizedRoutes", null, "Views", content => {
                content.Add("default", "", "default.dothtml");
                content.Add("About", "about", "about.dothml");
        }, LocalizablePresenter.BasedOnQuery("lang"));