Data binding to an enum on a command in Grails - data-binding

I have a class:
class User {
Set<Foo> foos = []
}
where Foo is an enum:
class Foo { A, B, C, D}
I have a controller action with a parameter of type User
def someAction = {User user ->
// impl omitted
}
I've created a multi-select in a GSP
<g:select name="foos" multiple="true" from="${Foo.values()}"/>
But when I submit the form the selected values do not get bound to the foos property of the User command object. What am I doing wrong?

http://www.grails.org/TipsAndTricks
Enum usage
If you want to use a Enum with a "value" String attribute (a pretty common idiom) in a element, try this:
enum Rating {
G("G"),PG("PG"),PG13("PG-13"),R("R"),NC17("NC-17"),NR("Not Rated")
final String value
Rating(String value) { this.value = value }
String toString() { value }
String getKey() { name() }
}
Then add optionKey="key" to your tag. Credit: Gregg Bolinger

This isn't really going to be an answer, per se. But I can't post the details of this in a comment. I just created the following:
enum State {
OK,KS,FL,MA
}
class User {
Set<State> states = []
static constraints = {
}
}
<g:form controller="home" action="save">
<g:select name="states" multiple="true" from="${com.orm.fun.State.values()}"/>
<g:submitButton name="save" value="Save"/>
</g:form>
// controller action
def save = { User user ->
// I didn't do anything here except
// set a breakpoint for debugging
}
And this is what I get:
So I'm not entirely sure what is different between yours and mine, except the name of the enum. Can you see anything?

Related

Haxe: Binding pattern with abstract fields access methods

I'd like to make wrapper to implement simple data binding pattern -- while some data have been modified all registered handlers are got notified. I have started with this (for js target):
class Main {
public static function main() {
var target = new Some();
var binding = new Bindable(target);
binding.one = 5;
// binding.two = 0.12; // intentionally unset field
binding.three = []; // wrong type
binding.four = 'str'; // no such field in wrapped class
trace(binding.one, binding.two, binding.three, binding.four, binding.five);
// outputs: 5, null, [], str, null
trace(target.one, target.two, target.three);
// outputs: 5, null, []
}
}
class Some {
public var one:Int;
public var two:Float;
public var three:Bool;
public function new() {}
}
abstract Bindable<TClass>(TClass) {
public inline function new(source) { this = source; }
#:op(a.b) public function setField<T>(name:String, value:T) {
Reflect.setField(this, name, value);
// TODO notify handlers
return value;
}
#:op(a.b) public function getField<T>(name:String):T {
return cast Reflect.field(this, name);
}
}
So I have some frustrating issues: interface of wrapped object doesn't expose to wrapper, so there's no auto completion or strict type checking, some necessary attributes can be easily omitted or even misspelled.
Is it possible to fix my solution or should I better move to the macros?
I almost suggested here to open an issue regarding this problem. Because some time ago, there was a #:followWithAbstracts meta available for abstracts, which could be (or maybe was?) used to forward fields and call #:op(a.b) at the same time. But that's not really necessary, Haxe is powerful enough already.
abstract Binding<TClass>(TClass) {
public function new(source:TClass) { this = source; }
#:op(a.b) public function setField<T>(name:String, value:T) {
Reflect.setField(this, name, value);
// TODO notify handlers
trace("set: $name -> $value");
return value;
}
#:op(a.b) public function getField<T>(name:String):T {
trace("get: $name");
return cast Reflect.field(this, name);
}
}
#:forward
#:multiType
abstract Bindable<TClass>(TClass) {
public function new(source:TClass);
#:to function to(t:TClass) return new Binding(t);
}
We use here multiType abstract to forward fields, but resolved type is actually regular abstract. In effect, you have completion working and #:op(a.b) called at the same time.
You need #:forward meta on your abstract. However, this will not make auto-completion working unless you remove #:op(A.B) because it shadows forwarded fields.
EDIT: it seems that shadowing happened first time I added #:forward to your abstract, afterwards auto-completion worked just fine.

Localization in EnumDropDownListFor using asp.net boilerplate

