Blazor. How to capture mouse in onmousemove event? - mouseevent

How to capture mouse in onmousemove event in Blazor like UIElement.CaptureMouse() in WPF?

... in Blazor like UIElement.CaptureMouse()
The closest match would be Element.setPointerCapture()
In order to use it you will need to get an ElementReference in Blazor with #ref and a JS method to invoke setPointerCapture on it. You need to pass a pointerId that you get from PointerEventArgs.
Do not use the Mouse* related events/methods, they are more or less deprecated in JS.
So you can start with something like:
<div #ref="myTarget" #onpointerdown="StartCapture"> ... </div>
#code{
ElementReference myTarget;
async Task StartCapture(PointerEventArgs args)
{
await JSRuntime.InvokeVoidAsync("myJsFunctions.capturePointer",
myTarget, args.PointerId);
}
}

Related

In Razor Pages, how can I preserve the state of a checkbox which is not part of a form?

I have page that has checkbox that is used to expand/collapse some part of the page. This is client-side logic done in JavaScript.
I want to preserve the state of this checkbox for this particular page. Can Razor Pages do this automatically?
I tried by adding bool property with [BindProperty(SupportsGet = true)] in PageModel but it doesn't work - when I check the checkbox and reload (HTTP GET) the checkbox is always false.
Guessing that this toggle feature is user-specific, and that you want to persist their choice over a number of HTTP requests, I recommend setting a cookie using client-side code, which is user- or more accurately device-specific and can persist for as long as you need, and can be read on the server too.
https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie
https://www.learnrazorpages.com/razor-pages/cookies
I want to preserve the state of this checkbox for this particular page. Can Razor Pages do this automatically?
No, since you don't send it to the backend it will not show it.
As Mike said, it better we could store it inside the client cookie or storage.
More details, you could refer to below codes:
<p>
<input type="checkbox" id="cbox1" checked="checked">
<label >This is the first checkbox</label>
</p>
#section scripts{
<script>
$(function(){
var status = getValue();
if(status === "true"){
$("#cbox1").attr("checked","checked");
}else{
$("#cbox1").removeAttr("checked");
}
})
$("#cbox1").click(function(){
var re = $("#cbox1").is(":checked")
alert(re);
createItem(re);
});
function createItem(value) {
localStorage.setItem('status', value);
}
function getValue() {
return localStorage.getItem('status');
} // Gets the value of 'nameOfItem' and returns it
console.log(getValue()); //'value';
</script>
}

NavLink updating URL but does not reloading page in Blazor

