Blazor server component isn't rerendered - blazor-server-side

I have a Blazor Server (.NETv5) application with a search page.
On this page I have a form to search by name.
On the form submit event I call the search method of my child component.
That component is doing the actual search. This is working fine.
Because the search might take a few seconds I want to show a spinner when the search starts and hide it when the search is done.
Also when I do a second search I want to hide the previous search results.
Hiding the spinner and showing the search results is working, but showing the spinner before the search doesn't work. The variable is set correctly but the page is not rerendered (I think).
My page:
<div class="container pt-2 mb-3">
<RadzenTemplateForm Data="#searchTerms" Submit="#((SearchTerms args) => { Submit(args); })">
<div class="row">
<div class="mb-2 col-6 pl-0">
<RadzenLabel Text="Name" />
<RadzenTextBox class="col-12" Name="Name" #bind-Value="searchTerms.Name"/>
</div>
</div>
<div class="row">
<div class="col-md-12 mt-3">
<RadzenButton ButtonType="ButtonType.Submit" Icon="search" Text="Search" Disabled="#isSearching" />
<RadzenButton ButtonStyle="ButtonStyle.Light" Icon="cancel" Text="Cancel" Click="#Cancel" class="ml-2"/>
</div>
</div>
</RadzenTemplateForm>
</div>
<SearchResultsComponent #ref="searchResultsComponent" />
protected SearchTerms searchTerms = new();
protected SearchResultsComponent searchResultsComponent;
protected bool isSearching;
protected void Submit(SearchTerms args)
{
if (string.IsNullOrEmpty(args.Name)) return;
// Disable submit button ==> NOT WORKING:
isSearching = true;
// Call search method on child component
searchResultsComponent.Search(args);
// Enable submit button again:
isSearching = false;
}
protected void Cancel()
{
// Reset form:
searchTerms = new SearchTerms();
}
My child component:
<div class="container">
#if (isSearching)
{
<div class="spinner-border text-primary mr-2" role="status">
<span class="sr-only">Searching...</span>
</div>
<b>Searching ...</b>
}
#if (noResults)
{
<div class="alert alert-warning" role="alert">
No results.
</div>
}
#if (getSearchResults != null && getSearchResults.Any())
{
<RadzenHeading Size="H2" Text=#($"Results({getSearchResults.Count})")></RadzenHeading>
<div class="row">
#foreach (var searchResult in getSearchResults)
{
<RadzenCard>
<b>#searchResult.Name</b>
</RadzenCard>
}
</div>
}
</div>
private IList<MultiShardSearchResultsWerknemer> _searchResults;
private bool _isSearching = true;
private bool _noResults;
protected bool noResults
{
get => _noResults;
set
{
if (Equals(_noResults, value)) return;
_noResults = value;
InvokeAsync(() => StateHasChanged());
}
}
protected bool isSearching
{
get => _isSearching;
set
{
if (Equals(_isSearching, value)) return;
_isSearching = value;
InvokeAsync(() => StateHasChanged());
}
}
protected IList<MultiShardSearchResultsWerknemer> getSearchResults
{
get => _searchResults;
set
{
if (Equals(_searchResults, value)) return;
_searchResults = value;
InvokeAsync(() => StateHasChanged());
}
}
public void Search(SearchTerms args)
{
Helpers.ConsoleLog(args);
if (string.IsNullOrEmpty(args.Name)) return;
// Reset ==> NOT WORKING:
isSearching = true;
noResults = false;
getSearchResults = null;
InvokeAsync(() => StateHasChanged());
getSearchResults = ShardingService.SearchForAllEmployees(args.Name, null).GetAwaiter().GetResult();
Helpers.ConsoleLog("Found results: " + getSearchResults.Count);
isSearching = false;
noResults = !getSearchResults.Any();
}
For debugging purposes, I've set _isSearching = true which shows me the spinner. The spinner is also hidden when the search is done, so that is working. But I can't get the spinner to show when I start searching.
I've tried all options I could find, without success.
I must be missing something. Please advice.

