UPDATE:
I added:
_ = endpoints.MapControllerRoute(name: "Default", pattern: "{controller}/{action}/{id?}");
and that gave me:
<form id="searchbox" action="/SearchPage/Index" method="get" autocomplete="off" class="page-searchbox tracked-search">
Still missing something.
Come to think aobut it, I think it is more strange that it works in CMS 11? Shouldn't
action="@Url.ContentUrl(Model.CurrentPage.ContentLink)"
be tha way to go?
/Kristoffer
Kristoffer I was looking into this myself and believe its due to the way the Middle Layer for routing has changed in .Net Core. As you mentioned it might be better switching to Url.ContentURL or if you have a view model at your disposle store the URL in a property.
@{ using (Html.BeginForm(null, null, Html.ViewContext.IsInEditMode() ? FormMethod.Post : FormMethod.Get, new { @action = Model.Layout.SearchActionUrl }))
{
<input type="text" class="search-query" name="q" id="SearchKeywords" placeholder="@Html.Translate("/mainnavigation/search")" />
<input type="submit" class="searchButton" id="SearchButton" value="" />
}
}
Or
<div class="searchContainer" id="searchBar">
<form class="searchWrap" method="get" action="@Url.ContentUrl(Model.SearchResultsPage)#results-top">
<div class="searchForm">
<label for="searchSite" class="offscreen">Search site</label>
<input type="text" id="searchSite" maxlength="50" name="@Configuration.Constants.QueryString.Query" placeholder="@Model.SearchBoxPlaceholderText" />
<button type="submit" class="icon searchButton">
<svg>
<title>@Model.SearchText</title>
<use xlink:href="#svg-search"></use>
</svg>
</button>
</div>
</form>
Hi Minesh!
Yes, I will solve it in some of the ways above, it will work just fine. I just wanted to know that is does not work and that I'm not just missed to add something.
I will start like that and we will see.
Thanks!
/Kristoffer
The reason is becasue of how middleware works and the order of exceution changed between the net 5/ 6 and full framework. Using contenturl is the way to go.
Have you tried using @Html.BeginContentForm instead? I think it's a new addition to CMS 12. It has a lot of methods so I created an extension method to simplify it in a couple of places:
public static class HtmlHelperExtensions
{
public static MvcForm BeginContentForm<T>(
this IHtmlHelper<T> htmlHelper,
ContentReference? contentReference,
FormMethod formMethod,
string formClass)
{
return htmlHelper.BeginContentForm(
contentReference,
CultureInfo.CurrentUICulture.Name,
null,
formMethod,
false,
new { @class = formClass });
}
}
Then on the razor file I have this:
@using (Html.BeginContentForm(Model.SiteSettings.SearchPage, FormMethod.Get, "footer-search__form"))
{
<div class="p-footer__input-group">
<input type="text"
name="query"
placeholder="@Model.SiteSettings.FooterSearchPlaceholder"
aria-label="@Model.SiteSettings.FooterSearchPlaceholder"
autocomplete="off"
required />
<button type="submit" class="footer-search__button">Search</button>
</div>
}
I'm running into this as well for Url.Action no longer working with content urls. I think the best bet in many situations may be to inject IUrlResolver in your view and call getUrl with VirtualPathArguments as you can set Action on it. Perhaps a UrlHelper extension might be a good idea for syntactic sugar.
Unfortunately, there are cases were link generation might not be in your complete control, so I would love to see content aware implementations of IUrlHelper and LinkGenerator
I'm experiencing this issue as well - @Url.Action(...) creates the MVC route rather than the content route.
What puzzles me is that Episerver Foundation tries to achieve this as well:
@Quan, can you please add this? You're literally doing the exact same thing in RedirectToContentResultExecutor. Also, while you're at it - can you add RouteValues to RedirectToContent/RedirectToContentResult and add it to the URL generator in RedirectToContentResultExecutor? :) Should be a easy thing for you, that makes life easier for us!
This is an interesting problem we have encountered too while upgrading a solution to the v12. How do you handle the query parameters when using Url.ContentUrl? Do you prefer to write them by hand or is there any extension method that can generate the link with query parameters given an anonymous object?
I've built the following helper methods to replace Url.Action and supply additional route values - it's worked out well so far
public static class UrlHelperExtensions
{
public static string ContentAction(this IUrlHelper urlHelper, string action)
{
return ContentAction(urlHelper, action, null, null, null);
}
public static string ContentAction(this IUrlHelper urlHelper, string action, object values)
{
return ContentAction(urlHelper, action, null, null, values);
}
public static string ContentAction(this IUrlHelper urlHelper, string action, ContentReference contentLink, string language, object values)
{
var routeValues = values as RouteValueDictionary ?? new RouteValueDictionary(values);
routeValues["action"] = action;
return ContentUrl(urlHelper, contentLink, language, routeValues);
}
public static string ContentUrl(this IUrlHelper urlHelper, ContentReference contentLink, string language, object values)
{
var routeValues = values as RouteValueDictionary ?? new RouteValueDictionary(values);
return ContentUrl(urlHelper, contentLink, language, routeValues);
}
public static string ContentUrl(this IUrlHelper urlHelper, ContentReference contentLink, string language, RouteValueDictionary routeValues)
{
if (ContentReference.IsNullOrEmpty(contentLink) || string.IsNullOrEmpty(language))
{
var feature = urlHelper.ActionContext.HttpContext.Features.Get<IContentRouteFeature>();
if (feature != null)
{
var routedContentData = feature.RoutedContentData;
language = string.IsNullOrEmpty(language) ? routedContentData.RouteLanguage : language;
contentLink = ContentReference.IsNullOrEmpty(contentLink) ? routedContentData.Content?.ContentLink : contentLink;
}
}
if (!ContentReference.IsNullOrEmpty(contentLink))
{
var action = routeValues["action"] as string;
routeValues.Remove("controller");
routeValues.Remove("action");
routeValues.RemoveEmptyValues();
var arguments = new VirtualPathArguments {
Action = action,
RouteValues = routeValues
};
var resolver = urlHelper.ActionContext.HttpContext.RequestServices.GetRequiredService<UrlResolver>();
return resolver.GetUrl(contentLink, language, arguments);
}
return string.Empty;
}
}
public static class IDictionaryExtensions
{
public static void RemoveEmptyValues<TKey, TValue>(this IDictionary<TKey, TValue> source)
{
var keys = source.Keys.ToList();
foreach (var key in keys) {
var value = source[key] as object;
if (value == null ||
(value is string s && string.IsNullOrEmpty(s)) ||
(value is StringValues sv && StringValues.IsNullOrEmpty(sv)))
{
source.Remove(key);
}
}
}
}
Hi!
We have just upgraded to CMS 12 and we have this in our view:
In CMS 11 I get this:
which is correct, but in CMS 12 I get this:
What am I missing?
Thanks!
/Kristoffer