I have a ProjectBase.razor page that is used to create, view & edit projects. The following routes all take you to this page:
/project/view/{projNum}
/project/create/
/project/edit/{projNum}
I also have a Navlink in my navigation menu that allows you to create a new project:
<NavLink class="nav-link" href="/Project/Create" Match="NavLinkMatch.All" >
<span aria-hidden="true">New Project</span>
</NavLink>
If I click on that link while on the view/edit features of the same page, the URL changes to "/Project/Create," but the page itself doesn't refresh or reload. Is there a way to force this through the NavLink? Or do I need to add an OnClick function to do this?
Create and use the OnParametersSetAsync task in your code block for the page. This event will fire when parameters change.
#code
protected override async Task OnParametersSetAsync()
{
// This event will fire when the parameters change
// Put your code here.
}
Yes, using something like Microsoft.AspNetCore.Components.NavigationManager and its NavigateTo function with forceLoad set to true will accomplish what you're looking for.
Of course yes, this will require you to set up an onclick function, but this is the way I ended up accomplishing something similar for a site-wide search page which never technically had its URL change outside of the query string search value I was passing it.
That being said, there may be a decent way of doing it with only NavLinks. I'll update my answer when I'm not on mobile.
In my component I had already overridden OnInitializedAsync in order to make an API call to get my data.
My solution looks like this:
protected override async Task OnInitializedAsync()
{
// Make your API call or whatever else you use to initialize your component here
}
protected override async Task OnParametersSetAsync()
{
await OnInitializedAsync();
}
I had same problem. Solution I have is...
Create new page
#page "/project/create/"
<ProjectBase></ProjectBase>
That's it! remove #page directive for(/project/create/) from ProjectBase page
Everything will work as expected... now do it for all pages you have.
In your case you have to make below changes as mention by Rod Weir, I am just extending the answer.
/project/view/{projNum}
/project/create/
/project/edit/{projNum}
For above query parameter you have to define [Parameter] in your code.
[Parameter]
public string projNum {get;set;}
Then add method
protected override async Task OnParametersSetAsync()
{
var projectDetail = await getProjectDetails(projNum); // ProgNum will change as it get changes in url, you don't have to do anything extra here.
}
Force page to reload will land you in some other problems, it will get the correct result but the page behavior will change. There are other components on the page like header/left Nav/ etc these will not changes if they are dynamic. It will force you to make changes and hanlde force reload in all the components. Also user experience is affected.
Hope this help.
That is by design.The page itself doesn't refresh or reload because the <NavLink> does not send request to the server (F12 to check) and it redirect to the same page on the client, so nothing updates.
If you enter those URLs in the browser,they will send requests and then refresh page.
A workaround is that you could display different content based on the current route.
#page "/project/view/{projNum}"
#page "/project/create/"
#page "/project/edit/{projNum}"
#using Models
<h3>ProjectBase</h3>
#if (projNum == null)
{
<EditForm Model="#createModel" OnValidSubmit="#HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<InputText id="name" #bind-Value="createModel.Name" />
<button type="submit">Create</button>
</EditForm>
}
else
{
<EditForm Model="#exampleModel" OnValidSubmit="#HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<InputText id="name" #bind-Value="exampleModel.Name" />
<button type="submit">Submit</button>
</EditForm>
}
#code {
[Parameter]
public string projNum { get; set; }
private ExampleModel createModel = new ExampleModel();
private ExampleModel exampleModel = new ExampleModel();
protected override void OnInitialized()
{
exampleModel.Name = projNum;
}
private void HandleValidSubmit()
{
//your logic
Console.WriteLine("OnValidSubmit");
}
}

In a view component invoked as a tag helper, how can we access the inner HTML?

