custom boolean attributes not binding - aurelia

Custom boolean attributes do not bind like built in. checked.bind='1===2' will not include the checked attribute in the tag. myboolatt.bind='1===2' WILL include the myboolatt in the tag. I did myboolatt.bind='1===2 | boolconverter' to log out the value and it says false.
So what am I doing wrong? It seems Aurelia is inconsistent on binding expressions. Another instance is I can do this title=${$index<12} inside a repeat and I get what is expected. So I thought this would work - myboolatt.bind=${$index<12}. That doesn't even compile.
Believe me I have read the all the doc (doesn't mean I didn't miss something) and many posts including the long running discussions between the Aurelia team concerning boolean attributes.
I have wrapped expressions in "" and in ${} and in both but just can't get it to work.
It feels like I am missing 1 vital piece of information that will magically explain these inconsistencies and slay my frustration. One of the reasons I like Aurelia so much (on top of convention based) is that I have actually just guessed at a few things - thinking this is how I would do it - and ding-dang if they didn't just work.
I really feel like this should just work. So again I ask - what am I doing wrong?
Thanks

If you are using .bind, you bind it to the att variable in your js/ts file, and you should not use any dollar signs or brackets.
For example, myboolatt.bind=${$index<12} should be myboolatt.bind="$index<12". The use of the dollar sign in index has nothing to do with the bindings. This is just a variable provided by Aurelia.
When you want to use variables without bind you use ${variable}.
The checked attribute not being present, I in the tag I'm guessing is because it's evaluated to false, and the checked attribute is not a true/false attribute. A input that's checked will look like <input type="checkbox" checked/>