I have an enum dropdown
//control
#Html.EnumDropDownListFor(
m => m.OrderBy,
new {#class = "btn btn-default dropdown-toggle toggle", onchange = "document.getElementById('hf_Pagename').value,this.form.submit();"})
//my enum
public enum OrderByOptions
{
Default,
PriceLowToHigh,
PriceHighToLow,
MostRecent
}
Now the problem is I need to localize them. But in this case from" PriceLowToHigh" needs to change to " Price- low to high"
You can use AbpDisplayNameAttribute:
public enum OrderByOptions
{
[AbpDisplayName(MyConsts.LocalizationSourceName, "OrderByOptions.Default")]
Default,
[AbpDisplayName(MyConsts.LocalizationSourceName, "OrderByOptions.PriceLowToHigh")]
PriceLowToHigh,
[AbpDisplayName(MyConsts.LocalizationSourceName, "OrderByOptions.PriceHighToLow")]
PriceHighToLow,
[AbpDisplayName(MyConsts.LocalizationSourceName, "OrderByOptions.MostRecent")]
MostRecent
}
Define them in your localization files:
<text name="OrderByOptions.PriceLowToHigh">Price - Low to High</text>
Update
AbpDisplayName works on type class
You can define:
[AttributeUsage(AttributeTargets.Field)]
public class FieldAbpDisplayNameAttribute : AbpDisplayNameAttribute
{
// ...
}
Then use [FieldAbpDisplayNameAttribute(...)] instead.
There are many ways to achieve the issue.
Way #1
Don't use #Html.EnumDropDownListFor! Just traverse enum and create the html element like below;
(I am writing the code on the top of my head)
<select>
#foreach (var item in Enum.GetValues(typeof(OrderByOptions)))
{
<option value="#((int)item)">#(Localize(item.ToString()))</option>
}
</select>
There's no Localize method. Just localize it with your way.
Way #2
Other alternative is not using enum but create a dropdown item collection. And let an item consist of DisplayText and Value. Display Text must be localized from server.
Way #3
Follow the instructions explained here:
https://ruijarimba.wordpress.com/2012/02/17/asp-net-mvc-creating-localized-dropdownlists-for-enums/
With the information above, I solve my problem this way.
I created a custome Attribute AbpEnumDisplayNameAttribute inherited from AbpDisplayNameAttribute.
[AttributeUsage(AttributeTargets.Field)]
public class AbpEnumDisplayNameAttribute : AbpDisplayNameAttribute
{
/// <summary>
/// <see cref="AbpDisplayNameAttribute"/> for enum values.
/// </summary>
public AbpEnumDisplayNameAttribute(string sourceName, string key) : base(sourceName, key)
{
}
}
Then I created an extension for Enum display value localization.
public static class EnumLocalizationExtension
{
public static string ToLocalizedDisplayName(this Enum value)
{
var displayName = value.ToString();
var fieldInfo = value.GetType().GetField(displayName);
if (fieldInfo != null)
{
var attribute = fieldInfo.GetCustomAttributes(typeof(AbpEnumDisplayNameAttribute), true)
.Cast<AbpEnumDisplayNameAttribute>().Single();
if (attribute != null)
{
displayName = attribute.DisplayName;
}
}
return displayName;
}
}

How to find reason "A circular reference was detected while serializing.... 'System.Reflection.RuntimeModule'

I have setup the view MyView.chstml with two submit action. but give me "A circular reference was detected while serializing an object of type 'System.Reflection.RuntimeModule'. How to troubleshoot easily ?
#using (Html.BeginForm("MyView", "Worker", FormMethod.Post, new { enctype = "multipart/form-data", #name = "formWorker" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal" id="divWork"> ....
<div class="form-group">
<button id="btnSubmit" class="btn btn-success" type="submit" value="Work1">Work1</button>
</div>
<div id="Dynamictable" class="table-responsive hidden" name="dtable">
<table class="table table-bordered" id="dctable" name="dctable"></table>
</div>
<div id="dialogsubmit" title="second submit">
<div id="dialog-content" name="dialog-content" class="form-control hidden"> </div>
</div>
</div>
then in script
(function () {
//strangely the below ajax is never called
$('#formWorker').submit(function () {
debugger;
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
debugger;
bootbox.alert(result);
}
});
// it is important to return false in order to
// cancel the default submission of the form
// and perform the AJAX call
return false;
});
}
$("#btnSubmit").click(function () {
if(document.getElementById('dctable').getElementsByTagName("tr").length < 1)
{
aRow = document.all("dctable").insertRow();
var oCell = aRow.insertCell();
oCell = newRow.insertCell();
oCell.innerHTML = '<input type="submit" name="submit2" value="submit2" />';
**//strangely if i replace the above RHS with below, it act as submit halft submit form (half becuase the FormCollection object in HttpPost method of controller lacks key submit2 and also HttpPostedFileBase object came as null in controller method ,**
*//'<button id="submit2" name="submit2" class="btn submit2" value="submit2" ><i class="fa fa-search"></i> submit2</button>'*
return false;
}
});
</script>
in controller
[HttpGet]
public ActionResult Worker()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Worker(HttpPostedFileBase file, FormCollection form)
{
string value = form["submit2"]; //half submit form as it is null in additon to file is null. if i use commented RHS , all good here, but no option in view to process the output in same view.
IEnumerable<object> data = Enumerable.Empty<object>();
if (value != null) // key doesn't exist
{
//process here and and return json to shown result on same page using popup/alert.
return this.Json(
new
{
Result = data.ToList()
}, JsonRequestBehavior.AllowGet
);
}
return PartialView("anoterhView", data.ToList());
}
A circular reference exception is generally thrown when the JSON serializer initiates a loop that causes effectively an infinite loop of serializing. This happens when you look at the schema of your serializable properties. For example:
Take the two classes below:
public class Node
{
public Node Parent { get; set; }
}
public class Parent : Node
{
public List<Node> Children { get; set; } = new List<Node>();
}
As you can see we have two simple classes, a Node class which has a public property Parent which is type Node. Now if we look at the Parent class it derrives from Node and has the property Children which is a List<Node>. This is where we will start to have circular dependency issues. Consider the following simple method.
public string SerializeJsonObject()
{
var parent = new Parent();
var child = new Node();
child.Parent = parent;
parent.Children.Add(child);
return Json(parent);
}
This method constructs a Parent object parent and then a Node object child. Next we set the Parent property of child to the parent instance. Then we Add the child to the parent's Children list.
Now consider the serialization of parent.
-- Parent Item
-- Parent: null no procesing
-- Children : Serialize each Node object
-- Node 1
-- Parent
-- Parent: null no processing
-- Children:
-- Node 1
-- Parent
--Parent: null no processing
--Children:
-- Node 1
..... continues forever never finishing serializing Node 1
From this we can see our circular dependency is the Parent property however it is only a circular reference because then Parent has a reference to the Node in the Children collection. Therefore the serializer can never finish serializing the object.
Now this isn't limited to lists we can look at a similar example where two classes have a reference to each other and are both serializable. Consider the following class.
public class Node
{
public string Name { get; set; }
public Node Previous { get; set; }
public Node Next { get; set; }
}
This class has a dependency on Node for both the Previous and Next property. Therefore given a method of constructing a small dataset.
public static object SerailizeANode()
{
Node first = null;
Node previous = null;
for(var i = 0; i < 10; i++)
{
var current = new Node();
current.Name = $"Node {i}";
if(previous != null)
{
previous.Next = current;
current.Previous = previous;
}
previous = current;
if (first == null)
first = current;
}
return Json(first);
}
This is really simple but ends up with 10 objects where 1-9 have a dependency on the Next node and objects 2-10 have a dependency to the Previous node. So given the serialization of first
-- first
-- Name: Node 0
-- Previous: Null
-- Next:
-- Name: Node 1
-- Previous
-- Name: Node 0
-- Previous: null
-- Next:
-- Name: Node 1
-- Previous:
-- Name: Node 0
-- Previous: null
-- Next:
--Name: Node 1
continues on forever.
Again as we see by the dependency serialization of the Property (Previous & Next) cause the serializer to hit a circular reference (infinite loop) and throws an exception.
My expectation is there a similar issue in the data that is returning to the browser through the section nicely commented out.
//process here and and return json to shown result on same page using popup/alert.
return this.Json(new
{
Result = data.ToList()
}, JsonRequestBehavior.AllowGet);
If you need more information could you please post the schema of the classes being returned in the commented out section.

