Magnolia - Cross field validation - magnolia

We have below requirement.
Field 1 : comboBoxField and it is mandatory.
Field 2: comboBoxField
Case 1. If editor choose field 1 value is "value1", then field 2 is mandatory
Case 2: if editor choose Field 1 value is "value2", then filed 2 is optional.
How to do this validation either in form/field validation before commit action.
Or how do we validate in commit action.
Any lead will help me a lot. I am stuck in this issue for longtime.
I tried with cross-field, dynamic filed , switchable and composite field as suggested in documentation, but I got null pointer exceptions. If you know the solution please share code snippet also.

I also suffered with this type of validation. The problem is in the isolation of the fields. I couldn't find a way to validate one field depending on another field value.
Anyway I found a workaround solution. I aggregate two dependent fields with complex field and create a validator for it.
Here's my code for the Magnolia 5.7 and vaadin validator (it's deprecated 7th version, nevertheless the logic will be the same with the 8th version).
public class CompanyRequiredFieldsValidator implements Validator {
private final ConfiguredFieldValidatorDefinition definition;
public CompanyRequiredFieldsValidator(ConfiguredFieldValidatorDefinition definition) {
this.definition = definition;
}
#Override
public void validate(Object value) throws InvalidValueException {
Optional<String> companyName = getString(value, "companyName");
if (companyName.isPresent()) {
getString(value, "companyAddress")
.orElseThrow(() -> new InvalidValueException(definition.getErrorMessage()));
}
}
private Optional<String> getString(Object value, String property) {
return Optional.of(value)
.filter(Item.class::isInstance).map(Item.class::cast)
.map(_v -> _v.getItemProperty(property))
.map(Property::getValue)
.filter(String.class::isInstance).map(String.class::cast)
.filter(StringUtils::isNotEmpty);
}
}
For complex fields validated value has com.vaadin.v7.data.Item type, so it's possible to get all properties from it.
The only problem, that the error message does not highlight the inner field, only whole complex field.

Related

initialize value of edit method in init method of form

I want to initialize a value of an edit method inside the init method of form, i wrote this:
[Form]
public class foo extends FormRun
{
str paymTermId;
public void init()
{
CustTable custTable = CustTable::find("DE-001");
paymTermId = custTable.paymTermId;
super();
}
edit str edtpaymTermId(boolean set, str _paymTermId)
{
if (set)
{
paymTermId= _paymTermId;
}
return paymTermId ;
}
}
But when i open the form the control remains empty.
any suggestions?
I tried to reproduce the issue, but was not successful. For me, when opening the form, the control shows a value.
A possible reason why it is not working for you could be that you open the form in the wrong company. In your code, you retrieve the value to display in the control from the payment term of customer DE-001. This customer exists in company USMF in the Contoso demo data and has payment term Net10. If the form is opened in this company, the value is shown in the control. If you are in another company (e.g. DAT), no value is shown.
I see 2 things that are wrong:
You are setting the value BEFORE super(). It should be after.
You SHOULDN'T initialize the value via field, you should do it calling edit method. Edit methods have a boolean SET parameter which can simulate a call for setting a value.

Default value for ViewModel

