Skip to content

Commit

Permalink
Merge pull request #523 from adamjez/fix/optional-param-url-builder
Browse files Browse the repository at this point in the history
Fixed generation of url with optional parameter and without default value
  • Loading branch information
exyi authored Dec 27, 2017
2 parents 469b001 + e4b9bd3 commit fdd414b
Show file tree
Hide file tree
Showing 10 changed files with 136 additions and 30 deletions.
5 changes: 3 additions & 2 deletions src/DotVVM.Framework/Controls/RouteLinkHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,9 @@ private static string GenerateRouteLinkCore(string routeName, RouteLink control,
// generate the function call

return
parametersExpression.Length > 0 ? $"dotvvm.buildRouteUrl({JsonConvert.ToString(route.Url)}, {{{parametersExpression}}})" :
JsonConvert.ToString(route.Url);
route.ParameterNames.Any()
? $"dotvvm.buildRouteUrl({JsonConvert.ToString(route.Url)}, {{{parametersExpression}}})"
: JsonConvert.ToString(route.Url);
}

private static string TranslateRouteParameter(DotvvmBindableObject control, KeyValuePair<string, object> param, bool caseSensitive = false)
Expand Down
6 changes: 4 additions & 2 deletions src/DotVVM.Framework/Resources/Scripts/DotVVM.js

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

