Internationalization in inList - grails

i found this and it was very useful to what i want to do, but is there is way to make what in the inList follow the Grails Internationalization
Thanks

i found workaround , i think it may help ..
1. toString()
As you know, the scaffolded screens just output values in selects and dropdowns through the toString() method of the shown object. We can construct each enum with a hardcoded translation and have toString() return that value.
class Product {
enum Status {
AVAILABLE("Beschikbaar"), SOLD_OUT("Uitverkocht")
final String value
Status(String value) {
this.value = value
}
String toString() {
value
}
}
String name
Status status
static constraints = { name blank: false, unique: true }
}
The actual names of the enum now only appear in the generated HTML:
<select name="status" required="" id="status" >
<option value="AVAILABLE" >Beschikbaar</option>
<option value="SOLD_OUT" >Uitverkocht</option>
</select>
2. MessageSourceResolvable
By now you’ll probably understand above hardcoded solution works for just one language, one hardcoded in the enum itself – which will cause problems when in a month from now your application actually needs to support a 2nd language :-) So how do we leverage the fact that we have already have message_XX.properties where we’ve put our other message keys?
Use the underlying Spring framework. Have our enum implement org.springframework.context.MessageSourceResolvable e.g. like this:
enum Status implements org.springframework.context.MessageSourceResolvable {
AVAILABLE, SOLD_OUT
public Object[] getArguments() { [] as Object[] }
public String[] getCodes() { [ name() ] }
public String getDefaultMessage() { "?-" + name() }
}
Now we can provide a value in our messages_nl.properties for each enum we have:
product.label=Product
product.name.label=Naam
product.status.label=Status
AVAILABLE=Beschikbaar
SOLD_OUT=Uitverkocht

Related

async call to Controller - for state and county

In Grails - I need to make a controller method that will populate State and County dropdown form fields so that when a State is selected it will fill only that State's counties into the County dropdown.
A colleague told me that's an asynchronous call in Grails, but I'm a novice in Grails and I really don't know what that is or how to start one. Any help would be greatly appreciated.
Here's my code snippets:
using Grails 2.43 currently. I have two domain classes (State and County), and two Select dropdowns for State and County.
Form elements:
<g:select name="locationState" class="form-control" from="${....State.list().sort{it.orderNumber}}">
<g:select name="locationCounty" class="form-control" from="${...State.FindByName(it.orderNumber).counties}">
Here are the example classes:
class State {
static hasMany = [county:County]
String name
String value
int orderNumber = 0
static constraints = {
name nullable:false, maxSize:50, blank:false
value nullable:false, maxSize:100, blank:false
}
String toString(){
"$value"
}
static mapping = {
table 'state'
cache: 'read-write'
columns{
id column:'id'
name column:'name'
value column:'value'
orderNumber column:'order_number'
}
id generator: 'assigned'
}
}
class County {
State state
String county
static constraints = {
state nullable:false
county nullable:false, maxSize:100, blank:false
}
String toString(){
"${state.name} - $county"
}
static mapping = {
table 'county'
cache: 'read-write'
columns{
id column:'id'
county column:'county'
state column:'state_id'
}
id generator: 'assigned'
}
}
The async guide linked in the comments is for make programatic, asynchronous calls. For example, if you had two computationally expensive method calls (or ones that would require network I/O) you can use threads to run them (roughly) in parallel. Grails provides many different helpers to make this kind of asynchronous programming very easy.
However, this is not likely something you need for your GORM queries. You want to populate a second select box. You could accomplish this two ways, by reloading the page after the state is selected, or by using JavaScript to populate the box. I am assuming you want to do the latter. Grails does provide tools (such as the <g:remoteFunction /> tag) to handle this without writing your own JavaScript but the Grails AJAX library has since been deprecated and its use is not recommended.
Instead, you should just write your own JavaScript. I'll show you a technique using jQuery:
In your view, initialize both selects, but the second should be initialized as empty. We are also going to give them IDs to make them easier to select from jQuery:
<g:select name="locationState"
class="form-control"
from="${....State.list().sort{it.orderNumber}}"
id="location-state" />
<g:select name="locationCounty"
class="form-control"
from="${[]}"
id="location-county" />
Then, we will need to expose an action on the controller to load the counties when the user selects a state:
def loadCountiesByState() {
def state = params.state
def counties = State.findByValue(state).counties
render g.select(name: 'locationCounty', class: 'form-control',
from: counties, id: 'location-county')
}
You should be able to test this part just by pointing your browser to /app-name/controller-name/loadCountiesByState?state=CA. I don't know exactly how your data is modeled so you might need to alter the State.findByValue(state) part to fit your needs.
Now we just need to wire up the control with some JavaScript. Make sure you have jQuery included.
<script type="text/javascript">
$(function() {
$('#location-sate').change(function() {
var state = $(this).val();
$.ajax({
url: '/app-name/controller-name/loadCountiesByState',
date: { state: state },
success: function(data) {
$('#location-county').replaceWith(data);
}
});
});
});
</script>
This will replace the dropdown with a new select that should be fully populated with the counties.