AutoMapper: Can't access original object instances passed to Map() from within AfterMap()

I have code like this:
//Fields
Product _prod, _existingProd;
void Test()
{
_prod = MakeAndPopulateSomeRandomProduct();
_existingProd = GetProdFromDb(1);
Mapper.CreateMap()
.AfterMap((s, d) =>
{
Console.WriteLine(d==_existingProd); //Why does this print false?
//Customize other properties on destination object
});
Mapper.Map(_prod, _existingProd);
}
When I call Test(), false is printed but I expected true. In my scenario, it is important to be able to access the original destination object via the AfterMap argument. I only included the fields to demonstrate the problem but in my real code, I don't have direct access to them. How can I access the object instances passed in to Map() when customizing the mapping?
The following example works. Probably you are using some type converter which creates new instance... Also please provide all mapping configurations to better understand the problem.
[TestFixture]
public class AfterMap_Test
{
//Fields
private Product _prod, _existingProd;
[Test]
public void Test()
{
Mapper.CreateMap<Product, Product>()
.AfterMap((s, d) =>
{
Trace.WriteLine(d == _existingProd); //Why does this print false?
//Customize other properties on destination object
});
_existingProd = new Product {P1 = "Destination"};
_prod = new Product {P1 = "Source"};
Mapper.Map(_prod, _existingProd);
}
}
internal class Product
{
public string P1 { get; set; }
}