2 changes: 1 addition & 1 deletion src/DotVVM.Framework/Resources/Scripts/DotVVM.min.js

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions src/DotVVM.Framework/Resources/Scripts/DotVVM.ts
Original file line number Diff line number Diff line change
Expand Up @@ -930,10 +930,13 @@ class DotVVM {
return ko.unwrap(ko.unwrap(array));
}
public buildRouteUrl(routePath: string, params: any): string {
var url = routePath.replace(/\{([^\}]+?)\??(:(.+?))?\}/g, (s, paramName, hsjdhsj, type) => {
// prepend url with backslash to correctly handle optional parameters at start
routePath = '/' + routePath;

var url = routePath.replace(/(\/[^\/]*?)\{([^\}]+?)\??(:(.+?))?\}/g, (s, prefix, paramName, _, type) => {
if (!paramName) return "";
const x = ko.unwrap(params[paramName.toLowerCase()])
return x == null ? "" : x;
return x == null ? "" : prefix + x;
});

if (url.indexOf('/') === 0) {
Expand Down
3 changes: 3 additions & 0 deletions src/DotVVM.Samples.Common/DotvvmStartup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ private static void AddRoutes(DotvvmConfiguration config)
config.RouteTable.Add("FeatureSamples_ParameterBinding_OptionalParameterBinding2", "FeatureSamples/ParameterBinding/OptionalParameterBinding2/{Id?}", "Views/FeatureSamples/ParameterBinding/OptionalParameterBinding.dothtml", new { Id = 300 });
config.RouteTable.AutoDiscoverRoutes(new DefaultRouteStrategy(config));
config.RouteTable.Add("RepeaterRouteLink-PageDetail", "ControlSamples/Repeater/RouteLink/{Id}", "Views/ControlSamples/Repeater/RouteLink.dothtml", new { Id = 0 });
config.RouteTable.Add("RepeaterRouteLink-PageDetail_IdOptional", "ControlSamples/Repeater/RouteLink/{Id?}", "Views/ControlSamples/Repeater/RouteLink.dothtml");
config.RouteTable.Add("RepeaterRouteLink-PageDetail_IdOptionalPrefixed", "ControlSamples/Repeater/RouteLink/id-{Id?}", "Views/ControlSamples/Repeater/RouteLink.dothtml");
config.RouteTable.Add("RepeaterRouteLink-PageDetail_IdOptionalAtStart", "id-{Id?}/ControlSamples/Repeater/RouteLink", "Views/ControlSamples/Repeater/RouteLink.dothtml");
config.RouteTable.Add("RepeaterRouteLinkUrlSuffix-PageDetail", "ControlSamples/Repeater/RouteLinkUrlSuffix/{Id}", "Views/ControlSamples/Repeater/RouteLink.dothtml", new { Id = 0 });
config.RouteTable.Add("FeatureSamples_Redirect_RedirectFromPresenter", "FeatureSamples/Redirect/RedirectFromPresenter", provider => new RedirectingPresenter());
config.RouteTable.Add("FeatureSamples_Validation_ClientSideValidationDisabling2", "FeatureSamples/Validation/ClientSideValidationDisabling/{ClientSideValidationEnabled}", "Views/FeatureSamples/Validation/ClientSideValidationDisabling.dothtml", new { ClientSideValidationEnabled = false });
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace DotVVM.Samples.BasicSamples.ViewModels.ControlSamples.RouteLink
{
public class RouteLinkUrlGenViewModel
{
public int RouteParameter { get; set; } = 1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,15 @@
<div class="container">
<h1>RouteLink enabled property demo</h1>

<p>
<dot:CheckBox Checked="{value: Enabled}" Text="Enabled" />
</p>
<p>
<dot:RouteLink RouteName="RepeaterRouteLink-PageDetail" Param-Id="0" Enabled="{value: Enabled}" Text="Static href" />
</p>

<p>
<dot:RouteLink RouteName="RepeaterRouteLink-PageDetail" Param-Id="{value: RouteParameter}" Enabled="{value: Enabled}" Text="Dynamic href" />
</p>

<p>
<dot:CheckBox Checked="{value: Enabled}" Text="Enabled" />
</p>
<p>
<dot:RouteLink RouteName="RepeaterRouteLink-PageDetail" Param-Id="0" Enabled="{value: Enabled}" Text="Static href" />
</p>
<p>
<dot:RouteLink RouteName="RepeaterRouteLink-PageDetail" Param-Id="{value: RouteParameter}" Enabled="{value: Enabled}" Text="Dynamic href" />
</p>
</div>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
@viewModel DotVVM.Samples.BasicSamples.ViewModels.ControlSamples.RouteLink.RouteLinkUrlGenViewModel

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Hello from DotVVM!</title>
<style>
.invalid {
color: red;
}
</style>
<dot:RequiredResource Name="globalize:cs-CZ" />
</head>
<body>
<div class="container">
<h1>RouteLink url generation demo</h1>
<p>
<dot:RouteLink RenderSettings.Mode="Client" RouteName="RepeaterRouteLink-PageDetail_IdOptional"
Text="Client rendered: Optional parameter" data-ui="optional-parameter-client" />
</p>
<p>
<dot:RouteLink RenderSettings.Mode="Server" RouteName="RepeaterRouteLink-PageDetail_IdOptional"
Text="Server rendered: Optional parameter" data-ui="optional-parameter-server" />
</p>
<p>
<dot:RouteLink RouteName="Default" Text="0 parameters" data-ui="0-parameters" />
</p>
<p>
<dot:RouteLink RenderSettings.Mode="Client" RouteName="RepeaterRouteLink-PageDetail_IdOptionalPrefixed"
Text="Client rendered: Optional parameter (Prefixed)" data-ui="optional-parameter-prefixed-client" />
</p>
<p>
<dot:RouteLink RenderSettings.Mode="Server" RouteName="RepeaterRouteLink-PageDetail_IdOptionalPrefixed"
Text="Server rendered: Optional parameter (Prefixed)" data-ui="optional-parameter-prefixed-server" />
</p>
<p>
<dot:RouteLink RenderSettings.Mode="Client" RouteName="RepeaterRouteLink-PageDetail_IdOptionalPrefixed" Param-Id="{value: RouteParameter}"
Text="Client rendered: Parameter (Prefixed)" data-ui="parameter-prefixed-client" />
</p>
<p>
<dot:RouteLink RenderSettings.Mode="Server" RouteName="RepeaterRouteLink-PageDetail_IdOptionalPrefixed" Param-Id="{value: RouteParameter}"
Text="Server rendered: Parameter (Prefixed)" data-ui="parameter-prefixed-server" />
</p>
<p>
<dot:RouteLink RenderSettings.Mode="Client" RouteName="RepeaterRouteLink-PageDetail_IdOptionalAtStart"
Text="Client rendered: Optional parameter (at start)" data-ui="optional-parameter-at-start-client" />
</p>
<p>
<dot:RouteLink RenderSettings.Mode="Server" RouteName="RepeaterRouteLink-PageDetail_IdOptionalAtStart"
Text="Server rendered: Optional parameter (at start)" data-ui="optional-parameter-at-start-server" />
</p>
<p>
<dot:RouteLink RenderSettings.Mode="Client" RouteName="RepeaterRouteLink-PageDetail_IdOptionalAtStart" Param-Id="{value: RouteParameter}"
Text="Client rendered: Optional prefixed parameter (at start)" data-ui="optional-prefixed-parameter-at-start-client" />
</p>
<p>
<dot:RouteLink RenderSettings.Mode="Server" RouteName="RepeaterRouteLink-PageDetail_IdOptionalAtStart" Param-Id="{value: RouteParameter}"
Text="Server rendered: Optional prefixed parameter (at start)" data-ui="optional-prefix-parameter-at-start-server" />
</p>
</div>
</body>
</html>
52 changes: 40 additions & 12 deletions src/DotVVM.Samples.Tests/Control/RouteLinkTests.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DotVVM.Testing.Abstractions;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using Riganti.Selenium.Core.Abstractions;

namespace DotVVM.Samples.Tests.Control
{
Expand All @@ -16,34 +12,66 @@ public class RouteLinkTests : AppSeleniumTest
[SampleReference(nameof(SamplesRouteUrls.ControlSamples_RouteLink_TestRoute))]
public void Control_RouteLink_RouteLinkEnabled()
{
RunInAllBrowsers(browser =>
{
RunInAllBrowsers(browser => {
browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_RouteLink_RouteLinkEnabled);
browser.Single("body > div.container > p:nth-child(2) > label > input[type=\"checkbox\"]")
.CheckIfIsNotChecked();
browser.Single("body > div.container > p:nth-child(3) > a").Click();

browser.Single("body > div.container > p:nth-child(2) > label > input[type=\"checkbox\"]").Click();
browser.Single("body > div.container > p:nth-child(3) > a").Click();
browser.CompareUrl("http://localhost:60320/ControlSamples/Repeater/RouteLink/0");
browser.CheckUrl("/ControlSamples/Repeater/RouteLink/0", UrlKind.Relative, UriComponents.PathAndQuery);
browser.NavigateBack();
});
}

[TestMethod]
[SampleReference(nameof(SamplesRouteUrls.ControlSamples_RouteLink_TestRoute))]
public void Control_RouteLink_RouteLinkUrlGeneration()
{
RunInAllBrowsers(browser => {
browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_RouteLink_RouteLinkUrlGen);

void checkNavigatedUrl(string selector, string relativeUrl)
{
browser.Single(selector).Click();
browser.CheckUrl(relativeUrl, UrlKind.Relative, UriComponents.PathAndQuery);
browser.NavigateBack();
}

checkNavigatedUrl("a[data-ui='optional-parameter-client']", "/ControlSamples/Repeater/RouteLink");
checkNavigatedUrl("a[data-ui='optional-parameter-server']", "/ControlSamples/Repeater/RouteLink");

checkNavigatedUrl("a[data-ui='0-parameters']", "/");

checkNavigatedUrl("a[data-ui='optional-parameter-prefixed-client']", "/ControlSamples/Repeater/RouteLink");
checkNavigatedUrl("a[data-ui='optional-parameter-prefixed-server']", "/ControlSamples/Repeater/RouteLink");

checkNavigatedUrl("a[data-ui='parameter-prefixed-client']", "/ControlSamples/Repeater/RouteLink/id-1");
checkNavigatedUrl("a[data-ui='parameter-prefixed-server']", "/ControlSamples/Repeater/RouteLink/id-1");

checkNavigatedUrl("a[data-ui='optional-parameter-at-start-client']", "/ControlSamples/Repeater/RouteLink");
checkNavigatedUrl("a[data-ui='optional-parameter-at-start-server']", "/ControlSamples/Repeater/RouteLink");

checkNavigatedUrl("a[data-ui='optional-prefixed-parameter-at-start-client']", "/id-1/ControlSamples/Repeater/RouteLink");
checkNavigatedUrl("a[data-ui='optional-prefixed-parameter-at-start-client']", "/id-1/ControlSamples/Repeater/RouteLink");
});
}

[TestMethod]
[SampleReference(nameof(SamplesRouteUrls.ControlSamples_RouteLink_TestRoute))]
public void Control_RouteLink_RouteLinkEnabledFalse()
{
RunInAllBrowsers(browser =>
{
RunInAllBrowsers(browser => {
browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_RouteLink_RouteLinkEnabledFalse);

//this RouteLink does not contain a binding (<dot:RouteLink Enabled="false" ... ) and should not redirect
browser.First("a").Click();
browser.CompareUrl("http://localhost:60320/ControlSamples/RouteLink/RouteLinkEnabledFalse");
browser.CheckUrl("/ControlSamples/RouteLink/RouteLinkEnabledFalse", UrlKind.Relative, UriComponents.PathAndQuery);

//this RouteLink contains a binding ( <dot:RouteLink Enabled={{value: "false" ... }} and should not redirect
browser.Last("a").Click();
browser.CompareUrl("http://localhost:60320/ControlSamples/RouteLink/RouteLinkEnabledFalse");
browser.CheckUrl("/ControlSamples/RouteLink/RouteLinkEnabledFalse", UrlKind.Relative, UriComponents.PathAndQuery);
});
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/DotVVM.Testing.Abstractions/SamplesRouteUrls.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ public class SamplesRouteUrls{

public static string ControlSamples_RouteLink_RouteLinkEnabledFalse => "ControlSamples/RouteLink/RouteLinkEnabledFalse";

public static string ControlSamples_RouteLink_RouteLinkUrlGen => "ControlSamples/RouteLink/RouteLinkUrlGen";

public static string ControlSamples_RouteLink_TestRoute => "ControlSamples/RouteLink/TestRoute";

public static string ControlSamples_SpaContentPlaceHolder_Default => "ControlSamples/SpaContentPlaceHolder/Default";
Expand Down

0 comments on commit fdd414b

Please sign in to comment.