I'm making a simple Search page in MVC with some filters in it. The filters are represented by properties in my ViewModel. My ViewModel is binded to a GET form in the cshtml so my filter will appears in the querystrings and the user will be able to bookmark his search.
What I want to do is to assign a default value to some of my filters.
My (simplified) ViewModel :
public class SearchViewModel
{
//Filter I want to set a default value to
public OrganizationType? OrganizationType {get; set;}
//Results of the search
public IEnumerable<ItemViewModel> Items {get; set;}
}
I'd like to set a default value for OrganizationType. I can't simply set it in the constructor of SearchViewModel because it depends on the current user :
public void InitViewModel(SearchViewModel vm)
{
vm.OrganizationType = _someLogic.GetDefaultValue(_currentUser);
}
First solution was simply to check if OrganizationType is null, then assign a default value :
public ActionResult Search(SearchViewModel vm)
{
if(vm.OrganizationType == null)
vm.OrganizationType = _someLogic.GetDefaultValue(_currentUser);
return View(vm);
}
But this solution doesn't work as a null value corresponds to an empty filter and it's a choice that the user can make. So I can't override it.
The second solution I tried was to specify that the default value of the controller should be null in the Search action :
public ActionResult Search(SearchViewModel vm = null)
{
if (vm == null)
{
vm = new SearchViewModel();
InitViewModel(vm);
}
...
return View(vm);
}
But in practice, the variable vm is never null, so the default values are never setted.
I also tried having two Action, one wihout a ViewModel where I instanciate a new ViewModel with the default values and then call the second action :
public ActionResult Search()
{
var vm = new SearchViewModel();
InitViewModel(vm);
//Simply call the second action with the initizalied ViewModel
return Search(vm);
}
public ActionResult Search(SearchViewModel vm)
{
...
return View(vm);
}
But it doesn't work because there is now an ambiguity between the two action, and asp.net doesn't know which one to choose.
So in summary, I'd like to find a way to set a default value for a ViewModel, without setting it in the constructor and overriding user choices.
Another way to say it, how can I distinguish an "empty" ViewModel from one where some values are binded from the form.
Any idea ?
Ok I think I found a solution to my own problem...
I can use the ModelState property of the controler to check it the ViewModel is empty or was binded from the form :
public ActionResult Search(SearchViewModel vm = null)
{
if (ModelState.Count == 0)
{
InitViewModel(vm);
}
...
return View(vm);
}
So if ModelState.Count equals to 0 it means that user didn't change any filters. So the form is empty and we can bind our default values. As soon as the user will change one of the filters or submit the request, the ModelState.Count will be greater than 0 so we shouldn't set the default value. Otherwise we would override an user choice.
The logic of what you're doing is a little iffy. Generally speaking, if a value is nullable then null is the default value. However, it seems that you're trying to make a distinction here between whether the value is null because it's not set or null because the user explicitly set it to null. This type of semantic variance is usually a bad idea. If null has a meaning, then it should always carry that meaning. Otherwise, your code becomes more confusing and bugs are generally introduced as a result.
That said, you can't count on ModelState having no items. I've honestly never played around with ModelState enough in scenarios where there's not post data, but it's possible there's some scenario where there's no post data and yet ModelState may have items. Even if there isn't, this is an implementation detail. What if Microsoft does an update that adds items to ModelState in situations where it previously had none. Then, your code breaks with no obvious reason why.
The only thing you can really count on here is whether the request method is GET or POST. In the GET version of your action, you can reasonably assume that the user has made no modifications. Therefore, in this scenario, you can simply set the value to whatever you like without concern.
In the POST version of your action, the user has made some sort of modification. However, at this point, there is no way to distinguish any more whether the value is null because it is or because the user explicitly wanted it to be. Therefore, you must respect the value as-is.

Replace default client side validation by custom one in ASP.NET MVC