Have a look at your search handel method
protected void Submit(SearchTerms args)
{
if (string.IsNullOrEmpty(args.Name)) return;
// Disable submit button ==> NOT WORKING:
isSearching = true;
// Call search method on child component
searchResultsComponent.Search(args);
// Enable submit button again:
isSearching = false;
}
Keep in mind, that rendering will occur once the method has finished. So, before the call isSearching is false and after it is also false. That's why you don't see the spinner.
Blazor offers a method to kick off a new render cycle: StateHasChanged().
So, you could modify your submit method like
protected void Submit(SearchTerms args)
{
if (string.IsNullOrEmpty(args.Name)) return;
isSearching = true;
StateHasChanged()
// Call search method on child component
searchResultsComponent.Search(args);
isSearching = false;
StateHasChanged()
}
So, you click the search/submit button on this method is executed.
Or if you like, create a property instead
#code
{
private Boolean isSearching = false;
public Boolean IsSearching
{
get => isSearching;
private set
{
isSearching = value;
StateHasChanged();
}
}
protected void Submit(SearchTerms args)
{
if (string.IsNullOrEmpty(args.Name)) return;
IsSearching = true;
// Call search method on child component
searchResultsComponent.Search(args);
IsSearching = false;
}
}
I haven't tested it but faced a similar problems once.

Related

My Bootstap modal form is covering the entire screen

I have a modal form in my ASP.NET Core 3.1 project. The problem is my modal form unexpectedly is covering entire of screen and I don't know why.
My _Modal.cshtml:
#model MyProject.CommonLayer.PublicClass.BootstrapModel
<div aria-hidden="true" aria-labelledby="#Model.AreaLabeledId" role="dialog" tabindex="-1" id="#Model.ID" class="modal fade">
<div class="modal-dialog #Model.ModalSizeClass">
<div class="modal-content" style="border-radius:6px;">
</div>
</div>
</div>
My modal.js file:
(function ($) {
function Modal() {
var $this = this;
function initilizeModel() {
$("#modal-action").on('loaded.bs.modal', function (e) {
}).on('hidden.bs.modal', function (e) {
$(this).removeData('bs.modal');
});
}
$this.init = function () {
initilizeModel();
}
}
$(function () {
var self = new Modal();
self.init();
})
}(jQuery))
and my BooststrapModal.cs file:
public class BootstrapModel
{
public string ID { get; set; }
public string AreaLabeledId { get; set; }
public ModalSize Size { get; set; }
public string Message { get; set; }
public string ModalSizeClass
{
get
{
switch (this.Size)
{
case ModalSize.Small:
return "modal-sm";
case ModalSize.Large:
return "modal-lg";
case ModalSize.Medium:
default:
return "";
}
}
}
public enum ModalSize
{
Small,
Large,
Medium
}
}
In the end of my index.cs file, for modal I put:
#Html.Partial("_Modal", new BootstrapModel { ID = "modal-action", Size = BootstrapModel.ModalSize.Medium })
#section AdminScripts {
<script src="~/js/modal/modal.js"></script>
}
Finally when button is clicked, the modal form covers my entire screen.
Please help. Thanks in advance.

Is there a way for Code Block feature to keep line breaks in CKEditor5 with ASP.Net Core?

