What is the difference between bind:after and bind:set in Blazor 7? - binding

Recently, in Blazor 7, a feature has been added to make it easier to bind and call the method based on changes in the bound expression.
In .NET 7, you can now easily run async logic after a binding event has completed using the new #bind:after modifier:
<input #bind="searchText" #bind:after="PerformSearch" />
#code {
string searchText = "";
async Task PerformSearch()
{
// Do something async with searchText
}
}
In this example, the PerformSearch async method runs automatically after any changes to the search text are detected.
Another method has been added too. The #bind:get and #bind:set modifiers are always used together. The #bind:get modifier specifies the value to bind to, and the #bind:set modifier specifies a callback that's called when the value changes.
The questions are:
What is the difference between #bind:after="PerformSearch" and #bind:set="PerformSearch"? Both of these seem to call the PerformSearch after the searchText is changed.
Where is the use of each?

What is the difference between #bind:after="PerformSearch" and #bind:set="PerformSearch"?
You should only use #bind:after="PerformSearch" with #bind="searchText",in which case the bind will set the value of searchText, so you shouldn't also try and set it in PerformSearch.
If you use #bind:set="PerformSearch" then you must set the value of searchText in PerformSearch, and use #bind:get="searchText".
Where is the use of each?
The MS Docs article I think gives a good guide. It all depends on your knowledge level on components.
It's important to understand two points:
This is Razor syntax, not C#.
It's just syntactic sugar: high level functionality, shorthand Razor directives to encapsulate existing functionality.
Also note:
There's been a lot of MS Blazor Team activity on this subject since 7.0 was released. See https://github.com/dotnet/aspnetcore/issues/44957 for details on problems with the way the Razor compiler treats the #bind directives.
There are some updates to the MS Docs on the subject - https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-7.0?view=aspnetcore-7.0#blazor-custom-elements.
Here's my demo page for this answer.
#page "/"
<PageTitle>Index</PageTitle>
<input class="form-control mb-3" type="text" #bind:get="this.Value" #bind:set="ValueSetter" #bind:event="oninput" />
<input class="form-control mb-3" type="text" #bind:get="this.Value" #bind:after="ValueSetter" />
<input class="form-control mb-3" type="text" #bind="this.Value" #bind:after="DoSearch" #bind:event="oninput"/>
<div class="alert alert-info m-2 p-2">
#Value
</div>
<div class="alert alert-primary m-2 p-2">
#message
</div>
#code {
private string? Value;
private string message = "Not Set";
private async Task DoSearch()
{
await Task.Delay(1000);
message= $"Set at {DateTime.Now.ToLongTimeString()}";
}
private void ValueSetter(string __value)
=> this.Value = __value;
private Task SearchSetter(string __value)
{
this.searchText = __value;
return DoSearch();
}
}
Let's look at the actual C# code the Razor compiler builds.
This is the code snippet when just using bind:set=this.ValueSetter:
__builder.AddAttribute(8, "oninput", EventCallback.Factory.CreateBinder(
this,
CompilerServices.RuntimeHelpers.CreateInferredBindSetter(
callback: this.ValueSetter,
value: this.Value
),
this.Value));
This simply calls the setter delegate assigned to set.
This is the code snippet when using :bind=this.Value and #bind:after=DoSearch:
__builder.AddAttribute(14, "oninput", EventCallback.Factory.CreateBinder(
this, CompilerServices.RuntimeHelpers.CreateInferredBindSetter(
callback: __value => {
this.Value = __value;
return RuntimeHelpers.InvokeAsynchronousDelegate(callback: DoSearch);
},
value: this.Value),
this.Value));
It's a little more complicated. The compiler builds the equivalent to this:
Task AnonymousMethod(string __value)
{
this.Value = __value;
return DoSearch()
}
A Note on Development Environment Errors
Depending on your development environment, you will get errors with certain combinations. Some of which at the moment appear to be misleading or totally wrong. They will be fixed shortly.
Quote from Dan Roth: Hi folks. The VS fix for this just missed the window for 17.4.4 but should be addressed in the next VS patch update in February. We apologize for the wait and thank you for your patience!
In Visual Studio.
This is a correct error:
<InputText class="form-control" #bind-Value:get="this.searchText" #bind-Value:set="this.SetSearchText" #bind-Value:after="DoSearch" />
Severity Code Description Project File Line Suppression State
Error (active) RZ10019 Attribute 'bind-Value:after' can not be used with 'bind-Value:set'. Invoke the code in 'bind-Value:after' inside 'bind-Value:set' instead.
While this is bull!
<input class="form-control mb-3" type="text" #bind:get="this.Value" #bind:set="ValueSetter" #bind:event="oninput" />
And while it gives this error compiles and runs!
Severity Code Description Project File Line Suppression State
Error (active) CS1503 Argument 3: cannot convert from 'Microsoft.AspNetCore.Components.EventCallback<string>' to 'System.Action<string?>'
And this line:
<input class="form-control mb-3" type="text" #bind:get="this.Value" #bind:after="ValueSetter" />
Compiles but is obviously also total bull.
__builder.AddMarkupContent(9, "\r\n\r\n<input class=\"form-control mb-3\" type=\"text\" #bind:get=\"this.Value\" #bind:after=\"ValueSetter\">\r\n\r\n");

