What is the difference between OnClick and #onclick in blazor components - onclick

I found out something curious and I am wondering if anyone knows the answer:
First of all this is not this question:
Different method calls in Blazor
That question refers to HTML elements. I am talking about Components.
So I have my own component named MyButton; and it has OnClick Parameter specified:
MyButton.razor
<button #onclick="OnClick">Do Something</button>
#code {
[Parameter]
public EventCallback<MouseEventArgs> OnClick { get; set; }
}
When I use MyButton I can use either the name exactly, i.e.
<MyButton OnClick="SomeMethod" />
But this is also working:
<MyButton #onclick="SomeMethod" />
When I remove the whole #code block from the MyButton.razor they both give me the exact same error message:
<Mybutton OnClick="MyMethod" />
Object of type 'MyButton' does not have a property matching the name 'OnClick'.
<Mybutton #onclick="MyMethod" />
Object of type 'MyButton' does not have a property matching the name 'onclick'.
The only difference is in the caps; "OnClick" vs "onclick"... that is logical. So it seems #onclick is the same as OnClick... but are they?
Is this simply an overload of some sorts?

#onclick is the native HTML click event and OnClick is the event parameter you explicitly expose in your MyButton component.
So in this case you should use
<MyButton OnClick="SomeMethod" />
I tried to reproduce the usage with #onclick but it didn't work in my case.
For science, you could try to add a text <p>Like this</p> to your MyButton component and see if the #onclick event still works and if it only fires if you click the button or also if you click the text.
Also, see Microsoft Docs for detailed information.

Do you use MudBlazor ? Because I think OnClick is part of the MudBlazor Button API and #onclick is part of the ASP.NET Core Blazor event handling features.

Related

Angular2 two-way data binding