I am making a bulletin board system using CKEditor. Most of the features work just fine, but when editing an existing post, the all line breaks in the text are removed from the code block.
Image of create a post
Image of edit a post
Image of part of the response source
I googled as much as possible to solve this problem, but the methods I found were to no avail, so I removed it from the code again.
It seems that line breaks are removed while processing the source internally in CKEditor5, is there any way?
Replace all line breaks with <br /> tags.
Add /\r|\n/g to protectedSource
The following is the view file for that feature.
#model BBSArticleWriteView
#{
// Action name of the current view
var thisActionString = #ViewContext.RouteData.Values["action"].ToString();
if (Model.ArticleId == null)
ViewData["Title"] = "Writing";
else
ViewData["Title"] = "Editing";
}
<p class="page-header">#ViewData["Title"]</p>
<form asp-action="#thisActionString" id="editor-form">
<input asp-for="ArticleId" value="#Model.ArticleId" hidden />
<div>
<input asp-for="Title" required placeholder="Please enter a title." class="form-control w-100 mb-2" />
</div>
<div>
<textarea name="Contents" id="editor">
#Html.Raw(Model.Contents)
</textarea>
</div>
<div>
<input class="btn btn-sm btn-primary" type="submit" value="Save" onsubmit="Editor.submit()" />
<button class="btn btn-sm btn-primary" type="button" href="##" onclick="history.back()">Back</button>
</div>
</form>
<style>
.ck-editor__editable_inline {
min-height: 400px;
}
</style>
#section Scripts {
<script src="~/lib/ckeditor5/ckeditor.js" asp-append-version="true"></script>
<script>
class Editor{
static submit() {
return true;
}
}
ClassicEditor
.create(document.querySelector('#editor'),
{
simpleUpload:{
uploadUrl: "#Url.Action(nameof(CreatorFront.Controllers.FileController.Upload), "File")",
withCredentials: true
},
protectedSource:[
/\r|\n/g
]
})
.catch(error => {
console.error(error);
});
</script>
}
And here is the controller action that puts data into the view model.
[HttpGet]
public async Task<IActionResult> BBSEdit(int id)
{
var user = await _userManager.GetUserAsync(HttpContext.User);
if(user == null)
{
return RedirectToAction("Index", "Home");
}
var article = _cContext.BBSArticle.First(a => a.ArticleId == id);
if(article == null)
{
return RedirectToAction(nameof(BBSList));
}
if(user.Id != article.UserId)
{
return RedirectToAction(nameof(BBSList));
}
var model = new BBSArticleWriteView();
CopyProperties(model, article);
return View(nameof(BBSWrite), model);
}
The following is a function that puts content data in DB.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> BBSWrite(BBSArticleWriteView article)
{
if(ModelState.IsValid)
{
var user = await _userManager.GetUserAsync(HttpContext.User);
if(user == null)
{
RedirectToAction("Index", "Home");
}
// XSS attacks prevent
article.Contents = _htmlSanitizer.Sanitize(article.Contents);
var currentDateTime = DateTime.Now;
CreatorLib.Models.BBS.BBSArticle data = new CreatorLib.Models.BBS.BBSArticle()
{
ArticleId = _cContext.BBSArticle.Max(a => a.ArticleId) + 1,
MainCategory = article.MainCategory,
SubCategory = article.SubCategory,
UserId = user.Id,
Title = article.Title,
Contents = article.Contents,
Status = CreatorLib.Models.BBS.ArticleStatus.A,
IpAddress = HttpContext.Connection.RemoteIpAddress.ToString(),
RegisteredTime = currentDateTime,
LastUpdatedTime = currentDateTime,
HasMedia = article.HasMedia
};
_cContext.BBSArticle.Add(data);
await _cContext.SaveChangesAsync();
return RedirectToAction(nameof(BBSList));
}
return View(article);
}
Here, it is confirmed that HtmlSanitizer has absolutely no impact on this issue.
In DB, line breaks are fully preserved.

Blazor Server - How to disable a control inside component?

I try to show and disable a button by adding "disabled" class in a component (pre-hidden) but failed - it's not working. Where I did wrong ?
Component Button :
<div class="form-group">
<button class="btn btn-primary #disabled">Disable Me</button>
</div>
#code {
string _password;
string disabled = "";
public void Disable()
{
disabled = "disabled";
}
}
Index.razor :
#page "/"
<h1>How to disable button in component ?</h1>
<button class="btn btn-primary" #onclick="ShowAndDisableButton">Show and disable button</button>
#if (show)
{
<ButtonComponent #ref="button"/>
}
#code
{
ButtonComponent button = new();
bool show = false;
void ShowAndDisableButton()
{
show = true;
button.Disable();
}
}
UPDATED : if I change the ShowAndDisableButton code to
async Task ShowAndDisableButton()
{
show = true;
await Task.Delay(TimeSpan.FromMilliseconds(10)); // add this, wait for a while
button.Disable();
}
and change button code in index.razor to
<button class="btn btn-primary" #onclick="()=>ShowAndDisableButton()">Show and disable button</button>
it works. but I don't know why and don't want to use such way, are there any proper way?
The problem is that button.Disable(); does not cause the normal rerendering.
And it is an overcomoplicated way of doing things.
In the page:
#if (show)
{
<ButtonComponent Disabled="!show" />
}
#code
{
//ButtonComponent button = new();
bool show = false;
void ShowAndDisableButton()
{
show = true;
//button.Disable();
}
}
and the button itself:
<div class="form-group">
<button class="btn btn-primary" disabled="Disabled">Disable Me</button>
</div>
#code {
string _password;
// string disabled = "";
[Parameter] public bool Disabled {get; set; }
}
But you won't be able to use this button.
Use disabled="#disabledState"
Where disabledState is a boolean

Popup containt become blank at the time of moving the popup positions by selecting popup header