In tag helpers, we can access the tags inner HTML.
<!-- Index.cshtml -->
<my-first>
This is the original Inner HTML from the *.cshtml file.
</my-first>
// MyFirstTagHelper.cs > ProcessAsync
var childContent = await output.GetChildContentAsync();
var innerHtml = childContent.GetContent();
If we invoke a view component as a tag helper, how can we access the inner HTML?
<vc:my-first>
This is the original Inner HTML from the *.cshtml file.
</vc:my-first>
// MyFirstViewComponent.cs > InvokeAsync()
var innerHtml = DoMagic();
A few further thoughts:
I appreciate that we can pass arbitrary content to a view component via HTML attributes. That can become impractical, though, in the case of very large amounts of text, in which case inner HTML would be more readable and have better tooling support.
Some might also say, "Why not just use a tag helper, then?" Well, we want to associate a view with the tag helper, and tag helpers do not support views; instead, tag helpers require us to build all the HTML programmatically.
So we're stuck in a bit of bind. On the one hand, we want a tag helper, but they don't support views; on the other hand, we can use a view component as a tag helper, but view components might not let us access the inner HTML.
Sorry, but the answer is "Just use a tag helper"
See the following issue: https://github.com/aspnet/Mvc/issues/5465
Being able to invoke a ViewComponent from a tag helper is just a different way to call it instead of using #Component.Invoke().
I can see where this is misleading, and I do recall seeing in the ASP.NET Core 2.1 tutorials a statement to the effect of "View Components are like Tag Helpers, but more powerful."
Finally, a way to have our cake and eat it too! Allow a tag-helper to use a Razor view as its source of HTML, yet still wrap markup when used in a Razor page.
Use a tag-helper to get the inner HTML as a string. Then directly operate the Razor view engine to render a partial view to a string. Finally, use string replacement to place the inner HTML string into the right place in the partial view string.
The key is to use the high-quality StackOverflow answers available on rendering a Razor view as a string. See the IRazorViewToStringRenderer service here (it says ASP.NET Core 3.1 but worked for me in 2.2), or elsewhere as Mvc.RenderViewToString.
The tag-helper:
// RazorViewTagHelper.cs
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace GetSafetyCone.TagHelpers
{
public class RazorViewTagHelper : TagHelper
{
private IRazorViewToStringRenderer RazorViewToStringRenderer { get; }
public ViewName { get; set; }
public RazorViewedTagHelperBase(
IRazorViewToStringRenderer razorViewToStringRenderer)
{
this.RazorViewToStringRenderer = razorViewToStringRenderer;
}
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
var childContent = await output.GetChildContentAsync();
var childContentString = childContent.GetContent();
var viewHtmlTemplateString = await this.RazorViewToStringRenderer.Render<object>(this.ViewName, null);
var viewHtmlString = viewHtmlTemplateString.Replace("BODY_GOES_HERE", childContentString);
output.Content.SetHtmlContent(viewHtmlString); // Set the content.
}
}
}
The partial view you want to use as the source of HTML for the tag-helper:
// ViewXYZ.cshtml
#*
ViewXYZ to be rendered to string.
*#
// No model specified, so ok model was a null object.
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="max-w-3xl mx-auto">
BODY_GOES_HERE
</div>
</div>
And here's the tag-helper in use:
// YourPage.cshtml
<razor-view view-name="ViewXYZ">
<p>You should see this content wrapped in the ViewXYZ divs.</p>
</razor-view>
If you want, you can simplify the string replacement and use the childContent TagHelperContent directly as the model of the view:
// RazorViewTagHelper.cs
...
var childContent = await output.GetChildContentAsync();
var viewHtmlString = await this.RazorViewToStringRenderer.Render("viewName", childContent);
...
// ViewXYZ.cshtml
...
#model Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContent;
...
<div class="max-w-3xl mx-auto">
#(this.Model)
</div>
...

unobtrusive validation not working with dynamic content