GORM: how to retrieve only two values for dropdown

In Grails using GORM, I'd like to retrieve two possible values for a form dropdown. This particular instance is to only have two possible countries in a dropdown. I've set them in my Config.groovy
The GORM statement in this that I've done only returns USA and I'd like to return Canada also - so I have the findAll statement slightly incorrect. Can someone help me?
Country<g:select name="Country" from="${....country.findAllById("100225","100038").sort{it.orderNumber}}" value="otherstuff" class="form-control" required="" aria-labelledby="country-label"/>
Config.groovy:
country.usa=100225
country.canada=100038
Domain class:
class country {
String name
String value
int orderNumber = 0
static constraints = {
name nullable:false, maxSize:50, blank:false
value nullable:false, maxSize:100, blank:false
}
String toString(){
"$name - $value"
}
static mapping = {
table 'country'
cache: 'read-write'
columns{
id column:'id'
name column:'name'
value column:'value'
orderNumber column:'order_number'
}
id generator: 'assigned'
}
}
you should rather use findAllByIdInList(["100225","100038"]).
Also consider not writing such code within the view. Make it part of your model and prepare it in the controller.

Integer enum and g:select

I have an Enumeration like this:
public enum MyEnum {
Apple (1)
Microsoft (2)
IBM (4)
Intel (8)
int company
MyEnum(int company) {
this.company = company
}
}
And I want a g:select box looking like this (the integer values are important in the value attribute):
<select>
<option value="1">Apple</option>
<option value="2">Microsoft</option>
<option value="4">IBM</option>
<option value="8">Intel</option>
</select>
Ok thats no problem using the g:select:
<g:select name="myenum" from="${MyEnum?.values()*.company}" />
But when I try to save the form I always get:
java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [MyEnum] for property myenum: no matching editors or conversion strategy found
How can I resolve this?
Try with:
public enum MyEnum {
Apple (1)
Microsoft (2)
IBM (4)
Intel (8)
int company
MyEnum(int company) {
this.company = company
}
String toString() { return company }
String getKey() { name() }
}
and then modify the tag with
<g:select name="myenum" from="${MyEnum?.values()*.company}" optionKey="key" />
Now I use a simple integer with the inList constrain instead of the enum. Its not the same but solves my problem.
class MyDomain {
int company
static constraints = {
company(inList: [1, 2, 4, 8])
}
}
In the form:
<g:select valueMessagePrefix="company" name="company" from="${MyDomain.constraints.company.inList}" value="${myDomainInstance?.company}"/>
Now I need to use the i18n files (messages.properties):
company.1=Apple
company.2=Microsoft
company.4=IBM
company.8=Intel

asp.net mvc validation messages localization for numbers (data-val-number) [duplicate]