Why is it #bind:get+#bind:set and not just #bind+#bind:set?
Because if you see <input #bind="#val" #bind:set="#MyMethod" /> often, it creates confusion:
It looks as if the #bind:set is what makes it a two-way binding, and that you could make it one-way by removing that. Whereas in fact that would be wrong (you'd still have a two-way binding, just one that behaves differently now).
It looks as if it would be equivalent to write <input value="#val" #bind:set="#MyMethod />, and it almost is, but not quite because the formatting logic would differ. Much better not to create the ambiguity and have one correct solution.
We can avoid the above problems by having a compiler rule that #bind:get and #bind:set must always be used as a pair - you can't just have one of them and not the other (nor can you have them with #bind). So none of the weird cases will arise.
Couldn't you use #bind:set to achieve (in effect) #bind:after, and hence we don't need #bind:after?
Theoretically yes. You could #bind:set to a method that writes to your field and then runs your async logic. However, this is far less obvious for newcomers, and is less convenient in common cases. And it invites mistakes: if you do something async before setting the field, the UI will temporarily revert and generally behave badly. So it's valuable to have #bind:after for convenience and to guide correct usage. We can regard #bind:get/#bind:set as a more advanced case mainly for people implementing bindable components, as it gives them a really clean and safe solution, and such developers are advanced enough to understand that they just shouldn't do async work before calling ValueChanged.
Can you use all three at once, e.g., <input #bind:get="#value" #bind:set="#MyMethod" #bind:after="#DoStuff" />?
Sure, why not? I think that the generated logic should await MyMethod before calling DoStuff, since "after" feels like it means "after all the work involved in calling set". It's an edge case but I can't think of any problems this will cause nor any major increase in implementation cost.
Do other #bind modifiers like #bind:event and #bind:format work with this?
Yes, and that's partly why it's a big improvement over manual value/onchange pairs.
you can refer this link get more idea https://github.com/dotnet/aspnetcore/issues/39837

Related

Svelte input binding breaks when a reactive value is a reference type?