It was not Aurelia binding. I initially created bool attributes but did not have a need to bind. They were just empty classes. Then I needed binding so I added the necessary methods. What ended up causing the confusion was using myboolatt sends no argument into valueChanged where myboolatt.bind sends T or F. Not being a regular js'er this threw me a bit and I see there are a number of ways to handle. Anyway here are the attributes and usage. Hope it helps someone else.
Thanks
function private_ValueChanged(self, att, value) {
if (value === false) self.element.removeAttribute(att);
else self.element.setAttribute(att, '');
}
export class toggleCustomAttribute {
static inject = [Element];
constructor(element) { this.element = element; }
valueChanged(newValue, oldValue) { private_ValueChanged(this, 'toggle', newValue);
}
export class radioCustomAttribute {
static inject = [Element];
constructor(element) { this.element = element; }
valueChanged(newValue, oldValue) { private_ValueChanged(this, 'radio', newValue); }
}
<ff-button repeat.for="keymen of keyChangeMenu" radio.bind="$index<12" class='ff-button' click.delegate="keyChangeClick($event.target)" id.bind="$index+'_key'" >
${keymen[0]}<sup>${keymen[1]}</sup>
</ff-button>

Related

Efficient way to bring parameters into controller action URL's

In ASP.Net Core you have multiple ways to generate an URL for controller action, the newest being tag helpers.
Using tag-helpers for GET-requests asp-route is used to specify route parameters. It is from what I understand not supported to use complex objects in route request. And sometimes a page could have many different links pointing to itself, possible with minor addition to the URL for each link.
To me it seems wrong that any modification to controller action signature requires changing all tag-helpers using that action. I.e. if one adds string query to controller, one must add query to model and add asp-route-query="#Model.Query" 20 different places spread across cshtml-files. Using this approach is setting the code up for future bugs.
Is there a more elegant way of handling this? For example some way of having a Request object? (I.e. request object from controller can be put into Model and fed back into action URL.)
In my other answer I found a way to provide request object through Model.
From the SO article #tseng provided I found a smaller solution. This one does not use a request object in Model, but retains all route parameters unless explicitly overridden. It won't allow you to specify route through an request object, which is most often not what you want anyway. But it solved problem in OP.
<a asp-controller="Test" asp-action="HelloWorld" asp-all-route-data="#Context.GetQueryParameters()" asp-route-somestring="optional override">Link</a>
This requires an extension method to convert query parameters into a dictionary.
public static Dictionary GetQueryParameters(this HttpContext context)
{
return context.Request.Query.ToDictionary(d => d.Key, d => d.Value.ToString());
}
There's a rationale here that I don't think you're getting. GET requests are intentionally simplistic. They are supposed to describe a specific resource. They do no have bodies, because you're not supposed to be passing complex data objects in the first place. That's not how the HTTP protocol is designed.
Additionally, query string params should generally be optional. If some bit of data is required in order to identify the resource, it should be part of the main URI (i.e. the path). As such, neglecting to add something like a query param, should simply result in the full data set being returned instead of some subset defined by the query. Or in the case of something like a search page, it generally will result in a form being presented to the user to collect the query. In other words, you action should account for that param being missing and handle that situation accordingly.
Long and short, no, there is no way "elegant" way to handle this, I suppose, but the reason for that is that there doesn't need to be. If you're designing your routes and actions correctly, it's generally not an issue.
To solve this I'd like to have a request object used as route parameters for anchor TagHelper. This means that all route links are defined in only one location, not throughout solution. Changes made to request object model automatically propagates to URL for <a asp-action>-tags.
The benefit of this is reducing number of places in the code we need to change when changing method signature for a controller action. We localize change to model and action only.
I thought writing a tag-helper for a custom asp-object-route could help. I looked into chaining Taghelpers so mine could run before AnchorTagHelper, but that does not work. Creating instance and nesting them requires me to hardcode all properties of ASP.Net Cores AnchorTagHelper, which may require maintenance in the future. Also considered using a custom method with UrlHelper to build URL, but then TagHelper would not work.
The solution I landed on is to use asp-all-route-data as suggested by #kirk-larkin along with an extension method for serializing to Dictionary. Any asp-all-route-* will override values in asp-all-route-data.
<a asp-controller="Test" asp-action="HelloWorld" asp-all-route-data="#Model.RouteParameters.ToDictionary()" asp-route-somestring="optional override">Link</a>
ASP.Net Core can deserialize complex objects (including lists and child objects).
public IActionResult HelloWorld(HelloWorldRequest request) { }
In the request object (when used) would typically have only a few simple properties. But I thought it would be nice if it supported child objects as well. Serializing object into a Dictionary is usually done using reflection, which can be slow. I figured Newtonsoft.Json would be more optimized than writing simple reflection code myself, and found this implementation ready to go:
public static class ExtensionMethods
{
public static IDictionary ToDictionary(this object metaToken)
{
// From https://geeklearning.io/serialize-an-object-to-an-url-encoded-string-in-csharp/
if (metaToken == null)
{
return null;
}
JToken token = metaToken as JToken;
if (token == null)
{
return ToDictionary(JObject.FromObject(metaToken));
}
if (token.HasValues)
{
var contentData = new Dictionary();
foreach (var child in token.Children().ToList())
{
var childContent = child.ToDictionary();
if (childContent != null)
{
contentData = contentData.Concat(childContent)
.ToDictionary(k => k.Key, v => v.Value);
}
}
return contentData;
}
var jValue = token as JValue;
if (jValue?.Value == null)
{
return null;
}
var value = jValue?.Type == JTokenType.Date ?
jValue?.ToString("o", CultureInfo.InvariantCulture) :
jValue?.ToString(CultureInfo.InvariantCulture);
return new Dictionary { { token.Path, value } };
}
}

How to keep your MVC controllers DRY for Edit->Save->ValidateFail

I've got a Manage User event that takes an an optional userID and displays a user edit screen. There is a manageUserViewModel to go with this screen.
My Manage page has some dependencies - eg, PageTitle, what method to submit to, etc.
If I validate-fail, I need to show the manage screen again, but this time, using the view-model that was passed into the same method.
Supplying these dependencies in the fail scenario isn't very DRY.
How do I step repeating the dependencies? I tried putting them into a separate method, but that does not feel right.
public ActionResult Manage(Guid? UserID)
{
User user = this._UserLogic.GetUser(UserID);
ViewBag.Title = "User List";
ViewBag.OnSubmit = "Save";
ManageUserViewModel uvm = Mapper.Map<User, ManageUserViewModel>(user);
return View("Manage", uvm);
}
[AcceptVerbs("POST")]
public ActionResult Save(ManageUserViewModel uvm)
{
User user = this._UserLogic.GetUser(uvm.UserID);
if (!ModelState.IsValid)
// This is not very DRY!!!
ViewBag.Title = "Manage User";
ViewBag.OnSubmit = "Save";
return View("Manage", uvm);
}
Mapper.Map<ManageUserViewModel, User>(uvm, user );
this._UserLogic.SaveUser(user);
return RedirectToAction("Manage", new { UserID = user.ID });
}
I think you misunderstand DRY. DRY does not mean "NEVER repeat yourself", it means that you should not repeat yourself when it makes sense not to.
Different views have different requirements, and creating a complex structure just to avoid repeating yourself violates other best practices, like KISS, and SRP.
SOLID is interesting because Single Responsibility Principle is often at odds with Don't Repeat Yourself, and you have to come up with a balance. In most cases, DRY loses because SRP is far more important.
It looks to me like you have code here that is handling multiple responsibilities just so you can avoid writing similar code more than once. I disagree with doing that, because each view has different responsibilities and different requirements.
I would suggest just creating separate controller actions, views, and models for each action, particularly if the validation requirements are different for them. There may be a few things you can do (like using Partial Views or Editor Templates) to reduce repetition, but in general don't add lots of complexity just to avoid repetition.
You could add the 'Manager User' Title and 'Save' OnSubmit strings as properties of on the ManageUserViewModel. This means that you would not have to add them to the ViewBag each time you called Save.
You could also make a ManageUserService which could be responsible for the AutoMapper mappings and saving the user.
You code would then look like this:
public ActionResult Manage(Guid? UserID)
{
var uvm = _userService.GetById(UserId);
return View("Manage", uvm);
}
[AcceptVerbs("POST")]
public ActionResult Save(ManageUserViewModel uvm)
{
if (!ModelState.IsValid)
{
return View("Save", uvm);
}
_userService.Save(uvm);
return RedirectToAction("Manage", new { UserID = uvm.ID });
}
Just put the CRUD logic and AutoMapping functionality in the a class called UserService, and instance of which can be injected using Inversion of Control into your controller.
If you don't want to hard-code your string values into the view model itself, then you could add the values to an ApplicationResources file and reference those from the view model.
You will have to find some way to preserve this information between requests, which either means passing it back and forth between the client and server or saving it on the server. Saving it on the server means something like session but this feels a little heavy to me. You could add it to your ViewModel as #Ryan Spears suggested. To me that feels a little wrong, polluting the ViewModel with something that might be considered metadata. But that is just an opinion and I am not discrediting his answer because it is valid. Another possibility would be to just add the extra fields to the parameter list of the action method itself and use hidden fields.
[AcceptVerbs("POST")]
public ActionResult Save(ManageUserViewModel uvm, string title, string onSubmit)
{
...
}
In the form add:
<input type="hidden" name="title" value="#ViewBag.Title" />
<input type="hidden" name="onSubmit" value="#ViewBag.OnSubmit" />
This is essentially the same concept and solution as adding them to the ViewModel except in this situation they are not actually part of the ViewModel.
You can use RedirectToAction() and then export and import your tempdata (to maintain the ModelState) if you're worried about the 3 lines.
Personally I'd find it a lot more readable if you kept the logic in the POST version of the method, as you're performing something slightly different from the GET method, therefore not really repeating yourself. You could you probably keep the two ViewBag variables you have inside the View, and then there's no repetition at all.
As a side note: [HttpPost] now supersedes [AcceptVerbs]
We have come up with another solution that I thought I would share.
This based on the view-model containing info on what actions it can do, but we feel the controller should be specifying these (ie, controlling what actions different links route to) these because we have cases where the view-models are reused across actions. EG, the case where when you edit you can edit a template or an instance of something - the UI is the same, the only difference is the actions you post to/cancel from.
We abstracted away the part of the view-model that contains the data bound properties and the view model that contains other things we need for the view to render. We call the property-only object a DTO - it's not a true dto because it contains validation attributes.
We figure that we might be able to re-use these DTO's in the future for ajax or even XML requests - it, can keep validation DRY.
Anyway - here is an example of the code, we are happy with it (for now) and hope it helps others.
[HttpGet]
[ValidateInput(false)]
public virtual ActionResult ManageUser(ManageUserDTO dto, bool PopulateFromObject = true)
{
User user = this._UserLogic.GetUser(dto.UserID);
if (PopulateFromObject)
Mapper.Map<User, ManageUserDTO>(user, dto);
ManageUserViewModel vm = new ManageUserViewModel()
{
DTO = dto,
PageTitle = Captions.GetCaption("pageTitle_EditUser"),
OnSubmit = GetSubmitEventData(this.ControllerName, "SaveUser"),
OnCancel = GetCancelEventData(this.ControllerName, "ListUsers"),
};
return View("ManageUser", vm);
}
[HttpPost]
public virtual ActionResult SaveUser(ManageUserViewModel vm)
{
User user = this._UserLogic.GetUser(vm.DTO.UserID);
if (!ModelState.IsValid)
{
return ManageUser(vm.DTO, false);
}
Mapper.Map<ManageUserDTO, User>(vm.DTO, user);
this._UserLogic.SaveUser(user);
TempData.AddSuccess(Captions.GetCaption("message_UserSavedSuccessfuly"));
return RedirectToAction("ManageUser", new { UserID = user.ID });
}
The model-binder will set any URI variables into the dto in the get action. My logic layer will return a new User object if a call to getUserByID(null) is made.