I have a view model containing a DateTime property, for which I want to provide a text box using using a custom format (only month and year, "MM.YYYY"):
public class MyModel {
public DateTime? DateField {get; set;}
}
Formatting the value for the TextBox is easy (using the format string). I also have implemented a custom model binder to do the conversion and this works fine.
I still have a problem with client side validation: I can implement a custom validator deriving from ValidationAttribute which implements IClientValidatable and set up the corresponding jquery.validate adapters etc.
But MVC still adds the "default" validation attribute data-val-date (in addition to my custom validation attribute data-val-monthyeardate) to the input field, so the default check still applies and the input "MM.YYYY" is rejected.
Is there any way to suppress the default client side validation for a data type and replace it with a custom one (instead of "adding" the custom one)?
Since the framework doesn't let you override the real type with some custom attribute or even override the ModelMetadataProvider for specific types, you'll have to register your own global ModelMetadataProvider that fools the validator to think it's actually a string.
Something like:
public class MyModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
if (attributes.OfType<ExcludeCharAttribute>().Any())
modelType = typeof (String);
return base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName); ;
}
}
Register it using:
ModelMetadataProviders.Current = new MyModelMetadataProvider();
Still, since the core problem is a client-side problem, I would deal with it purely with client-side code. In your monthyeardate adapter you can force removal of the date validation (I can provide an example if you'll share your monthyeardate code).
See MSDN
This is hacky but one simple thing you could do is add:
$(function () {
$.validator.methods.date = function () { return true; };
});
So that the default data-val-date always returns true along with firing your custom date validation.
I know that this is a little older, and just in case someone doesn't think about it (and since I cannot leave comments yet), to enhance #AlexC response, you can add validation to to that statement.
For instance, I use moment.js for date validation (moment.js), and this allows you to add your own validation rules.
if (moment(a, "M/YYYY").isValid() || moment(a).isValid())
{
return true;
}
This will check to see if it is a regular date, and also in this case, if the date is in "M/YYYY" format. If one of these are true, it accepts the validation.

Entity Framework and MVC 3: The relationship could not be changed because one or more of the foreign-key properties is non-nullable

I have been trying to use one View for updating an object and all its child collections (based on one-to-many relationships in an SQL Server database with an Entity Framework model).
It was suggested I should use AutoMapper, and I tried that and got it to work. (see Trying to use AutoMapper for model with child collections, getting null error in Asp.Net MVC 3 ).
But the solution is really hard to maintain. And when I try the simple one I had to begin with, using an entity object directly as the model (a "Consultant" object, the parent of all the child collections), I am able to get all the correct changed data back in the POST, and I can use UpdateModel to get them, including child collections. Simple. Granted, UpdateModel only worked after creating a custom model binder from a tip here at SO:
From my custom model binder:
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
bindingContext.ModelMetadata.ConvertEmptyStringToNull = false;
return base.BindModel(controllerContext, bindingContext);
}
protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value)
{
ModelMetadata propertyMetadata = bindingContext.PropertyMetadata[propertyDescriptor.Name];
propertyMetadata.Model = value;
string modelStateKey = CreateSubPropertyName(bindingContext.ModelName, propertyMetadata.PropertyName);
// Try to set a value into the property unless we know it will fail (read-only
// properties and null values with non-nullable types)
if (!propertyDescriptor.IsReadOnly)
{
try
{
if (value == null)
{
propertyDescriptor.SetValue(bindingContext.Model, value);
}
else
{
Type valueType = value.GetType();
if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(EntityCollection<>))
{
IListSource ls = (IListSource)propertyDescriptor.GetValue(bindingContext.Model);
IList list = ls.GetList();
foreach (var item in (IEnumerable)value)
{
list.Add(item);
}
}
else
{
propertyDescriptor.SetValue(bindingContext.Model, value);
}
}
}
catch (Exception ex)
{
// Only add if we're not already invalid
if (bindingContext.ModelState.IsValidField(modelStateKey))
{
bindingContext.ModelState.AddModelError(modelStateKey, ex);
}
}
}
}
Here's my simple Edit POST method:
[HttpPost]
[ValidateInput(false)] //To allow HTML in description box
public ActionResult Edit(int id, FormCollection collection)
{
Consultant consultant = _repository.GetConsultant(id);
UpdateModel(consultant);
_repository.Save();
return RedirectToAction("Index");
}
But after that UpdateModel worked. The problem is, at the next stage, when trying to call SaveChanges on the context, that fails. I'm getting this error:
The operation failed: The relationship
could not be changed because one or
more of the foreign-key properties is
non-nullable. When a change is made to
a relationship, the related
foreign-key property is set to a null
value. If the foreign-key does not
support null values, a new
relationship must be defined, the
foreign-key property must be assigned
another non-null value, or the
unrelated object must be deleted.
I don't understand what is wrong. I'm seeing all the correct values in the Consultant object posted, I just can't save it to database. The route of AutoMapper in this case (although an interesting tool) is not working well, it's complicating my code immensely and making the application, which should be rather simple, a nightmare to maintain.
Can anyone offer any insight into why I'm getting this error and how to overcome it?
UPDATE:
Reading some posts here, I found one that seemed slightly related: How to update model in the database, from asp.net MVC2, using Entity Framework? . I don't know if it relates to this, but when I inspected the Consultant object after POST it seems this object itself has entitykey, but the individual items in a collection do not (EntityKeySet = null). Each item however does have the correct id. I don't pretend to understand any of this with the EntityKey, so please explain if it has any bearings on my issue, and if so, how to resolve it...
UPDATE 2:
I thought of something that might have something to do with my problems: The View is using a technique described by Steven Sanderson (see http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/ ), and when debugging it seems to me as if UpdateModel has trouble matching the items in a collection in the View with the ones in the actual Consultant object. I'm wondering if this has to do with the indexing in this technique. Here's the helper from that code (I can't follow it very well myself, but it uses a Guid to create indexes, which might be the problem):
public static class HtmlPrefixScopeExtensions
{
private const string idsToReuseKey = "__htmlPrefixScopeExtensions_IdsToReuse_";
public static IDisposable BeginCollectionItem(this HtmlHelper html, string collectionName)
{
var idsToReuse = GetIdsToReuse(html.ViewContext.HttpContext, collectionName);
string itemIndex = idsToReuse.Count > 0 ? idsToReuse.Dequeue() : Guid.NewGuid().ToString();
// autocomplete="off" is needed to work around a very annoying Chrome behaviour whereby it reuses old values after the user clicks "Back", which causes the xyz.index and xyz[...] values to get out of sync.
html.ViewContext.Writer.WriteLine(string.Format("<input type=\"hidden\" name=\"{0}.index\" autocomplete=\"off\" value=\"{1}\" />", collectionName, html.Encode(itemIndex)));
return BeginHtmlFieldPrefixScope(html, string.Format("{0}[{1}]", collectionName, itemIndex));
}
public static IDisposable BeginHtmlFieldPrefixScope(this HtmlHelper html, string htmlFieldPrefix)
{
return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix);
}
private static Queue<string> GetIdsToReuse(HttpContextBase httpContext, string collectionName)
{
// We need to use the same sequence of IDs following a server-side validation failure,
// otherwise the framework won't render the validation error messages next to each item.
string key = idsToReuseKey + collectionName;
var queue = (Queue<string>)httpContext.Items[key];
if (queue == null)
{
httpContext.Items[key] = queue = new Queue<string>();
var previouslyUsedIds = httpContext.Request[collectionName + ".index"];
if (!string.IsNullOrEmpty(previouslyUsedIds))
foreach (string previouslyUsedId in previouslyUsedIds.Split(','))
queue.Enqueue(previouslyUsedId);
}
return queue;
}
private class HtmlFieldPrefixScope : IDisposable
{
private readonly TemplateInfo templateInfo;
private readonly string previousHtmlFieldPrefix;
public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix)
{
this.templateInfo = templateInfo;
previousHtmlFieldPrefix = templateInfo.HtmlFieldPrefix;
templateInfo.HtmlFieldPrefix = htmlFieldPrefix;
}
public void Dispose()
{
templateInfo.HtmlFieldPrefix = previousHtmlFieldPrefix;
}
}
}
But then again, I wouldn't have thought this should be the problem since the hidden input contains the id in the value attribute, and I thought UpdateModel just looked at the name of the field to get Programs (the collection) and Name (the property), and then the value to the the id...? And then again there's seems to be some mismatch during update. Anyway, here's the generated html from FireBug also:
<td>
<input type="hidden" value="1" name="Programs[cabac7d3-855f-45d8-81b8-c31fcaa8bd3d].Id" id="Programs_cabac7d3-855f-45d8-81b8-c31fcaa8bd3d__Id" data-val-required="The Id field is required." data-val-number="The field Id must be a number." data-val="true">
<input type="text" value="Visual Studio" name="Programs[cabac7d3-855f-45d8-81b8-c31fcaa8bd3d].Name" id="Programs_cabac7d3-855f-45d8-81b8-c31fcaa8bd3d__Name">
<span data-valmsg-replace="true" data-valmsg-for="Programs[cabac7d3-855f-45d8-81b8-c31fcaa8bd3d].Name" class="field-validation-valid"></span>
</td>
Anyone know if this is the problem? And if so, how can I work around it to be able to easily update the collections with UpdateModel? (While still being able to add or remove items in the View before POST, which was the purpose of this technique to begin with).
It looks like there is a Parent entity that has a one to many relationship with your Consultant entity. When you change an attribute of the Consultant entity that is used as the ForeignKey for that relationship, Entity Framework sets the relevant field in the Parent entity to null to decouple the relationship. When that field is not nullable you'll get this error. Actually that error definition is surprisingly good, I've seen this problem with far more cryptic errors.
So, I recommend that you check the parent entity in the database, and proceed to a remedy from there (if you can change it to nullable all is well, if it is part of a different constraint -pk or suchlike- you'll have to fiddle with your object models). I'd ask you to post your entity models, but the chunk of text is intimidating as it is.
I think the error you are getting is related to: EF 4: Removing child object from collection does not delete it - why? You have created an orphan somewhere.
Yes it is related to HtmlPrefixScopeExtensions, but only because you are using Mvc Futures model binders.
In global.asax.cs comment out the line
Microsoft.Web.Mvc.ModelBinding.ModelBinderConfig.Initialize();
and retry: it will work ok !
The problem happens because the MVC futures model binder does not handle correctly this case. It converts ok the form data into your model when you submit the form, but it has a problem when filling the ModelState object when you use HtmlPrefixScopeExtensions to generate non incremental ids.
The model itself is correctly created from the form data. The problem lies inside ModelState which contains only the last value of the collection instead of all elements of the collection.
The strongly typed helper method - which renders the list - only select items which are in your Model property list AND in the matching ModelState entry which is converted into a list. So because there is only one item in the matching ModelState entry other list items get deselected.
This method called by the strongly typed helper code:
htmlHelper.GetModelStateValue(fullName, typeof(string[]))
returns only the last element of the list, because ModelState["Programs[cabac7d3-855f-45d8-81b8-c31fcaa8bd3d].List"].Value contains only the last element of the list.
This is a bug (or non supported scenario) in MVC3 Futures extensible model binders.

Why does Hibernate Validator #NotEmpty produce duplicate messages?

While working with Hibernate Validator, I noticed that the #NotEmpty and #NotNull annotations produce duplicate messages in the InvalidValue array returned by getInvalidValues(...).
If I specify a message like #NotEmpty(message = "My error message."), then I'll get one InvalidValue of "My error message." and a second of "may not be null or empty"
If i don't include a message (eg #NotEmpty by itself), then I get two copies of the InvalidValue with a message field of "may not be null or empty".
Why does Hibernate Validator do this?? Shouldn't I get one message, either the value that I override using the parameter, or the default message, but not both??
For some more context:
I am extending ClassValidator<T> with my own ClassValidator<MyClass> implementation. I do so to add some custom validations which cannot be done by annotation. I need to see the run time value of more than one property of the class in order to determine the validation.
I get the validations when I call myClassValidator.getInvalidValues(), which I override. Inside my implementation of getInvalidValues() I call super.getInvalidValues() to create the initial error list, and then I add my custom errors to that list. In any case, the call to super.getInvalidValues() contains the duplicate messages, one matching the message property passed into the annotation, and a second with the stock value of the annotation message.
Justin,
I've been working with Hibernate Validator for the last couple of months. While I have not run into the same issue that you've described, I also have not extended ClassValidator. For custom validation, the Hibernate Reference Guide indicates that writing custom constraints is the way to go.
I have been able to use the built-in constraints almost exclusively on my current project. In one case, where I needed to do some very specific calculations on an integer field, I wrote a custom constraint as described in the reference guide; it was a breeze.
Speaking to your specific problem, I wrote a simple test app as a sort of sanity check on my part:
import org.hibernate.validator.*;
public class HibernateValidatorTest {
#NotEmpty
#NotNull
private String validateMe;
public static void main ( String[] args ) {
ClassValidator<HibernateValidatorTest> validator =
new ClassValidator<HibernateValidatorTest>( HibernateValidatorTest.class );
InvalidValue[] inVals =
validator.getInvalidValues( new HibernateValidatorTest() );
for ( InvalidValue inVal : inVals ) {
System.out.println( inVal.getMessage() );
}
}
}
With both Hibernate constraints on the validateMe field, the console output is:
may not be null or empty
may not be null
Removing one or the other has the expected effect of printing only a single message to the console.
I hope this is helpful.
Brian T. Grant

Resources