Thymeleaf custom dialect with fragments: how to keep th: attributes - thymeleaf

I'm creating a custom dialect that replaces <myprefix:mytag> with a <div th:replace="myTagFragment::myfrag"> and it works with no attributes or "standard" attributes.
It stops working if I use th: attributes in the custom tag.
This works:
<myprefix:mytag required id="myid">
This does not work:
<myprefix:mytag th:field="*{name}">
TagProcessor code snippet:
protected void doProcess(ITemplateContext context, IModel model, IElementModelStructureHandler structureHandler) {
final IOpenElementTag customTag = (IOpenElementTag) model.get(0);
//
// I create a comma-separated list of the original attributes and add them in the fragment with th:attr
Map<String, String> attributes = sourceTag.getAttributeMap();
String attributeString = attributes.toString(); // "{attr1=val1, attr2=val2}"
attributeString = StringUtils.chop(attributeString); // Remove "}"
attributeString = StringUtils.removeStart(attributeString, "{"); // Remove "{"
structureHandler.setLocalVariable("attributeString", attributeString); // "attr1=val1, attr2=val2"
//
Map<String, String> newtagAttributes = new HashMap<>();
newtagAttributes.put("th:replace", "myTagFragment::myfrag");
IOpenElementTag replacementTag = modelFactory.createOpenElementTag("div", newtagAttributes, AttributeValueQuotes.DOUBLE, false);
model.replace(0, replacementTag);
Fragment snippet from "myTagFragment.html":
<th:block th:fragment="myfrag">
<div>somestuffhere</div>
<input th:attr="__${attributeString}__">
When I use
<myprefix:mytag type="password" maxlength="70" id="titolo">
I correctly get the replaced fragment:
<div>somestuffhere</div>
<input type="password" maxlength="70" id="titolo">
but if I want to use th: attributes in the original tag, they fail because something like
th:field is not processed properly when added with th:attr="__${attributeString}__" of course.
Maybe there is a way to retrieve the fragment model from inside the TagProcessor and add the th: attributes by code?
Any other idea to replace a custom tag with a template fragment while keeping the original attributes and have them processed when needed?

The (partial) solution I found so far is to process th: attributes myself in the TagProcessor: most of them just need to be replaced with the equivalent plain version after evaluation, some others need a special trearment, some more I just ignore.
(pseudo)Code snippet:
Map<String, String> attributes = sourceTag.getAttributeMap();
Map<String, String> targetAttributes = new HashMap<>();
for (Map.Entry<String,String> sourceAttribute : attributes.entrySet()) {
String attributeName = sourceAttribute.getKey();
String attributeValue = sourceAttribute.getValue();
if (attributeName.startsWith("th:") && !attributeName.endsWith("append")) {
String thAttributeName = ... remove "th:" from the name
String parsedValue = null;
if (!"attr".equals(thAttributeName)) {
try {
final IStandardExpression expression = parser.parseExpression(context, attributeValue);
parsedValue = (String) expression.execute(context);
} catch (Exception e) {
log.debug("Can't parse \"{}\" for tag \"{}\" - skipping", attributeValue, attributeName);
continue; // Next attribute
}
switch (thAttributeName) {
case "field":
targetAttributes.put("name", parsedValue);
targetAttributes.put("value", parsedValue);
break;
... also handle "attr", "alt-title", "lang-xmllang"
default:
targetAttributes.put(thAttributeName, parsedValue);
} else {
// Plain attributes with no "th:"
targetAttributes.put(attributeName, attributeValue);
}
}
... repeat the loop to handle "append" and "prepend" attributes here
String attributeString = targetAttributes.toString();
... same cose as in the question follows
The problem with this approach is that all conditional and looping th tags are lost, so I can't use th:each on my custom tag for example.

Another solution is to give a higher precedence to my custom dialect so that all th: attributes are executed before my tag processor.
This way I can use th:each and other stuff on my custom tag (see discussion here).
The only problem seems to be with th:field because it checks the tag before adding name/value attributes and skips them on any custom tag.
public class YadaDialect extends AbstractProcessorDialect {
public YadaDialect() {
super("Yada Dialect", "yada", StandardDialect.PROCESSOR_PRECEDENCE+1);
}

Related

changing Html.Raw into string without html markup in a razor view

Quite simple question. I have the following code
#Html.Raw(following.Description).ToString()
when this comes from database it has some markup in it (its a forum post but i want to show a snippet in the list without the markup
is there any way to remove this and replace this line or shall I just regex it from the controller?
Here is a utility class extension method that is able to strip tags from fragments without using Regex:
public static string StripTags(this string markup)
{
try
{
StringReader sr = new StringReader(markup);
XPathDocument doc;
using (XmlReader xr = XmlReader.Create(sr,
new XmlReaderSettings()
{
ConformanceLevel = ConformanceLevel.Fragment
// for multiple roots
}))
{
doc = new XPathDocument(xr);
}
return doc.CreateNavigator().Value; // .Value is similar to .InnerText of
// XmlDocument or JavaScript's innerText
}
catch
{
return string.Empty;
}
}

Validation of fields on indexers

I'm creating a new message, by setting the indexers, like:
Iso8583 isoMsg = new Iso8583();
isoMsg[field] = value;
I noticed that I'm not receiving any exceptions; following the code I've seen that the validator is not running when I'm setting the fields this way; it only executes when unpacking a byte[] message. Do you think it would be possible to adapt the format and length validators to run also when setting a field?
Thanks in advance!
The validators are run on the fields when you call .Pack() on the message.
I guess you just set the value to one of the existing fields form the default template
When you create Iso8583() it uses the DefaultTemplate, which adds the set of default fields into the message instance on creation.
Indexer property is derived from AMessage class, which is Iso8583 class is inherited from.
public string this[int field]
{
get { return this.GetFieldValue(field); }
set { this.SetFieldValue(field, value); }
}
These methods:
protected string GetFieldValue(int field)
{
return this.bitmap[field] ? this.fields[field].Value : null;
}
protected void SetFieldValue(int field, string value)
{
if (value == null)
{
this.ClearField(field);
return;
}
this.GetField(field).Value = value;
}
So it seems that your code sets the value for one of the field from the default template
isoMsg[field] = value;

Zend Framework 2, filter a value after validation in an InputFilter

In an InputFilter I have this code:
$password = new Input('password');
$password->setRequired(false)
->getValidatorChain()
->attach(new Validator\StringLength(6));
$password->getFilterChain()
->attach($this->passwordHash);
The problem is that the filter is applying to the value before the validation, so the validator always returns true.
I was wondering if there is any way to do the filtering after the validation.
Zend\InputFilter\FileInput class is one of the input types that does validation before filtering. You can see how they solved it in the source code and make your own PasswordInput class based on that solution.
As you can see in the source code on line 64 there is an if clause that prevents the filters to run as long as isValid is false (the initial value). So the validators run first and then when the parent InputFilter after validation calls getValue again to get the value from FileInput it runs the filters.
It seems odd to filter after validation as the input before validation could be edited by the filters. Like strip tags or trim the string. If you do use a filter like \Zend\I18n\Filter\Alnum() you could let it remove the whitespaces. So see it as bad practice to filter after validating your input.
E.g., take those filters:
// Default settings, deny whitespace
$filter = new \Zend\I18n\Filter\Alnum();
echo $filter->filter("This is (my) content: 123");
// Returns "Thisismycontent123"
// First param in constructor is $allowWhiteSpace
$filter = new \Zend\I18n\Filter\Alnum(true);
echo $filter->filter("This is (my) content: 123");
// Returns "This is my content 123"
Notice that the begin value differs from the result. But you can filter after validation, but I guess you have to it by yourself. So you could add a method to your inputfilter or form which you call after validation. For example:
public function filterPassword() {
$filter = new \Zend\I18n\Filter\Alnum(true);
$inputs = $this->getInputs();
$password = $filter->filter($inputs['password']);
return $password;
}
In your controller you can do this for example:
public function ExampleController extends AbstractActionController
{
public function sampleAction() {
$form = new LoginForm();
if($form->isValid()) {
$inputFilter = $form->getInputFilter();
$filterdPassword = $inputFilter->filterPassword();
}
return array('form' => $form);
}
}

How to send a dictionary parameter to a URL.Action() in asp.net MVC3

Say I have a action method.......
public ActionResult DisplayXml(int viewId, Dictionary<string,string> parameter, string dataFormat )
{
string xml = "";
return Content(xml, "text/xml");
}
and in view I did.......
<iframe title="Xml" class="ResultDisplay"
src = "#Url.Action("DisplayXml", "OutputData", new {viewId = Model.ViewId, parameter = Model.Parameter, dataFormat = Model.DataFormat })">
</iframe>
Here parameter is a dictionary and I am getting null.
How I can send it?????
You're trying to pass and arbitrary dictionary as a parameter in a querystring?
Its a pretty unusual requirement to need to serialize the contents of a dictionary to a query string parameter and back again. When generating querystring parameters, MVC will just call .ToString() on the values, and the Dictionary<,> object just uses the default implementation (which returns it's type)
Since this requirement is so uncommon, there's nothing built in to do this.. You can quite easily serialize the dictionary yourself to a string (perhaps json?) and then, change the parameter variable in your action to be a string. You'll have to deserialize the value back to a dictionary after that.
Before I provide much more of an example, I want to check you're absolutely sure this is what you want to do
Update:
Here is way of doing that (requires json.net):
public ActionResult DisplayXml(int viewId, string parameterJson, string dataFormat )
{
var parameter = JsonConvert.DeserializeObject<Dictionary<string,string>>(parameterJson);
string xml = "";
return Content(xml, "text/xml");
}
And use:
<iframe title="Xml" class="ResultDisplay"
src = "#Url.Action("DisplayXml", "OutputData", new {viewId = Model.ViewId, parameterJson = Newtonsoft.Json.JsonConvert.SerializeObject(Model.Parameter), dataFormat = Model.DataFormat })">
</iframe>

Struts2 using Map in select tag

You can easily use a List in struts2 select tag, but is there a way to use Map in tag?? If it is possible please provide a sample code...
thanx !
In my action class
public class MyAction extends ActionSupport {
private Map<String, String> map;
public String execute() throws Exception {
map = new HashMap<String, String>();
map.put("abc", "abc");
map.put("xyz", "xyz");
return SUCCESS;
}
}
For the jsp mapped to success, use some thing like this
<s:select list = "map" name = "name" label = "Name" headerKey="" headerValue = "Enter Value"/>
It depends on what are you trying to do. Lacking details, I can only point you to the docs : the list attribute of the select tag is an ...
Iterable source to populate from. If
the list is a Map (key, value), the
Map key will become the option 'value'
parameter and the Map value will
become the option body.
Below in the same doc there is an example with a (literal, inline) map (Months).

Resources