Unwrapping breeze Entity properties

I'm very new to breeze/knockout, but I'm 99% of the way to doing what I need to do. I'm using the Hot Towel template, and I'm successfully retrieving a list of items via breeze. The entity (ITBAL) is a database first Entity Framework entity. When I look at the JSON coming back in Fiddler, I see the correct data. The problem is that all of the properties of data.results are dependentobservables, and not the raw values themselves.
We have a custom grid control that is trying to display the data.results array. Because it is not expecting observables, it is simply displaying "function dependentobservable" instead of the value.
I tried to unwrap the object, but keep getting the circular reference error. I don't know why that is, since ITBAL isn't associated with anything.
The data as Fiddler reports it:
[{"$id":"1","$type":"WebUIHtml5HotTowel.Models.ITBAL, WebUIHtml5HotTowel","IBITNO":"A100 ","IBWHID":"1 ","IBITCL":"50","IBITSC":"3 ","IBSUSP":" ","IBVNNO":"100 ","IBPRLC":" ","IBSCLC":" ","IBCCCD":"P","IBPICD":" ","IBSAFL":"Y","IBSTCS":399.99000,"IBUSCS":0.00000,"IBAVCS":414.95214,"IBLCST":7.00000,"IBLCCC":20.0,"IBLCDT":110923.0,"IBLSCC":20.0,"IBLSDT":130111.0,"IBLXCC":19.0,"IBLXDT":990102.0,"IBMXO1":2100.000,"IBMXO2":0.000,"IBMXO3":0.000,"IBMNO1":5.000,"IBMNO2":0.000,"IBMNO3":0.000,"IBFOQ1":0.000,"IBFOQ2":0.000,"IBFOQ3":0.000,"IBOHQ1":327.000,"IBOHQ2":0.000,"IBOHQ3":0.000,"IBAQT1":1576.000,"IBAQT2":0.000,"IBAQT3":0.000,"IBBOQ1":50.000,"IBBOQ2":0.000,"IBBOQ3":0.000,"IBPOQ1":448.000,"IBPOQ2":0.000,"IBPOQ3":0.000,"IBIQT1":1446.000,"IBIQT2":0.000,"IBIQT3":0.000,"IBRMD1":10.000,"IBRMD2":0.000,"IBRMD3":0.000,"IBRYD1":10.000,"IBRYD2":0.000,"IBRYD3":0.000,"IBISM1":0.000,"IBISM2":0.000,"IBISM3":0.000,"IBISY1":0.000,"IBISY2":0.000,"IBISY3":0.000,"IBAMD1":0.000,"IBAMD2":0.000,"IBAMD3":0.000,"IBAYD1":0.000,"IBAYD2":0.000,"IBAYD3":0.000,"IBMMD1":0.000,"IBMMD2":0.000,"IBMMD3":0.000,"IBMYD1":0.000,"IBMYD2":0.000,"IBMYD3":0.000,"IBSMD1":1.0,"IBSMD2":0.0,"IBSMD3":0.0,"IBSYD1":1.0,"IBSYD2":0.0,"IBSYD3":0.0,"IBBLME":335.000,"IBBLYO":2680.000,"IBBLLY":1441.000,"IBNMTY":8.0,"IBNMLY":11.0,"IBQSMD":21.000,"IBQSYD":21.000,"IBQSLY":20.000,"IBISMD":16318.19,"IBISYD":16318.19,"IBISLY":45714.87,"IBCSMD":373.46,"IBCSYD":373.46,"IBCSLY":67.00,"IBDQMD":0.000,"IBDQYD":0.000,"IBDQLY":0.000,"IBDSMD":0.00,"IBDSYD":0.00,"IBDSLY":0.00,"IBDCMD":0.00,"IBDCYD":0.00,"IBDCLY":0.00,"IBNOMD":18.0,"IBNOYD":18.0,"IBNOLY":18.0,"IBPKMD":15.0,"IBPKYD":15.0,"IBPKLY":14.0,"IBINUS":" ","IBIAID":0.0,"IBSAID":0.0,"IBCQT1":1527.000,"IBCQT2":0.000,"IBCQT3":0.000,"IBFCST":"Y","IBDRSH":" ","IBWMIU":"JP","IBFL15":" ","IBUS20":" ","IBLPR1":0.00000,"IBLPR2":0.00000,"IBLPR3":0.00000,"IBLPR4":0.00000,"IBLPR5":0.00000,"IBLPCD":" ","IBABCC":"B","IBPRCL":0.0,"IBQBCL":" ","IBACDC":"Y","IBTDCD":" ","IBDOUM":" ","IBTP01":0.0,"IBTP02":0.0,"IBTP03":0.0,"IBTP04":0.0,"IBLMCC":20.0,"IBLMDT":130513.0,"IBTMPH":"Y","IBCOMC":" ","IBCOMF":0.00000,"IBITCT":" ","IBEOQT":0.000,"IBITCM":0.0,"IBBRVW":" ","IBPTID":" ","IBQTLT":0.0000,"IBCTY1":"AUS","IBCTY2":"AUS","IBTXCD":"1","IBREVS":"Y","IBITXC":" ","IBMNOQ":0.000,"IBSTUS":0.000,"IBUS30":" ","IBPSLN":" ","IBPLIN":"N","IBUPDP":"Y","IBDFII":"2011-08-11T00:00:00.000","IBLHRK":"A","IBPLNC":" "}]
My Controller:
[BreezeController]
public class ItemInquiryController : ApiController
{
readonly EFContextProvider<AplusEntities> _contextProvider = new EFContextProvider<AplusEntities>();
[System.Web.Http.HttpGet]
public string Metadata()
{
return _contextProvider.Metadata();
}
[HttpGet]
public IQueryable<ITBAL> ItemBalances(string itemNumber, string warehouse)
{
return _contextProvider.Context.ITBALs.Where(i => i.IBITNO == itemNumber && i.IBWHID == warehouse)
.OrderBy(i => i.IBWHID)
.ThenBy(i => i.IBITNO);
}
}
The relevant portion from the viewmodel:
var manager = new breeze.EntityManager("api/ItemInquiry");
var store = manager.metadataStore;
var itbalInitializer = function (itbal) {
itbal.CompositeKey = ko.computed(function () {
return itbal.IBITNO() + itbal.IBWHID();
});
};
store.registerEntityTypeCtor("ITBAL", null, itbalInitializer);
var index = "0" + (args.pageNum * args.pageSize);
var query = new breeze.EntityQuery("ItemBalances")
.withParameters({ itemNumber: "A100", warehouse: "1" })
.take(args.pageSize);
if (index > 0) {
query = query.skip(index);
}
manager.executeQuery(query).then(function (data) {
vm.itbals.removeAll();
var itbals = data.results;//[0].Data;
itbals.forEach(function (itbal) {
vm.itbals.push(itbal);
});
vm.totalRecords(1);
itemBalancesGrid.mergeData(vm.itbals(), args.pageNum, parseInt(vm.totalRecords()));
}).fail(function (e) {
logger.log(e, null, loggerSource, true, 'error');
});
I figure I must be missing something fairly simple, but it is escaping me.
UPDATE: I removed the BreezeController attribute from the ApiController, and it works correctly.
Jon, removing the [Breeze] attribute effectively disables breeze for your application so I don't think that is the long term answer to your problem.
If you don't actually want entities for this scenario - you just want data - than a Breeze projection that mentions just the data to display in the grid would seem to be the best choice. Projections return raw data that are not wrapped in KO observables and are not held in the Breeze EntityManager cache.
If you want the data as cached entities and you also want to display them in a grid that doesn't like KO observable properties ... read on.
You can unwrap a KO'd object with ko.toJS. However, the grid is likely to complain about circular references (or throw an "out of memory" exception as some grids do) ... even if the entity has no circular navigation paths. The difficulty stems from the fact that every Breeze entity has an inherent circularity by way of its entityAspect property:
something.entityAspect.entity //'entity' points back to 'something'
Because you are using Knockout for your model library and because you say ITBAL has no navigation properties ("is not related to anything"), I think the following will work for you:
manager.executeQuery(query).then(success) ...
function success(data) {
var unwrapped = ko.toJS(data.results).map(
function(entity) {
delete entity.entityAspect;
return entity;
});
vm.itbals(unwrapped);
vm.totalRecords(1); // huh? What is that "parseInt ..." stuff?
itemBalancesGrid.mergeData(vm.itbals(), args.pageNum, parseInt(vm.totalRecords()));
})
ko.toJS is a Knockout function that recursively unwraps an object or collection of objects, returning copies of values. Then we iterate over the copied object graphs, deleting their entityAspect properties. The array of results is stuffed into the vm.itbals observable and handed along.
You can imagine how to generalize this to remove anything that is giving you trouble.
Extra
What the heck is vm.totalRecords? I sense that this is supposed to be the total number of matching records before paging. You can get that from Breeze by adding .inlineCount() to the breeze query definition. You get the value after the query returns from the data.inlineCount property.
Do you really need vm.itbals()? If all you do here is pass values to the grid, why not do that and cut out the middle man?
The following success callback combines these thoughts
function success(data) {
var unwrapped = ko.toJS(data.results).map(
function(entity) {
delete entity.entityAspect;
return entity;
});
itemBalancesGrid.mergeData(unwrapped, args.pageNum, data.inlineCount);
})