(I'm new to Svelte so it is quite likely that I'm doing something wrong here)
UPDATE: I've added a second, slightly different REPL which may demonstrate the problem better. Try this one: https://svelte.dev/repl/ad7a65894f8440ad9081102946472544?version=3.20.1
I've encountered a problem attempting to bind a text input to a reactive value.
I'm struggling to describe the problem in words, so hopefully a reduced demo of the issue in the attached REPL will make more sense.
https://svelte.dev/repl/6c8068ed4cc048919f71d87f9d020696?version=3.20.1
The demo contains two custom <Selector> components on a page.
The first component is passed two string values ("one" and "two"):
<Selector valueOne="one" valueTwo="two"/>
Clicking the buttons next to the input field sets selectedValue to one of these values.
This, in turn, triggers the following reactive declaration to update:
$: value = selectedValue
The input field is bound to this reactive value:
<input type="text" bind:value>
So clicking the "One" button sets the input text to "one", and clicking the "Two" button sets the input field to "two".
Importantly though, you can still type anything into the input field.
The second component is passed two array values:
<Selector valueOne={[1, "one"]} valueTwo={[2, "two"]}/>
Again, clicking the buttons sets selectedValue to one of these.
However this time the reactive declaration depends on an array element:
$: value = selectedValue[1]
Everything works as before, except now you can no longer type into the input field at all.
So the question is - why does <input bind:value> behave differently for these two:
$: value = aString
vs
$: value = anArray[x]
It seems that this is only an issue when using two-way bindings.
By switching to a one-way and an on:input handler, the problem goes away:
i.e. instead of this:
<input type="text" bind:value={valX}/>
use this:
<input type="text" value={valX} on:input={e => valX = e.target.value}/>
I'm pretty sure your reactive declaration is overwriting your bound value as soon as it changes, which is with every key stroke on the input and every button press. Meaning it technically is working, you're just reverting it each time it changes. Check out this version of it that uses a watcher.
Also binding to a reactive declaration means you're never actually changing the variables with the input (which you can see in your JSON result on the first selector when you type in the input the value doesn't update only on button click).
Why not lose the reactive declaration and bind directly to the variable you want. Then use an {#if} block to switch between which version of the input you're showing based on the truthiness of index?
<script>
export let valueOne;
export let valueTwo;
export let index;
let selectedValue = index? [] : '';
let selectValue = (val) => selectedValue = val;
</script>
{#if index}
<input type="text" bind:value={selectedValue[index]} placeholder="Type anything...">
{:else}
<input type="text" bind:value={selectedValue} placeholder="Type anything...">
{/if}
<button on:click={() => selectValue(valueOne)}>One</button>
<button on:click={() => selectValue(valueTwo)}>Two</button>
<p>
<strong>Selected value:</strong> {JSON.stringify(selectedValue)}
</p>
By binding directly to the selectedValue or an index of it you have the added benefit of changing the value with the input. Here's a working example in the REPL

Dynamically bind (or format) two #observable variables to a third #observable variable

Here's something I thought might be a bit easier. Despite the specifics of the question, I'm interested in any method that will let me have a third form field auto-updated based on the content of two other fields with Polymer.dart.
Something like this, where the "[ ]" represent form fields.
Name: [given name] [family name]
Full name: [family_name, given_name]
So for example; if someone enters "John" and "Smith" in the first two fields. Then the "full name" line shows: [Smith, John], when either of the fields are updated.
I've based the following example on the classes and mark-up from the form Dart Polymer tutorial
Get Input from a Form tutorial
For a form like this ...
<polymer-element name="reference-form" extends="form" >
<template>
<style> ... </style>
<div id="slambookform" >
<div class="entry">
<label>Author:</label>
<input type="text" value="{{theData['authorGivenName']}}" >
<input type="text" value="{{theData['authorFamilyName']}}">
</div>
:
<div class="entry">
<label>Full name:</label>
<input disabled type="text" value="{{fullName}}" >
</div>
:
</div>
<template>
</polymer-element>
My initial attempt to make this happen was a function like:
#observable
String fullName(){
return theData['authorFamilyName'] +', '+ theData['authorGivenName'];
}
Which doesn't work. When I make 'fullName' to an #observable variable and update it with a button the form is updates as required. Hence my question, can I bind a third field to two (or more) others?
I think I will need some kind of event handler. For two fields, formatting on a change even is simple enough. I want to format several fields in the ultimate case, not just two fields.
While on this topic, is there a hook in dart-polymer or dart to supply a future or call-back? In my example, something like: 'after-change'. Just thinking out loud, something like that would be good.
Thanks in advance.
Along those lines (caution - code is not tested)
<polymer-element name="reference-form" extends="form" >
<template>
<style> ... </style>
<div id="slambookform" >
<div class="entry">
<label>Author:</label>
<input type="text" value="{{authorGivenName}}" >
<input type="text" value="{{authorFamilyName}}">
</div>
:
<div class="entry">
<label>Full name:</label>
<input disabled type="text" value="{{fullName}}" >
</div>
:
</div>
<template>
</polymer-element>
class reference_form.dart
String _authorGivenName;
#observable get authorGivenName => _authorGivenName;
set authorGivenName(String val) {
_authorGivenName = val;
notifyPropertyChange(#fullName, '${_authorGivenName} ${_authorFamilyName}',
'${val} ${_authorFamilyName}');
}
String _authorFamilyName;
#observable get authorFamilyName => _authorFamilyName;
set authorFamilyName(String val) {
_authorFamilyName = val;
notifyPropertyChange(#fullName, '${_authorGivenName} ${_authorFamilyName}',
'${_autorGivenName} ${val}');
}
#observable
String get fullName => '${_authorGivenName} ${_authorFamilyName}';
I have a workaround for this problem, standing on the shoulders of Günter Zöchbauer (comment above). My objective is to "bind" one field value to two in a read-only fashion. We are not quite there yet, however the pathway is educational in its own right.
Observer method
This solution is kind of a workaround for the objective I set myself. I've made some annotations on this code to explain what I saw, or why I think is happening.
The intention is for fullName to show both names in the form:
familyName, givenName; e.g.
Smith, John
reference-form.html:
<polymer-element name="reference-form" extends="form" >
<template>
<style> ... </style>
<div id="slambookform" >
<div class="entry">
<label>Author:</label>
<input type="text" value="{{theData['givenName']}}" >
<input type="text" value="{{familyName}}">
</div>
:
<div class="entry">
<label>Full name:</label>
<input disabled type="text" value="{{fullName}}" >
</div>
:
</div>
<template>
</polymer-element>
The code for the form properties, the things Polymer-dart binds to the HTML with the moustache syntax, "{{fullName}}". To keep things simple, I used just one 'notifier' field and this updates the fullName field from both familyName and givenName.
reference_form.dart:
//---- testing ----
String _familyName; // (1)
#observable // (2)
String get familyName => _familyName; // (3)
void set familyName( String nam ){ // (4)
_familyName = nam;
fullName = notifyPropertyChange( // (5)
#fullName,
"${fullName}",
"${nam}, ${theData['givenName']}" );
}
#observable
String fullName; // (6)
//---- end: testing ----
The private member, "_familyName", is a shadow for the public familyName property used in the template (snippet above).
Shadow (private) member, "_familyName", stores the data for the familyName pseudo property.
The next three lines declare an #observable property, familyName
Get familyName. Simply echo the value for the shadow variable.
Set familyName. Updates the shadow variable and the composite fullName property.
Note: the composit formatting could be done with two lines: _familyName = nam; fullName = nam; ... But we want to see all changes propagated see (#5).
The notifyPropertyChange() method updated all observers of the fullName property.
Note: I didn't hack around inside Polymer itself; inside the Observable class, fullName doesn't has no observers with the code shown.
Until I saw this, I assumed that the Polymer binding to the HTML template was via an observer (watcher), it would seem not. I may be mistaken. In any case, the call to notifyPropertyChange() for the '#fullName' symbol didn't change the results for this test case.
fullName property bound to the Polymer form.
Basically the {{fullName}} value will be updated every time there's a change to the familyName pseudo property.
Note on efficiency:
The familyName setter is called with every keystroke (observed while debugging). I understand that, and suggest it is not always really the best solution.
For me, I'd prefer to only call the setter when a user exits the field. However when I used onblur, the trigger was a blur of the form, not the field.
It seems that we might all benefit in terms of performance with a bit more insider information about these hooks, pathways and any options available to make things more efficient.
Comments and improvements welcome. This example is a workaround for me, so its definitely a work in progress. ;-)
Encapsulation method
I am evolving a solution closer to the original ambition and based on the 'observer method' above. This approach relies on the current, i.e. Dart v4, use of modules and libraries. I'll show the working code first and explain interesting stuff with notes.
reference_form.dart:
import 'package:exportable/exportable.dart'; // [1]
class _Data // [2]
extends Object with Exportable { // [3]
#export String publishDate; // [4]
#export String authorGivenName = '(given)';
#export String authorFamilyName = '(family)';
#export String authorUrl = '';
//--- attributes ---
String get fullName => "${authorFamilyName}, ${authorGivenName}"; // [5]
void set fullName( String nam ){ // [6]
//don't need this
}
//--- ctor ---
_Data(){
publishDate = new DateTime.now().toString(); // [7]
}
} //_Data
#CustomTag('reference-form')
class SlamBookComponent extends FormElement with Polymer, Observable {
SlamBookComponent.created() : super.created();
//---- testing ----
#observable
_Data data = new _Data(); // [8]
:
} //SlambookComponent
Notes:
Include Exportable mixin to convert to JSON. I'm not exporting 'fullName' because it is just formatting at the moment.
Add exportable to your pubspec.yaml and 'Run Pub get'.
The "_Data" class is private to the reference_form.dart module. I did a bit of testing of the scope rules because I do not want the internal data structure to leak, except for something catholic like JSON of course (small-c).
Bring-in the Exportable mixin.
I have tested Exportable, it implements exactly what I thought I'd have to write myself. Happy with this.
JSON is not a requirement of the original question; but I did want the (eventual) solution to be a first class artefact that can be serialized or saved is important in the majority of my use-cases.
This is a very good example of the facility to extend Dart quick and agile!
Use the #export modifier to identify fields specific to be interchanged as JSON.
Export the fullName attribute as a String (get).
There is no need for set operation. However Dart apparently insists that a Set method matches 'get'.
I am disappointed by this. I much prefer the idea that I can have READ-ONLY properties and attributes, e.g. like ruby.
As tested, Dart SDK v1.4.0; fails when a matching setter is not implemented/declared(??).
Use a constructor to set initial values for Date data attribute.
Declares an opaque public property called "data", as an (private) _Data instance.
The data formatting of key fields is encapsulated in the private _Data declaration.
The Exportable mixin interface is used to map the private class to a public JSON result.
Point #8 demonstrates a powerful aspect of dart, to enable an opaque implementation of objects and yet, you can 'deliver'/'share' details without specific internal details.
I have run this code and checked that the concepts work for hidden data (the _Data type) and opaque access and serialisation. Also you can't accidentally look at internal private type (accidentally, although explicit hacks may be possible). I don't apologise for accepting the C / C++ conscious responsibility paradigm -- I think this a the most powerful aspect of being a programmer; WE are responsible for effects/bugs stemming from the code we produce. I recommend testing 'bits of behaviour' in small mini-use-cases.
I put examples of the polymer markup; nothing surprising. For me this approach is less verbose and a bit more Object Oriented than the original (early) Dart tutorial
reference_form.html
<polymer-element name="reference-form" extends="form" >
<template>
<style> ... </style>
<div id="slambookform" >
<div class="entry">
<label>Author:</label>
<input type="text" value="{{data.authorGivenName}}" >
<input type="text" value="{{data.authorFamilyName}}">
</div>
<div class="entry">
<label>Published:</label>
<input type="date" value="{{data.publishDate}}">
</div>
</div>
</template>
<script type="application/dart" src="reference_form.dart"> </script>
</polymer-element>
In the Polymer mark-up can know (and has visibility over) internal field names. Why?
... Because the "reference_form.html" and "reference_form.dart" via Polymer-dart. It is quite nice really; although it seems that the ".dart" and ".html" components are closely coupled like ASP.NET and C#/VN.NET as (also) specified by convenience(??). I confess that's a completely different subject; there are things to resolve to keep things yar (yachting term).
Anyway for me, I feel the approach begun with the encapsulation shamble above is better suited to my needs for a small utility.
Polymer now supports this use case directly with #ObserveProperty
#observable String authorGivenName = '';
#observable String authorFamilyName = '';
#observable String get fullName => '${authorGivenName} ${authorFamilyName}';
#ObserveProperty('authorGivenName authorFamilyName')
void updateFullName(old) {
notifyPropertyChange(#fullName, old, fullName);
}

Input box in foreach not binding correctly in knockout, only in IE

I have seen some people witm similar problems, but no solutions work. I have a viewmodel looking like
var House = function() {
this.houseName = ko.observable("");
this.reports = ko.observableArray([]);
this.addReport = function() { this.reports.push(new Report) }.bind(this);
}
and "reports" gets filled - at House.addReport() - with
var Report = function() {
this.reportname = ko.observable("");
this.sensor_id = ko.observable(0);
}
The HTML looks like (simplified)
<input type="text" data-bind="value: houseName">
<div data-bind="foreach: reports">
<input type="text" data-bind="value: reportname"><select data-bind="value: sensor_id" />
</div>
So, to the problem. When I fill in houseName, outside of the foreach, the model changes OK (I continually see it in a debug DIV). When I trigger the addReport method, the UI does as it's told and adds a textbox and a select. But only a change of the select actually changes the model - NOT a change of the textbox! Strangest of all, it seems to work in IE but not in Chrome.
Most probably a miss by me that gets behind-the-scenes treatment by IE - but what is the bug?
EDIT
This jsfiddle seems to aim at almost exactly the same goal - without my problem.
EDIT reloaded It doesn't work in Firefox either. However, it seems to work OK in JSFiddle!
My apologies - Jeff's analysis of not reproducing enough code was correct (it would have been too much to reproduce, here!)
Anyway, my error was that deep down in my code (even before Knockout came into the pic), I handled the "change" event to know when the form was dirty - thereby hijacking an event important to Knockout's business!
Be warned, others who may think of the same solution!
I see that RP Niemeyer has addressed the problem here.

ASP.NET MVC: Tri-state checkbox

I'm just now starting to learn ASP.NET MVC. How would I go about creating a reusable tri-state checbox? In WebForms this would be a control, but I don't know the MVC equivalent.
Add a TriStateCheckBox (or TriStateCheckBoxFor if you use the strongly typed overloads) extension method to HtmlHelper and add the namespace of that extension method class to the namespaces section of your web.config.
As for the implementation, I'd recommend having at look at the InputExtensions source on codeplex and using that to create your own.
Limitations:
View Rendering - When rendering HTML content, there is no attribute you can possibly place on an <input type="checkbox" /> that will give it the property indeterminate.
At some point, you'll have to use JavaScript to grab the element and set the indeterminate property:
// vanilla js
document.getElementById("myChk").indeterminate = true;
// jQuery
$("#myCheck).prop("indeterminate", true);
Form Data - model binding will always be limited to what values are actually sent in the request, either from the url or the data payload (on a POST).
In this simplified example, both unchecked and indeterminate checkboxes are treated identically:
And you can confirm that for yourself in this Stack Snippet:
label {
display: block;
margin-bottom: 3px;
}
<form action="#" method="post">
<label >
<input type="checkbox" name="chkEmpty">
Checkbox
</label>
<label >
<input type="checkbox" name="chkChecked" checked>
Checkbox with Checked
</label>
<label >
<input type="checkbox" name="chkIndeterminate" id="chkIndeterminate">
<script> document.getElementById("chkIndeterminate").indeterminate = true; </script>
Checkbox with Indeterminate
</label>
<label >
<input name="RegularBool" type="checkbox" value="true">
<input name="RegularBool" type="hidden" value="false">
RegularBool
</label>
<input type="submit" value="submit"/>
</form>
Model Binding - Further, model binding will only occur on properties that are actually sent. This actually poses a problem even for regular checkboxes, since they won't post a value when unchecked. Value types do always have a default value, however, if that's the only property in your model, MVC won't new up an entire class if it doesn't see any properties.
ASP.NET solves this problem by emitting two inputs per checkbox:
Note: The hidden input guarantees that a 'false' value will be sent even when the checkbox is not checked. When the checkbox is checked, HTTP is allowed to submit multiple values with the same name, but ASP.NET MVC will only take the first instance, so it will return true like we'd expect.
Render Only Solution
We can render a checkbox for a nullable boolean, however this really only works to guarantee a bool by converting null → false when rendering. It is still difficult to share the indeterminate state across server and client. If you don't need to ever post back indeterminate, this is probably the cleanest / easiest implementation.
Roundtrip Solution
As there are serious limitations to using a HTML checkbox to capture and post all 3 visible states, let's separate out the view of the control (checkbox) with the tri-state values that we want to persist, and then keep them synchronized via JavsScript. Since we already need JS anyway, this isn't really increasing our dependency chain.
Start with an Enum that will hold our value:
/// <summary> Specifies the state of a control, such as a check box, that can be checked, unchecked, or set to an indeterminate state.</summary>
/// <remarks> Adapted from System.Windows.Forms.CheckState, but duplicated to remove dependency on Forms.dll</remarks>
public enum CheckState
{
Checked,
Indeterminate,
Unchecked
}
Then add the following property to your Model instead of a boolean:
public CheckState OpenTasks { get; set; }
Then create an EditorTemplate for the property that will render the actual property we want to persist inside of a hidden input PLUS a checkbox control that we'll use to update that property
Views/Shared/EditorTemplates/CheckState.cshtml:
#model CheckState
#Html.HiddenFor(model => model, new { #class = "tri-state-hidden" })
#Html.CheckBox(name: "",
isChecked: (Model == CheckState.Checked),
htmlAttributes: new { #class = "tri-state-box" })
Note: We're using the same hack as ASP.NET MVC to submit two fields with the same name, and placing the HiddenFor value that we want to persist first so it wins. This just makes it easy to traverse the DOM and find the corresponding value, but you could use different names to prevent any possible overlap.
Then, in your view, you can render both the property + checkbox using the editor template the same way you would have used a checkbox, since it renders both. So just add this to your view:
#Html.EditorFor(model => model.OpenTasks)
The finally piece is to keep them synchronized via JavaScript on load and whenever the checkbox changes like this:
// on load, set indeterminate
$(".tri-state-hidden").each(function() {
var isIndeterminate = this.value === "#CheckState.Indeterminate";
if (isIndeterminate) {
var $box = $(".tri-state-box[name='" + this.name + "'][type='checkbox']");
$box.prop("indeterminate", true);
}
});
// on change, keep synchronized
$(".tri-state-box").change(function () {
var newValue = this.indeterminate ? "#CheckState.Indeterminate"
: this.checked ? "#CheckState.Checked"
: "#CheckState.Unchecked";
var $hidden = $(".tri-state-hidden[name='" + this.name + "'][type='hidden']");
$hidden.val(newValue);
});
Then you can use however you'd like in your business model. For example, if you wanted to map to a nullable boolean, you could use the CheckState property as a backing value and expose/modify via getters/setters in a bool? like this:
public bool? OpenTasksBool
{
get
{
if (OpenTasks == CheckState.Indeterminate) return null;
return OpenTasks == CheckState.Checked;
}
set
{
switch (value)
{
case null: OpenTasks = CheckState.Indeterminate; break;
case true: OpenTasks = CheckState.Checked; break;
case false: OpenTasks = CheckState.Unchecked; break;
}
}
}
Alternative Solution
Also, depending on your domain model, you could just use Yes, No, ⁿ/ₐ radio buttons
ASP.NET MVC certainly doesn't provide such component, actually it simply relies on the standard elements available in HTML but you may want to check out this solution.

Grails: checkbox not being set back to false

I am developing a Grails (1.0.4) app where I want to edit a collection of collections on a single page in a grid view. I got it to work quite well depending only on the indexed parameter handling of Spring MVC, except for one thing:
boolean (or, for that matter, Boolean) values in the grid can be set via checkbox, but not unset, i.e. when I check the checkbox and update, the value is set to true, but afterwards when I edit again, uncheck the checkbox and update, it remains true.
This is the GSP code of the checkbox:
<g:checkBox name="tage[${indexTag}].zuweisungen[${indexMitarb}].fixiert" value="${z.fixiert}" />
And this is the HTML that is generated:
<input type="hidden" name="tage[0].zuweisungen[0]._fixiert" />
<input type="checkbox" name="tage[0].zuweisungen[0].fixiert" checked="checked" id="tage[0].zuweisungen[0].fixiert" />
I've found a Grails bug that describes exactly this effect, but it's marked as fixed in 1.0.2, and the problem mechanism described there (underscore in hidden field name is put in the wrong place) is not present in my case.
Any ideas what could be the reason?
This is the solution a guy named Julius Huang proposed on the grails-user mailing list. It's reusable but relies on JavaScript to populate a hidden field with the "false" response for an unchecked checkbox that HTML unfortunately does not send.
I hack GSP to send "false" when
uncheck the box (true -> false) with
custom TagLib.
By default checkBox send nothing when
uncheck, so I use the checkBox as
event handler but send hidden field
instead.
"params" in Controller can handle
"false" -> "true" without any
modification. eg. Everything remain
same in Controller.
The Custom Tag Usage in GSP (sample usedfunc_F is "true"),
<jh:checkBox name="surveyList[${i}].usedfunc_F" value="${survey.usedfunc_F}"></jh:checkBox>
Here is what the Tag generate,
<input type="hidden" name="surveyList[#{i}].usedfunc_F" id="surveyList[#{i}].usedfunc_F" value="false" />
<input type="checkbox" onclick="jhtoggle('surveyList[#{i}].usedfunc_F')" checked="checked" />
The Javascript
<script type="text/javascript">
function jhtoggle(obj) {
var jht = document.getElementById(obj);
jht.value = (jht.value !='true' ? 'true' : 'false');
}
</script>
This is my own solution, basically a workaround that manually does what the grails data binding should be doing (but doesn't):
Map<String,String> checkboxes = params.findAll{def i = it.key.endsWith("._fixiert")} // all checkboxes
checkboxes.each{
String key = it.key.substring(0, it.key.indexOf("._fixiert"))
int tagIdx = Integer.parseInt(key.substring(key.indexOf('[')+1, key.indexOf(']')))
int zuwIdx = Integer.parseInt(key.substring(key.lastIndexOf('[')+1, key.lastIndexOf(']')))
if(params.get(key+".fixiert"))
{
dienstplanInstance.tage[tagIdx].zuweisungen[zuwIdx].fixiert = true
}
else
{
dienstplanInstance.tage[tagIdx].zuweisungen[zuwIdx].fixiert = false
}
}
Works, requires no change in grails itself, but isn't reusable (probably could be made so with some extra work).
I think that the simplest workaround would be to attach a debugger and see why Grails is failing to populate the value. Considering Grails is open source you'll be able to access the source code and once you figure out the solution for it you can patch your version.
I have also found this other bug GRAILS-2861 which mentions the issue related to binding to booleans (see Marc's comment in the thread). I guess that is exactly the problem you are describing.
I would create a small sample app that demonstrates the problem and attach it to the Grails bug (or create a new one). Someone here may be able to debug your sample app or you'll have shown the bug isn't really fixed.
Try this out, set the logs to DEBUG, frist try the first 3 if they don't show the problem up, flip them all to DEBUG:
codehaus.groovy.grails.web.servlet="error" // controllers
codehaus.groovy.grails.web.pages="error" // GSP
codehaus.groovy.grails.web.sitemesh="error" // layouts
codehaus.groovy.grails."web.mapping.filter"="error" // URL mapping
codehaus.groovy.grails."web.mapping"="error" // URL mapping
codehaus.groovy.grails.commons="info" // core / classloading
codehaus.groovy.grails.plugins="error" // plugins
codehaus.groovy.grails.orm.hibernate="error" // hibernate integration
This should allow you to see exactly when and how the parameters setting is failing and probably figure out a work around.

Resources