I know Angular2 doesn't have two-way data binding but is there a way to mimick the two-way data binding behavior from Angular1.x?
Note - scroll down the answer for ng-model binding
You could actually do that, just that you need to invoke internal changelistener tick (similar to digest) to update binding in the zone, You can just add a (keyup) event for that. Similarly you could use directive bindings as well with properties dictionary of component settings.
Example:-
<input #label (keyup)>
<!-- variable #label represented as the element itself and accessible as property on controller instance
You can even bind keyup to a function or another another function and pass value from the label property-->
Display as:
<p>{{label.value}}</P>
Parent Component has a textbox and a label.
import { Component, bootstrap} from '#angular/core';
import {Display} from 'display';
#Component({
selector: 'my-app',
template: `<p><b>Parent Component:</b><p><input #label (keyup) (change)="handleChange(label.value)">
<p>{{label.value}}</P> <display [text]="label"></display></p></p>`,
directives: [Display]
})
class MainComponent {
label: any;
constructor() {
}
handleChange(label){
this.label = label;
console.log(this.label);
}
}
Now displaying it in child component as well:
#Component({
selector: 'edit',
template: `<p><b>Child Component:</b></p>{{text.value}}`
})
export class Edit {
#Input() text:any;
}
Demo
Update - ng-model for 2-way binding
Though Angular2 is one-time bound by default, ngModel sugar has been introduced to achieve 2-way binding. With that you could do for instance:
<input ngControl="name" [(ngModel)]="name">
Here usage of square brackets ([..]) suggests the property binding and round brackets ((..)) for event binding. Basically when you use ng-model, you are enabling both the bindings ngModel is more of an event. Behind the scenes it creates an observable event(with EventEmitter) to track the value changes in the bound element and update the bound property respectively.
For example:-
Include formDirectives:
import {FORM_DIRECTIVES} from '#angular/common';
and with form
<form (ngSubmit)="onSubmit()" let-f="form">
<input ngControl="name" [(ngModel)]="name">
<button>Click me and check console</button>
</form>
without form
<input [(ngModel)]="name">
<button (click)="onSubmit()">Click me and check console</button>
not necessary anymore
include formDirectives dependency in view annotation.
#Component({
template: .....,
directives: [FORM_DIRECTIVES]
})
Demo
Also read the nice write up from Victor Savkin on Two-way binding in angular2 by creating the ng-model event and how it works.
You can now simply do this by using ngModel using the following syntax:
<input [(ngModel)]="myProp" />
The combination of the square and round brackets means "two-way binding".
Please see the plunk here
Yes there is two-way binding in angular2. See here: https://angular.io/docs/ts/latest/guide/template-syntax.html#!#ngModel
So, how to use it in custom components?
What I like to do is something this:
private currentSelectedItem: MachineItem;
#Output() selectedItemChange: EventEmitter<MachineItem> = new EventEmitter<MachineItem>();
#Input() set selectedItem(machineItem: MachineItem) {
this.currentSelectedItem = machineItem;
this.selectedItemChange.emit(machineItem);
}
get selectedItem(): MachineItem {
return this.currentSelectedItem;
}
And use it like
<admin-item-list [(selectedItem)]="selectedItem"></admin-item-list>
You can also emit the new value where it is actually changed. But I find it quite convenient to do that gloabaly in a setter method and don't have to bother e.g. when I bind it directly to my view.
You can do this by attaching to the events on the input field and updating the internal value as is done in this example:
http://plnkr.co/edit/lOFzuWtUMq1hCnrm9tGA?p=preview
Create a component that has an internal attribute that holds the label this.label and a callback changeLabel that expects an event object
#Component({
selector: 'app',
templateUrl: 'bound.html'
})
class App {
label: string;
constructor() {
this.label = 'default label'
}
changeLabel(event) {
this.label = event.target.value;
}
}
bootstrap(App);
The create your template and attach the callback to the appropriate event (you could attach it to the keypress event but then you might need a timeout. I attached it to the change event for simplicity (which means you might need to tab off the input to see the update).
<label for="myinput">{{label}}</label>
<input id="myinput" type="text"/>
<p></p>You can change the label above by typing something below</p>
<label for="labeltext">New Label Text</label>
<input type="text" id="labeltext" (change)="changeLabel($event)"/>
There is another way to trick Angular2 into two-way binding. Don't pass a property but an object into the component. If you pass an object via one-way binding all of its properties are in fact two-way bound.
It makes the component less versatile as it needs to know the object but in many cases it's still useful.
I have a component that looks like this:
import { Component, Input } from "#angular/core";
import { NgSwitch, NgSwitchWhen, NgSwitchDefault } from "#angular/common";
export class Movie
{
public Title: string;
public Rating: number;
public Seen: boolean;
}
#Component
({
selector: "hh-image-checkbox",
template: `
<div [ngSwitch]="movie.Seen">
<div *ngSwitchWhen="true">
<img src="/Content/res/CheckTrue.png" (click)="onClick()">
</div>
<div *ngSwitchDefault>
<img src="/Content/res/CheckFalse.png" (click)="onClick()">
</div>
</div>
`,
directives: [NgSwitch, NgSwitchWhen, NgSwitchDefault]
})
export class ImageCheckboxComponent
{
#Input() movie: Movie;
public onClick()
{
this.movie.Seen = !this.movie.Seen;
}
}
It is invoked like this:
<hh-image-checkbox [movie]="movie"></hh-image-checkbox>
The movie object itself is one-way bound but all of its properties can be used for two-way binding.
Here is a simple plunker which demonstrates one way, two way and event driven approaches in action according to Angular2 2.0.0-beta.17
http://plnkr.co/eXZMoU
Two-way Event and property
<input [(ngModel)]="name" />
One way property
<input [value]="name" />
Event driven
<input (input)="name=$event.target.value">
We can dig Angular docs for more
[UPDATE 1/26/2020]
Since Angular2 beta libs are removed from project CDN ! above plnkr link doesn't work anymore.
Use below new plnkr Angular 6+ page , I ported previous page to NPMJS, new angular edition and new plnkr!
http://next.plnkr.co/edit/4okdOSgw3SMvdktR?preview
From the Docs:
Two-way binding ( [(...)] )
You often want to both display a data property and update that property when the user makes changes.
On the element side that takes a combination of setting a specific element property and listening for an element change event.
Angular offers a special two-way data binding syntax for this purpose, [(x)]. The [(x)] syntax combines the brackets of property binding, [x], with the parentheses of event binding, (x).
[( )] = BANANA IN A BOX
Visualize a banana in a box to remember that the parentheses go inside the brackets.
For more information, see
Angular Developer Guide - Template Syntax - Two-way binding
Angular #angular/forms API Reference - ngModel Directive
Its simple, try this;
<input [(ngModel)]="property" placeholder="property Value"/>
<h1>{{property}}</h1>

asp.net mvc Redirect to action without triggering validation in current view?

I have a view which has multiple submit buttons. One button is for going back to another action which does some processing and load previous view.
However, in current view, it has some validations for model. So, when the button is click, it will show validation error.
Is there a way to get around this? or, we have to validate the data on server side?
Thanks
The built in client side validation in ASP.NET MVC uses the jquery validation plugin under the hood where you can skip validation while still using a submit-button with adding class="cancel" to that input:
<input type="submit" value="Do not validate" class="cancel" />
See this also in the documentation.
You can cancel validation by doing this:
$('#yourButton').click(function() {
$('input, textarea, select', '#yourForm').each(function () {
$(this).attr('data-val', 'false');
});
})
or
$("#yourButton").click(function() {
$('#yourForm').removeData('validator');
$('#yourForm').removeData('unobtrusiveValidation');
$('#yourForm').validate().cancelSubmit = true;
$('#yourForm').submit();
});
class ="cancel" is now replaced by formnovalidate attribute

Required Field conditional on button pressed

I may be having a blonde moment here.
I have a data entry form with the usual "Save" and "Cancel" buttons. In addition to these two I have another button "Approve". If the user clicks the "Approve" button I have an additional field (Approver) that must hold data. Is it possible to have a required field validator that is active on one button press but not another?
Yes This is possible :
You can Define multiple Validation group and decide witch group to validate depending on the clicked button, for that you should call javascript function onClientClient in order to validate the inputs :
See example bellow:
Triggering multiple validation groups with a single button
I think you will have to create a custom validator or just use javascript or jquery.
You can use the OnClientClick property of the buttons and add some javascript on there.
function CheckSave() {
if (/*text1 is filled*/) return true;
}
function CheckApprove() {
if (/*text1 is filled and text2 is filled*/) return true;
}
<asp:button id='btnSave' OnClientClick='return CheckSave()' OnClick='btnSave_Click' />
<asp:button id='btnApprove' OnClientClick='return CheckApprove()' OnClick='btnAprove_Click' />
you need the return in order for it to work

How to bind control property?

I am using the convention-based binding from Caliburn.Micro, but I have a small issue:
How do I set the property that my binding should bind to? If I create a control with x:Name="SomeProperty", how do I choose if the value of SomeProperty should be binded to the Value Property of my control, the OnClick Event of my control or something different, like the Content or the Tag property?
Example: I have this HyperlinkButton that I want to bind to a specific URL, and I want to bind the OnClick to an event handler in my ViewModel.
<HyperlinkButton x:Name="BookDetailsViewModel_InfoLink" Content="Read more" />
The Content property however is not filled with Read more but with the value of the URL. In this example, how do I:
Set the navigation URI to the value of the URL in my ViewModel property
Set the content to "Read more"
Specify an event handler in my ViewModel that will handle the click
Can anyone help me please?
You can customise the ConventionManager per element type in CM. The default out-of-box implemententation is applied to any element which doesn't have an explicit customisation
To add a new convention you just simply call ConventionManager.AddElementConvention
The method looks like this (from CM source)
/// <summary>
/// Adds an element convention.
/// </summary>
/// <typeparam name="T">The type of element.</typeparam>
/// <param name="bindableProperty">The default property for binding conventions.</param>
/// <param name="parameterProperty">The default property for action parameters.</param>
/// <param name="eventName">The default event to trigger actions.</param>
public static ElementConvention AddElementConvention<T>(DependencyProperty bindableProperty, string parameterProperty, string eventName)
{
return AddElementConvention(new ElementConvention
{
ElementType = typeof(T),
GetBindableProperty = element => bindableProperty,
ParameterProperty = parameterProperty,
CreateTrigger = () => new EventTrigger { EventName = eventName }
});
}
As you can see, it takes a few args - you need to pass the default property for bindings, actions, and triggers e.g.
ConventionManager.AddElementConvention<HyperlinkButton>(HyperlinkButton.NavigateUri, "NavigateUri", "Click");
(assuming the click event is called Click)
Since you aren't binding the Content property any more (because the convention is to now bind NavigateUri) you can just leave that as-is and it should remain 'Read more...'
So now you have a HyperlinkButton control which should bind by convention to the NavigateUri, and call the method which shares it's name when the Click event is triggered.
Edit:
I might make a clarification that I don't think you can bind to both a method and a property on the same VM since you can't have a method and a property that share the same name, but I'm sure CM would bubble the action message up the VM hierarchy if you didn't have the appropriate method on the VM... not tried it though. To bind the actions see my other edit below
Don't forget, you could always just use the explicit syntax for all of this!
<HyperlinkButton Content="Read more..." NavigationURI="{Binding SomeUri}" cal:Message.Attach="[Event Click] = [Action HyperlinkClicked($this.NavigateUri)" />
but it's probably better to go the convention route :)
Edit:
Might add how to get convention to grab the property value from the hyperlink -
<HyperlinkButton x:Name="SomeLink" Content="Read more..." cal:Message.Attach="HyperlinkClicked(SomeLink)" />
CM knows that since you set NavigateUri as the default action parameter, it should grab this and pass it to the method that you specified in the action binding. I'm wondering if $this will also work (you probably would need $this.NavigateUri). You can do this across controls e.g.
<TextBox x:Name="SomeTextBox" />
<HyperlinkButton x:Name="SomeLink" Content="Read more..." cal:Message.Attach="HyperlinkClicked(SomeTextBox)" />
The above would pass the Text property of the textbox to the HyperlinkClicked method by default.

Use naming convention to apply class if label contains text

I'm new to jquery, and sorry if this question is asked before (could find exactly what I was looking for)
I would like to implement the following convetion: If I create a error label with the name '_lblError' like this:
<div>
<asp:label ID="_lblError" text="this is a error msg"/>
</div>
classA or classB would be applied to it depending on a empty string or not in the 'text' parameter of the label.
I also would like to place this code in a external js file that would be used throughout the app.
Thanks for any help!
To start with, you probably need to give the label a css class that can be used for selection:
<asp:Label Id="_lblError" Text="This is the error message"
CssClass="ThisIsWhatWeWillllWorkWith" />
This will probably output something like
<span id="ct100__lblError" class="ThisIsWhatWeWillWorkWith">
This is the error message.
</span>
You can now select the label in jQuery using the class as selector, and add class A or B depending on whether the .text() property is empty or not.
$(function() {
$('.ThisIsWhatWeWillWorkWith').each(function() {
if($(this).text() == '') { $(this).addClass('ClassA'); }
else { $(this).addClass('ClassB'); }
});
});
All code is provided as is, with no guarantees of working without modification. But you get the general idea of how to solve the problem...
EDIT: In response to your comment, here's a way to do it without adding a css class to the label. Instead of using an <asp:Label> tag for the error message, wrap a literal in a tag you hard-code on your page:
<span class="ThisIsWhatWeWillWorkWith"><asp:Literal ID="__ltlError" Text="This is the error message.</asp:Literal></span>
Another, perhaps more elegant way, would be to create your own custom label, and use that instead.
public class ErrorLabel : System.Web.UI.WebControls.Label
{
public ErrorLabel() {
this.CssClass = "ThisIsWhatWeWillWorkWith";
}
}
You then put the error message on your page with the following line:
<asp:ErrorLabel ID="__lblError" Text="This is the error message" />
Again, not sure if the above code will work as is. But again, you get the idea of what to do...
If the idea is to provide say, have different font and/or background if there is an error and display nothing if there is no text in the error label then you could make the control a literal instead of a label. The literal control does not create a control with no text (MSDN doc)

Resources