Assume this model:
Public Class Detail
...
<DisplayName("Custom DisplayName")>
<Required(ErrorMessage:="Custom ErrorMessage")>
Public Property PercentChange As Integer
...
end class
and the view:
#Html.TextBoxFor(Function(m) m.PercentChange)
will proceed this html:
<input data-val="true"
data-val-number="The field 'Custom DisplayName' must be a number."
data-val-required="Custom ErrorMessage"
id="PercentChange"
name="PercentChange" type="text" value="0" />
I want to customize the data-val-number error message which I guess has generated because PercentChange is an Integer. I was looking for such an attribute to change it, range or whatever related does not work.
I know there is a chance in editing unobtrusive's js file itself or override it in client side. I want to change data-val-number's error message just like others in server side.
You can override the message by supplying the data-val-number attribute yourself when rendering the field. This overrides the default message. This works at least with MVC 4.
#Html.EditorFor(model => model.MyNumberField, new { data_val_number="Supply an integer, dude!" })
Remember that you have to use underscore in the attribute name for Razor to accept your attribute.
What you have to do is:
Add the following code inside Application_Start() in Global.asax:
ClientDataTypeModelValidatorProvider.ResourceClassKey = "Messages";
DefaultModelBinder.ResourceClassKey = "Messages";
Right click your ASP.NET MVC project in VS. Select Add => Add ASP.NET Folder => App_GlobalResources.
Add a .resx file called Messages.resx in that folder.
Add these string resources in the .resx file:
FieldMustBeDate The field {0} must be a date.
FieldMustBeNumeric The field {0} must be a number.
PropertyValueInvalid The value '{0}' is not valid for {1}.
PropertyValueRequired A value is required.
Change the FieldMustBeNumeric value as you want... :)
You're done.
Check this post for more details:
Localizing Default Error Messages in ASP.NET MVC and WebForms
This is not gonna be easy. The default message is stored as an embedded resource into the System.Web.Mvc assembly and the method that is fetching is a private static method of an internal sealed inner class (System.Web.Mvc.ClientDataTypeModelValidatorProvider+NumericModelValidator.MakeErrorString). It's as if the guy at Microsoft coding this was hiding a top secret :-)
You may take a look at the following blog post which describes a possible solution. You basically need to replace the existing ClientDataTypeModelValidatorProvider with a custom one.
If you don't like the hardcore coding that you will need to do you could also replace this integer value inside your view model with a string and have a custom validation attribute on it which would do the parsing and provide a custom error message (which could even be localized).
As an alternate way around this, I applied a RegularExpression attribute to catch the invalid entry and set my message there:
[RegularExpression(#"[0-9]*$", ErrorMessage = "Please enter a valid number ")]
This slightly a hack but this seemed preferable to the complexity the other solutions presented, at least in my particular situation.
EDIT: This worked well in MVC3 but it seems that there may well be better solutions for MVC4+.
From this book on MVC 3 that I have. All you have to do is this:
public class ClientNumberValidatorProvider : ClientDataTypeModelValidatorProvider
{
public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata,
ControllerContext context)
{
bool isNumericField = base.GetValidators(metadata, context).Any();
if (isNumericField)
yield return new ClientSideNumberValidator(metadata, context);
}
}
public class ClientSideNumberValidator : ModelValidator
{
public ClientSideNumberValidator(ModelMetadata metadata,
ControllerContext controllerContext) : base(metadata, controllerContext) { }
public override IEnumerable<ModelValidationResult> Validate(object container)
{
yield break; // Do nothing for server-side validation
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
yield return new ModelClientValidationRule {
ValidationType = "number",
ErrorMessage = string.Format(CultureInfo.CurrentCulture,
ValidationMessages.MustBeNumber,
Metadata.GetDisplayName())
};
}
}
protected void Application_Start()
{
// Leave the rest of this method unchanged
var existingProvider = ModelValidatorProviders.Providers
.Single(x => x is ClientDataTypeModelValidatorProvider);
ModelValidatorProviders.Providers.Remove(existingProvider);
ModelValidatorProviders.Providers.Add(new ClientNumberValidatorProvider());
}
Notice how the ErrorMessage is yielded, you specify the current culture and the localized message is extracted from the ValidationMessages(here be culture specifics).resx resource file. If you don't need that, just replace it with your own message.
Here is another solution which changes the message client side without changed MVC3 source. Full details in this blog post:
https://greenicicle.wordpress.com/2011/02/28/fixing-non-localizable-validation-messages-with-javascript/
In short what you need to do is include the following script after jQuery validation is loaded plus the appropriate localisation file.
(function ($) {
// Walk through the adapters that connect unobstrusive validation to jQuery.validate.
// Look for all adapters that perform number validation
$.each($.validator.unobtrusive.adapters, function () {
if (this.name === "number") {
// Get the method called by the adapter, and replace it with one
// that changes the message to the jQuery.validate default message
// that can be globalized. If that string contains a {0} placeholder,
// it is replaced by the field name.
var baseAdapt = this.adapt;
this.adapt = function (options) {
var fieldName = new RegExp("The field (.+) must be a number").exec(options.message)[1];
options.message = $.validator.format($.validator.messages.number, fieldName);
baseAdapt(options);
};
}
});
} (jQuery));
You can set ResourceKey of ClientDataTypeModelValidatorProvider class to name of a global resource that contains FieldMustBeNumeric key to replace MVC validation error message of number with your custom message. Also key of date validation error message is FieldMustBeDate.
ClientDataTypeModelValidatorProvider.ResourceKey="MyResources"; // MyResource is my global resource
See here for more details on how to add the MyResources.resx file to your project:
Here is another solution in pure js that works if you want to specify messages globally not custom messages for each item.
The key is that validation messages are set using jquery.validation.unobtrusive.js using the data-val-xxx attribute on each element, so all you have to do is to replace those messages before the library uses them, it is a bit dirty but I just wanted to get the work done and fast, so here it goes for number type validation:
$('[data-val-number]').each(function () {
var el = $(this);
var orig = el.data('val-number');
var fieldName = orig.replace('The field ', '');
fieldName = fieldName.replace(' must be a number.', '');
el.attr('data-val-number', fieldName + ' باید عددی باشد')
});
the good thing is that it does not require compiling and you can extend it easily later, not robust though, but fast.
Check this out too:
The Complete Guide To Validation In ASP.NET MVC 3 - Part 2
Main parts of the article follow (copy-pasted).
There are four distinct parts to creating a fully functional custom validator that works on both the client and the server. First we subclass ValidationAttribute and add our server side validation logic. Next we implement IClientValidatable on our attribute to allow HTML5 data-* attributes to be passed to the client. Thirdly, we write a custom JavaScript function that performs validation on the client. Finally, we create an adapter to transform the HTML5 attributes into a format that our custom function can understand. Whilst this sounds like a lot of work, once you get started you will find it relatively straightforward.
Subclassing ValidationAttribute
In this example, we are going to write a NotEqualTo validator that simply checks that the value of one property does not equal the value of another.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class NotEqualToAttribute : ValidationAttribute
{
private const string DefaultErrorMessage = "{0} cannot be the same as {1}.";
public string OtherProperty { get; private set; }
public NotEqualToAttribute(string otherProperty)
: base(DefaultErrorMessage)
{
if (string.IsNullOrEmpty(otherProperty))
{
throw new ArgumentNullException("otherProperty");
}
OtherProperty = otherProperty;
}
public override string FormatErrorMessage(string name)
{
return string.Format(ErrorMessageString, name, OtherProperty);
}
protected override ValidationResult IsValid(object value,
ValidationContext validationContext)
{
if (value != null)
{
var otherProperty = validationContext.ObjectInstance.GetType()
.GetProperty(OtherProperty);
var otherPropertyValue = otherProperty
.GetValue(validationContext.ObjectInstance, null);
if (value.Equals(otherPropertyValue))
{
return new ValidationResult(
FormatErrorMessage(validationContext.DisplayName));
}
}
return ValidationResult.Success;
}
}
Add the new attribute to the password property of the RegisterModel and run the application.
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
[NotEqualTo("UserName")]
public string Password { get; set; }
...
Implementing IClientValidatable
ASP.NET MVC 2 had a mechanism for adding client side validation but it was not very pretty. Thankfully in MVC 3, things have improved and the process is now fairly trivial and thankfully does not involve changing the Global.asax as in the previous version.
The first step is for your custom validation attribute to implement IClientValidatable. This is a simple, one method interface:
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
ModelMetadata metadata,
ControllerContext context)
{
var clientValidationRule = new ModelClientValidationRule()
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "notequalto"
};
clientValidationRule.ValidationParameters.Add("otherproperty", OtherProperty);
return new[] { clientValidationRule };
}
If you run the application now and view source, you will see that the password input html now contains your notequalto data attributes:
<div class="editor-field">
<input data-val="true" data-val-notequalto="Password cannot be the same as UserName."
data-val-notequalto-otherproperty="UserName"
data-val-regex="Weak password detected."
data-val-regex-pattern="^(?!password$)(?!12345$).*"
data-val-required="The Password field is required."
id="Password" name="Password" type="password" />
<span class="hint">Enter your password here</span>
<span class="field-validation-valid" data-valmsg-for="Password"
data-valmsg-replace="true"></span>
</div>
Creating a custom jQuery validate function
All of this code is best to be placed in a separate JavaScript file.
(function ($) {
$.validator.addMethod("notequalto", function (value, element, params) {
if (!this.optional(element)) {
var otherProp = $('#' + params);
return (otherProp.val() !=
}
return true;
});
$.validator.unobtrusive.adapters.addSingleVal("notequalto", "otherproperty");
}(jQuery));
Depending on your validation requirements, you may find that the jquery.validate library already has the code that you need for the validation itself. There are lots of validators in jquery.validate that have not been implemented or mapped to data annotations, so if these fulfil your need, then all you need to write in javascript is an adapter or even a call to a built-in adapter which can be as little as a single line. Take a look inside jquery.validate.js to find out what is available.
Using an existing jquery.validate.unobtrusive adapter
The job of the adapter is to read the HTML5 data-* attributes on your form element and convert this data into a form that can be understood by jquery.validate and your custom validation function. You are not required to do all the work yourself though and in many cases, you can call a built-in adapter. jquery.validate.unobtrusive declares three built-in adapters which can be used in the majority of situations. These are:
jQuery.validator.unobtrusive.adapters.addBool - used when your validator does not need any additional data.
jQuery.validator.unobtrusive.adapters.addSingleVal - used when your validator takes in one piece of additional data.
jQuery.validator.unobtrusive.adapters.addMinMax - used when your validator deals with minimum and maximum values such as range or string length.
If your validator does not fit into one of these categories, you are required to write your own adapter using the jQuery.validator.unobtrusive.adapters.add method. This is not as difficulty as it sounds and we'll see an example later in the article.
We use the addSingleVal method, passing in the name of the adapter and the name of the single value that we want to pass. Should the name of the validation function differ from the adapter, you can pass in a third parameter (ruleName):
jQuery.validator.unobtrusive.adapters.addSingleVal("notequalto", "otherproperty", "mynotequaltofunction");
At this point, our custom validator is complete.
For better understanding refer to the article itself which presents more description and a more complex example.
HTH.
I just did this and then used a regex expression:
$(document).ready(function () {
$.validator.methods.number = function (e) {
return true;
};
});
[RegularExpression(#"^[0-9\.]*$", ErrorMessage = "Invalid Amount")]
public decimal? Amount { get; set; }
Or you can simply do this.
#Html.ValidationMessageFor(m => m.PercentChange, "Custom Message: Input value must be a number"), new { #style = "display:none" })
Hope this helps.
I make this putting this on my view
#Html.DropDownListFor(m => m.BenefNamePos, Model.Options, new { onchange = "changePosition(this);", #class="form-control", data_val_number = "This is my custom message" })
I have this problem in KendoGrid, I use a script at the END of View to override data-val-number:
#(Html.Kendo().Grid<Test.ViewModel>(Model)
.Name("listado")
...
.Columns(columns =>
{
columns.Bound("idElementColumn").Filterable(false);
...
}
And at least, in the end of View I put:
<script type="text/javascript">
$("#listado").on("click", function (e) {
$(".k-grid #idElementColumn").attr('data-val-number', 'Ingrese un número.');
});
</script>
a simple method is, use dataanotation change message on ViewModel:
[Required(ErrorMessage ="الزامی")]
[StringLength(maximumLength:50,MinimumLength =2)]
[Display(Name = "نام")]
public string FirstName { get; set; }

RazorEngine and parsing physical view files always causes exception

I have the following RazorEngine call:
public class RazorEngineRender
{
public static string RenderPartialViewToString(string templatePath, string viewName, object model)
{
string text = System.IO.File.ReadAllText(Path.Combine(templatePath, viewName));
string renderedText = Razor.Parse(text, model);
return renderedText;
}
}
This is called from:
_emailService.Render(TemplatePath, "Email.cshtml", new { ActivationLink = activationLink });
I also have this view file (email.cshtml):
<div>
<div>
Link: #Model.ActivationLink
</div>
</div>
When the call to Razor.Parse() occurs, I always get a:
Unable to compile template. Check the Errors list for details.
The error list is:
error CS1061: 'object' does not contain a definition for 'ActivationLink' and no extension method 'ActivationLink' accepting a first argument of type 'object' could be found
I've tried everything under the sun, including trying a concrete type as opposed to anonymous type, declaring the #Model line at the top of the view file but no luck. I'm wondering if the library is at fault or definately me?
By the way, the razorengine I am referring to is available here at codeplex:
RazorEngine
If you make the call like so:
Razor.Parse(System.IO.File.ReadAllText(YourPath),
new { ActivationLink = activationLink });
That should give you the correct output. But after I see your method posted above I'll be able to make a determination where the problem lies.
Update
Change your method to the following:
public class RazorEngineRender {
public static string RenderPartialViewToString<T>(string templatePath, string viewName, T model) {
string text = System.IO.File.ReadAllText(Path.Combine(templatePath, viewName));
string renderedText = Razor.Parse(text, model);
return renderedText;
}
}
and you can call it like you do above.
The reason it doesn't work is because you're telling the Parser that the model is of type object rather than passing in what type it really is. In this case an anonymous type.
The accepted answer was perfect in 2011 (I believe pre-v3 of RazorEngine) but this code is now marked as obsolete in latest version (in time of typing it is 3.7.3).
For newer version your method can be typed like this:
public static string RenderPartialViewToString<T>(string templatePath, string templateName, string viewName, T model)
{
string template = File.ReadAllText(Path.Combine(templatePath, viewName));
string renderedText = Engine.Razor.RunCompile(template, templateName, typeof(T), model);
return renderedText;
}
and in order for it to work you need to add
using RazorEngine.Templating;
Here are a few hints you might try:
Make your razor view strongly typed to a model:
#model Foo
<div>
<div>
Link:
<a href="#Model.ActivationLink" style="color:#666" target="_blank">
#Model.ActivationLink
</a>
</div>
</div>
When rendering it pass a Foo model:
_emailService.Render(
TemplatePath,
"Email.cshtml",
new Foo { ActivationLink = activationLink }
)
If you are trying to send emails from your Views make sure you checkout Postal before reinventing something.

Resources