An interesting Restlet Attribute behavior

Using Restlet 2.1 for Java EE, I am discovering an interesting problem with its ability to handle attributes.
Suppose you have code like the following:
cmp.getDefaultHost().attach("/testpath/{attr}",SomeServerResource.class);
and on your browser you provide the following URL:
http://localhost:8100/testpath/command
then, of course, the attr attribute gets set to "command".
Unfortunately, suppose you want the attribute to be something like command/test, as in the following URL:
http://localhost:8100/testpath/command/test
or if you want to dynamically add things with different levels, like:
http://localhost:800/testpath/command/test/subsystems/network/security
in both cases the attr attribute is still set to "command"!
Is there some way in a restlet application to make an attribute that can retain the "slash", so that one can, for example, make the attr attribute be set to "command/test"? I would like to be able to just grab everything after testpath and have the entire string be the attribute.
Is this possible? Someone please advise.
For the same case I usually change the type of the variable :
Route route = cmp.getDefaultHost().attach("/testpath/{attr}",SomeServerResource.class);
route.getTemplate().getVariables().get("attr") = new Variable(Variable.TYPE_URI_PATH);
You can do this by using url encoding.
I made the following attachment in my router:
router.attach("/test/{cmd}", TestResource.class);
My test resource class looks like this, with a little help from Apache Commons Codec URLCodec
#Override
protected Representation get() {
try {
String raw = ResourceWrapper.get(this, "cmd");
String decoded = new String(URLCodec.decodeUrl(raw.getBytes()));
return ResourceWrapper.wrap(raw + " " + decoded);
} catch(Exception e) { throw new RuntimeException(e); }
}
Note my resource wrapper class is simply utility methods. The get returns the string of the url param, and the wrap returns a StringRepresentation.
Now if I do something like this:
http://127.0.0.1/test/haha/awesome
I get a 404.
Instead, I do this:
http://127.0.0.1/test/haha%2fawesome
I have URLEncoded the folder path. This results in my browser saying:
haha%2fawesome haha/awesome
The first is the raw string, the second is the result. I don't know if this is suitable for your needs as it's a simplistic example, but as long as you URLEncode your attribute, you can decode it on the other end.

Translation of Zend_Validation in ZF2

I have a strange Problem in Zend Framework 2. I've used the Zend Skeleton Application (https://github.com/zendframework/ZendSkeletonApplication) and added PhlyContact as Vendor Module (https://github.com/weierophinney/PhlyContact). I changed the Translation-Type to PhpArray so that i can use the Zend_Validate.php located in the resources-dir of the ZF2-Dist.
Everything translates EXCEPT the validation Messages :/ So i guess i am missing something:
I must pass the Translator to Zend_Validate (but how and where?)
The Translation should use a Text-Domain, but doesn't
When i remember right in ZF1 you had to set the Translator to default to pass it to Zend_Validate. Any Ideas on that !?
have a look at these methods
\Zend\Validator\AbstractValidator::setDefaultTranslator();
\Zend\Validator\AbstractValidator::setDefaultTranslatorTextDomain();
You can even do this with only one line (2nd parameter is text domain):
AbstractValidator::setDefaultTranslator($translator, 'default');
Example within Module.php:
use Zend\Validator\AbstractValidator;
class Module
{
public function onBootstrap(MvcEvent $e)
{
$translator = ....
AbstractValidator::setDefaultTranslator($translator, 'default');
}
}