I'm having problems trying to get the unobtrusive jquery validation to work with a partial view that is loaded dynamically through an AJAX call.
I've been spending days trying to get this code to work with no luck.
Here's the View:
#model MvcApplication2.Models.test
#using (Html.BeginForm())
{
#Html.ValidationSummary(true);
<div id="res"></div>
<input id="submit" type="submit" value="submit" />
}
The Partial View:
#model MvcApplication2.Models.test
#Html.TextAreaFor(m => m.MyProperty);
#Html.ValidationMessageFor(m => m.MyProperty);
<script type="text/javascript" >
$.validator.unobtrusive.parse(document);
</script>
The Model:
public class test
{
[Required(ErrorMessage= "required field")]
public int MyProperty { get; set; }
}
The Controller:
public ActionResult GetView()
{
return PartialView("Test");
}
and finally, the javascript:
$(doument).ready(function () {
$.ajax({
url: '/test/getview',
success: function (res) {
$("#res").html(res);
$.validator.unobtrusive.parse($("#res"));
}
});
$("#submit").click(function () {
if ($("form").valid()) {
alert('valid');
return true;
} else {
alert('not valid');
return false;
}
});
The validation does not work. Even if I don't fill any information in the texbox, the submit event shows the alert ('valid').
However, if instead of loading dynamically the view, I use #Html.Partial("test", Model) to render the partial View in the main View (and I don't do the AJAX call), then the validation works just fine.
This is probably because if I load the content dynamically, the controls don't exist in the DOM yet. But I do a call to $.validator.unobtrusive.parse($("#res")); which should be enough to let the validator about the newly loaded controls...
Can anyone help ?
If you try to parse a form that is already parsed it won't update
What you could do when you add dynamic element to the form is either
You could remove the form's validation and re validate it like this:
var form = $(formSelector)
.removeData("validator") /* added by the raw jquery.validate plugin */
.removeData("unobtrusiveValidation"); /* added by the jquery unobtrusive plugin*/
$.validator.unobtrusive.parse(form);
Access the form's unobtrusiveValidation data using the jquery data method:
$(form).data('unobtrusiveValidation')
then access the rules collection and add the new elements attributes (which is somewhat complicated).
You can also check out this article on Applying unobtrusive jquery validation to dynamic content in ASP.Net MVC for a plugin used for adding dynamic elements to a form. This plugin uses the 2nd solution.
As an addition to Nadeem Khedr's answer....
If you've loaded a form in to your DOM dynamically and then call
jQuery.validator.unobtrusive.parse(form);
(with the extra bits mentioned) and are then going to submit that form using ajax remember to call
$(form).valid()
which returns true or false (and runs the actual validation) before you submit your form.
Surprisingly, when I viewed this question, the official ASP.NET docs still did not have any info about the unobtrusive parse() method or how to use it with dynamic content. I took the liberty of creating an issue at the docs repo (referencing #Nadeem's original answer) and submitting a pull request to fix it. This information is now visible in the client side validation section of the model validation topic.
add this to your _Layout.cshtml
$(function () {
//parsing the unobtrusive attributes when we get content via ajax
$(document).ajaxComplete(function () {
$.validator.unobtrusive.parse(document);
});
});
test this:
if ($.validator.unobtrusive != undefined) {
$.validator.unobtrusive.parse("form");
}
I got struck in the same problem and nothing worked except this:
$(document).ready(function () {
rebindvalidators();
});
function rebindvalidators() {
var $form = $("#id-of-form");
$form.unbind();
$form.data("validator", null);
$.validator.unobtrusive.parse($form);
$form.validate($form.data("unobtrusiveValidation").options);
}
and add
// Check if the form is valid
var $form = $(this.form);
if (!$form.valid())
return;
where you are trying to save the form.
I was saving the form through Ajax call.
Hope this will help someone.
just copy this code again in end of modal code
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
;)

Is there a way to disconnect an event added by dojoAttachEvent

I have a dojo widget which uses a a custom-library code having a link like this in its template.
Go Back
I need to find a way to disconnect this event from my widget. The only way i know how an event can be disconnected is, using a
dojo.disconnect(handle)
I could use this if I had the event connected using dojo,connect() which returns me the handle.
However with dojoAttachEvent i don't have the event handle hence no way to disconnect it.
Note :
Changing this html is not an option for me, since this an external library i am using.
Also, I am not looking for a solution to disconnect all events.
CODE:
otherWidget.js:
dojo.provide("otherWidget");
dojo.declare("otherWidget", [], {
templateString : dojo.cache("otherWidget","templates/otherWidget.html"),
_goBack: function(){
this.destroyWidgetAndRedirect();
},
destroyWidgetAndRedirect: function(){
//Code to destory and redirect.
},
});
otherWidget.html:
<div>
Go Back
<!-- Other Widget related code -->
...
</div>
myWidget.js:
dojo.provide("myWidget");
dojo.require("otherWidget");
dojo.declare("myWidget", [], {
templateString : dojo.cache("myWidget","templates/myWidget.html"),
this.otherWidget = new otherWidget({}, dojo.byId('otherWidgetContainer'));
});
myWidget.html:
<div>
<div id="otherWidgetContainer"></div>
<!-- My Widget related code -->
...
</div>
Any thoughts..
Thanks.
Extension points can be used directly on your html, or in javascript. Suppose the widget you are using is called 'my.custom.dojowidget', and that it has an onClick extension point. I will show here the declarative way, in your html. Try this :
<div data-dojo-type="my.custom.widget">
<script type="dojo/method" data-dojo-event="onClick" data-dojo-args"evt">
dojo.stopEvent(evt);
console.debug("did this work ?");
</script>
</div>
Now this depends on the existence of the extension point... if you can't still do what you want, please post the relevant parts of your widget's code.
So... based on the sample code you posted in your edit, I think you should do the following :
<div data-dojo-type="otherWidget">
<script type="dojo/method" data-dojo-event="destroyWidgetAndRedirect" data-dojo-args="evt">
dojo.stopEvent(evt);
// do whatever custom code you want here...
</script>
</div>