I have a model class like this:
class Person {
string FirstName,
string LastName,
string ID
}
When I send the model to the browser via a GET, I send the data as a composite of two fields (e.g. FirstName.ToString() + LastName.ToString()) through an anonymous type.
The problem comes when I do a POST back to the server. Since the JSON is coming back as different from the model, it comes back as invalid through ModelState.IsValid() because my action method is expecting a List<Person> persons.
I really don't want to create a ModelViewModel duplicating code, because one field is causing the model to be invalid. Is there a way around this?
In this case, I would add DataAnnotations to the ViewModel class and change the Action to accept the ViewModel rather than the Model.
Once you validate that the ViewModel being passed to the Action is valid, you can parse the composite field back to into the First and Last names properly.
Related
While trying to assign an object to a view in my controller action I get the following message because this object is not persisted:
Could not serialize Domain Object Vendor\Extension\Domain\Model\Object. It is neither an Entity with identity properties set, nor a Value Object.
Is there any possibility to add this object to the view without creating a databaseentry?
The exception [InvalidArgumentValueException('Could not serialize Domain Object $className. It is neither an Entity with identity properties set, nor a Value Object.', 1260881688)][1] is thrown in the UriBuilder, thus when a model shall be used as argument for creating a link.
The instance of Vendor\Extension\Domain\Model\Object must either fulfill these requirements:
can be represented as array (is array or implements Iterator interface) OR
extends TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject AND one of
extends TYPO3\CMS\Extbase\DomainObject\AbstractValueObject OR
having a valid uid, not null
Thus, if you instantiated the object directly in the controller, the uid property is not defined yet. This property is assigned if domain objects are fetched or added with a repository.
TypeConverters
TypeConverters allow to convert from a given identifier (some string representation, hash-value, ...) to a proper domain object. The following links show how to do that for the concept of an IBAN (International Bank Account Identifier).
IBAN model
TypeConverter to create object from string
TypeConverters have to registerd in ext_localconf.php like this:
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerTypeConverter(
\H4ck3r31\BankAccountExample\Domain\Property\TypeConverter\IbanTypeConverter::class
);
The Iban object can be used then in your controller:
public function someAction(Iban $iban) { ... }
Use array representation of your object
Another alternative could be to assign an array representation of the domain object to the view and use that to fill the link arguments:
$this->view->assign('myObject', $object->toArray());
When invoking a controller action, the object is reconstituted from the submitted array keys and are used as properties - thus array keys and properties must have the same naming, or a persistence column mapping is defined.
public function someAction(MyObject $object) { ... }
In my previously mentioned bank account example it looks like this:
BankDto model
Controller action invocation
The term "Dto" is the abbreviation for "Data Transfer Object", thus it's not a real domain entity, does not have a proper UID and is just used to encapsulate information in a domain object when passing that to different components.
I'm trying to bind a list/arraylist/hashmap/etc of custom objects to my form in JSP using Spring. Right now, the controller creates a Map of the two lists (Boolean list and custom object list) in referenceData(), and provides it to the form which uses those values to populate the fields. The values are initialized from a MySQL database using Hibernate, and all that works fine. The list is a known length before the form is initialized, so that part is easier. Now what I'd like to do is correctly bind those objects in the form, so that when there are changes made, I can detect that in onSubmit() (or wherever is appropriate), and update the database accordingly. I can't seem to bind them correctly in the form so that I can see changes made. I tried just using a list of the form fields as the model, but even that wasn't working correctly. Do I just need to inject the list in a particular way? Any ideas or examples here? Any help would be greatly appreciated.
UPDATE: At Ralph's request here is the solution I used:
In my data object class, I lazy loaded a map using MapUtils.lazyMap(), with a String key and other custom object value. The other custom object is just a class that contains List<String> and getters/setters. In the corresponding .jsp file, I just nest several loops to loop through the keys first using loop.current.key and then loop2.current.value.paramsList to loop through the values for that key. This was not really what I asked for in my original post, as I was looking for a more general solution, and the lazy loading pointed me in the right direction.
In Spring 2 you need a special List in your Command object, that is able to grow if one add the x-th element event if the list has not this size yet.
One way to do that is to use LayzList decorator from commons-collections.
#Override
protected Object formBackingObject(final HttpServletRequest request)
throws Exception {
List<PosterSelectionRow> posterSelectionRowList = LazyList.decorate(
new ArrayList<PosterSelectionRow>(),
new PosterSelectionRowListFactory());
return new PosterSelectionCommand(posterSelectionRowList);
//PosterSelectionCommand contains a list of selected poster rows
}
private static class PosterSelectionRowListFactory
implements org.apache.commons.collections.Factory {
/** Invoked if the list need a new element */
public Object create() {
return = new PosterSelectionRow();
}
}
When I remember right, there is a way without that Factory stuff, but I am not sure.
I need to work with the below posted data, or a variant thereof. Basically, I need to post a variable number of key-value pairs that represent a question id and answer string.
How do I write an ASP.NET MVC2 controller method signature to accept an unknown number of key-value pairs?
attachmentId=8809&question_712=&question_713=&question_714=&question_715=&question_716=&question_717=&question_719=&question_720=&question_721=&question_722=&question_723=&question_724=&question_725=&question_726=&question_727=&question_731=&question_738=&question_739=&question_741=&question_742=&question_743=&question_744=&question_745=&question_746=&question_747=&question_748=
Please note that in this example, there are 26 question keys with empty values. There may be more or less keys and they may or may not have a value. I can reformulate the way the data is sent by the client, so if the best solution is to rethink the way it is sent, I'm open to that.
This is basically the data a FormCollection collects. It's used in the automatically generated controllers by default. i.e. public ActionResult Edit(int id, FormCollection collection)
Use an array. The default modelbinder can detect arrays.
model.questions[0].key model.questions[1].value and so on for the html tag names then build an object that follows those conventions.
public class QuestionUpdateModel{
public int attachmentID{get;set;}
public QuestionPair[] Questions{get;set;}
}
public class QuestionPair{
public int key{get;set;}
public string value{get;set;}
}
After that your controller should accept an argument of QuestionUpdateModel type. The modelbinder should take care of the rest. Make sure you index them sequentially so it can create the array without null entries.
I've been looking around for an answer to this, which I can't believe hasn't been asked before, but with no luck I'm attempting here.
I have a signup form which differs slightly based upon what type of participant the requester is. While writing tests for the solution, I realized that all actions did the same things, so I'm attempting to combine the actions into one using a strategy pattern.
public abstract class BaseForm { common properties and methods }
public class Form1 : BaseForm { unique properties and overrides }
....
public class FormX : BaseForm { unique properties and overrides... in all about 5 forms }
Here is the combined action:
[ModelStateToTempData, HttpPost]
public ActionResult Signup(int id, FormCollection collection)
{
uiWrapper= this.uiWrapperCollection.SingleOrDefault(w => w.CanHandle(collection));
// nullcheck on uiWrapper, redirect if null
var /*BaseForm*/ form = uiWrapper.GetForm(); // Returns FormX corresponding to collection.
this.TryUpdateModel(form, collection.ToValueProvider()); // Here is the problem
form.Validate(this.ModelState); // Multi-Property validation unique to some forms.
if (!this.ModelState.IsValid)
return this.RedirectToAction(c => c.Signup(id));
this.Logic.Save(form.ToDomainClass());
return this.RedirectToAction(c => c.SignupComplete());
}
The problem I'm having is that TryUpdateModel binds only the common properties found in BaseForm. My previous code used public ActionResult FormName(int id, FormX form) which bound properly. However, I did some testing and discovered that if I replace var form with FormX form the form binds and everything works, but I'm back to one action per form type.
I'm hoping to find a way to get this to bind properly. form.GetType() returns the proper non-base class of the form, but I'm not sure of how to grab the constructor, instantiate a class, and then throw that into TryUpdateModel. I know that the other possibility is a custom ModelBinder, but I don't see a way of creating one without running into the same FormBase problem.
Any ideas or suggestions of where to look?
I was trying to do something similar to Linq, I was trying to create a class that would inherit some standard fields (ID, etc). I found that the default Linq engine would only use fields from the instantiated class, not from any inherited classes or interfaces.
To construct a Type simply use code like:
var form = Activator.CreateInstance(uiWrapper.GetForm());
I figured it out!
Erik's answer wasn't the solution, but for some reason it made me think of the solution.
What I really want form to be is a dynamic type. If I change this line:
dynamic form = uiWrapper.GetForm();
Everything works :)
On top of that, logic.Save(form.ToDomainClass()) also goes directly to Save(DomainTypeOfForm) rather than Save(BaseDomainForm) so I can avoid the headache there as well. I knew that once I figured out the problem here I could apply the answer in my logic class as well.
I'm using NHibernate and I've got a Campaign class and it has a Client class as one of its members and I'm attempting to use ModelState.IsValid in my [HttpPost] public Create (CreateCampaignViewModel vm) method.
As my ViewModel contains the object Campaign ModelStats.IsValid is always false as I'm passing the Client.Id from the form instead of the entire Client object.
Is there a way to have it load the Client member before it runs the validation code that sets ModelState.IsValid? Or is there a way to refresh the state of ModelState after I 'manually' retrieve the client object based on the ClientId and bind it to the Campaign?
You probably want to add an attribute to the parameter of your action method, to disable binding of the Id:
[HttpPost]
public Create([Bind(Ignore="ClientId")]CreateCampaignViewModel vm)
Steven Sanderson has a blog post on just this. He removes any of the items which do not have incoming values from the errors collection.
http://blog.stevensanderson.com/2010/02/19/partial-validation-in-aspnet-mvc-2/
Try using one of the overloads on the controller's UpdateModel method. Then call ModelState.IsValid.
I believe SharpArch has a modelbinder that can load the NH objects. But, I don't like that idea very much...instead I'd use a separate PostModel to hold the posted form values. If you re-use the ViewModel to also hold the post data, you'll usually have a lot of fields that make no sense to be posted (like the Client object in your example) or readonly display fields.
class CampaignPostModel{
...
public int ClientId { get; set; }
}
All you need to do is match-up the property names to the form input names and add this as a parameter on your post action method.