I am opening the view into popup control by using ContentUrl property of popup.
But when I try to move the positions of popup by selecting popup header. popup containt become blank still I am moving or selected the popup header. Same issue is present for in devexpress demo which was provided online.
I refered the following demo of devexpress popup controal
http://demos.devexpress.com/MVCxDockAndPopupsDemos/PopupControl/ContentUrl
I have written the following code
HomeController
public class HomeController : Controller
{
public ActionResult SendProduct(string rowId)
{
Product objProduct = new Product();
return View(objProduct);
}
[HttpPost]
public ActionResult SendProduct(Product objProduct)
{
return View(objProduct);
}
}
Product model
public class Product
{
public int ProductId { get; set; }
[Required]
public string Name { get; set; }
}
Index.chtml
#{
ViewBag.Title = "Home Page";
}
#using DevExpress.Web.Mvc.UI
#using DevExpress.Web.ASPxGridView
#using UI.Infrastructure.Resources;
<script type="text/javascript">
function OnBeginCallback(s, e) {
e.customArgs["rowId"] = 123;
}
function Click() {
pcSendProduct.PerformCallback();
if (!pcSendProduct.IsVisible())
pcSendProduct.Show();
}
</script>
Enumalate menu click
<div>
#Html.DevExpress().Button(settings =>
{
settings.Name = "btnSend";
settings.Width = 80;
settings.Text = "Find";
settings.UseSubmitBehavior = false;
settings.ClientSideEvents.Click = string.Format("function(s, e) {{ Click(); }}");
}).GetHtml()
</div>
#Html.DevExpress().PopupControl(
settings =>
{
settings.Name = "pcSendProduct";
settings.Width = 1050;
settings.Height = 550;
settings.HeaderText = "Plan Customer Interaction";
settings.CloseAction = DevExpress.Web.ASPxClasses.CloseAction.CloseButton;
settings.Styles.Header.HorizontalAlign = System.Web.UI.WebControls.HorizontalAlign.Center;
settings.Styles.Header.VerticalAlign = System.Web.UI.WebControls.VerticalAlign.Middle;
settings.Styles.Header.Font.Size = 10;
settings.Modal = true;
settings.ShowHeader = true;
settings.ShowCloseButton = true;
settings.CloseAction = DevExpress.Web.ASPxClasses.CloseAction.CloseButton;
settings.Left = 1245;
settings.Top = 300;
settings.Styles.ModalBackground.BackColor = System.Drawing.Color.Transparent;
//settings.ContentUrl = Url.Action("SendProduct", "Home");
settings.ShowLoadingPanel = true;
settings.ClientSideEvents.BeginCallback = "OnBeginCallback";
}).GetHtml()
SendProduct.cshtml
#model Demo.Models.Product
#{
ViewBag.Title = "SendProduct";
}
<h2>SendProduct</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Product</legend>
#Html.HiddenFor(model => model.ProductId)
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
In above code when i click on button popup get open on brouser properly. but when I select popup header for changing the positions position or any reason popup containt will get dissapered or popup will show blank containt. When I deselect popup header popup containt view display on popup
Please please give me solution so that w hen user select popup header then popup containt will not become blank. Popup must show the view .
Here devexress team six year ago gave an explanation that it is impossible when you use ContentUrl property because the control render it in the iframe.
You can write view content in the ViewContext but your controller should return PartialView.
public class HomeController : Controller
{
public ActionResult SendProduct(string rowId)
{
Product objProduct = new Product();
return PartialView(objProduct);
}
[HttpPost]
public ActionResult SendProduct(Product objProduct)
{
return PartialView(objProduct);
}
}
Popup settings
settings.SetContent(() =>
{
ViewContext.Writer.Write((Html.Action("SendProduct", "Home").ToHtmlString()));
});
//settings.ContentUrl = Url.Action("SendProduct", "Home");

Durandal 2.0: transition not triggered while staying on same view with new data

Here's my shell:
<div>
<div id="content" class="container-fluid page-host main">
<!--ko router: { transition:'entrance', alwaysTriggerAttach: 'true', cacheViews: 'true' }--><!--/ko-->
</div>
</div>
I always navigate to views using:
router.navigate(route);
Now, when being on a certain view (item), and calling the navigate function for another id (/#item/id), the data changes correctly, but the transition does not happen.
Any advice?
Thanks
Nicolas
Should transition be triggered is defined in this function of durandal/js/composition.js:
function shouldTransition(context) {
if (system.isString(context.transition)) {
if (context.activeView) {
if (context.activeView == context.child) {
return false;
}
if (!context.child) {
return true;
}
if (context.skipTransitionOnSameViewId) {
var currentViewId = context.activeView.getAttribute('data-view');
var newViewId = context.child.getAttribute('data-view');
return currentViewId != newViewId;
}
}
return true;
}
return false;
If you want to trigger transition then navigating within the same view, you can comment this if statement:
if (context.activeView == context.child) {
return false;