Get all descendants types of base class

I have a base class called BaseEvent and several descendants classes:
public class BaseEvent {
// the some properties
// ...
}
[MapInheritance(MapInheritanceType.ParentTable)]
public class Film : BaseEvent {
// the some properties
// ...
}
[MapInheritance(MapInheritanceType.ParentTable)]
public class Concert : BaseEvent {
// the some properties
// ...
}
I have a code which create the BaseEvent instance at runtime:
BaseEvent event = new BaseEvent();
// assign values for a properties
// ...
baseEvent.XPObjectType = Database.XPObjectTypes.SingleOrDefault(
t => t.TypeName == "MyApp.Module.BO.Events.BaseEvent");
Now, this event will be shows in BaseEvent list view.
I want to do the following: when a user click Edit button then show in list view lookup field with all descendants types. And when user saves record change ObjectType to selected value.
How can I do this?
Thanks.
PS. this is asp.net app.
I'm not sure that your approach is correct for what you are trying to achieve. First, I'll answer the question you have asked, and afterwards I'll try to explain how the XAF already provides the functionality you are trying to achieve, namely how to choose which subclass of record to create from the user interface.
In order to create a property which allows the user to choose a Type within the application, you can declare a TypeConverter:
public class EventClassInfoTypeConverter : LocalizedClassInfoTypeConverter
{
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
List<Type> values = new List<Type>();
foreach (ITypeInfo info in XafTypesInfo.Instance.PersistentTypes)
{
if ((info.IsVisible && info.IsPersistent) && (info.Type != null))
{
// select BaseEvent subclasses
if (info.Type.IsSubclassOf(typeof(BaseEvent)))
values.Add(info.Type);
}
}
values.Sort(this);
values.Insert(0, null);
return new TypeConverter.StandardValuesCollection(values);
}
}
And then your base event class would look like:
public class BaseEvent: XPObject
{
public BaseEvent(Session session)
: base(session)
{ }
private Type _EventType;
[TypeConverter(typeof(EventClassInfoTypeConverter))]
public Type EventType
{
get
{
return _EventType;
}
set
{
SetPropertyValue("EventType", ref _EventType, value);
}
}
}
However, I suspect this is not the functionality you require. Modifying the value of the property will NOT change the base type of the record. That is, you will end up with a record of type BaseEvent which has a property Type equal to 'Concert' or 'Film'.
XAF already provides a mechanism for selecting the type of record to create. In your scenario, you will find that the New button is a dropdown with your different subclasses as options:
Therefore you do not need to create a 'type' property within your object. If you need a column to show the type of event in the list view, you can declare a property as follows
[PersistentAlias("XPObjectType.Name")]
public string EventType
{
get
{
return base.ClassInfo.ClassType.Name;
}
}

Resources