Polymer: What is the data-binding syntax attribute-name\$ for? - data-binding

I have come across some syntax I don't understand. The docs for data binding in Polymer explain 2 different syntaxes for data bindings:
One for binding to properties:
prop-name=...
And one for binding to attributes (e.g. href, src, style etc.):
attr-name$=...
Trying to learn from source code of existing components I have come accross the construct name\$=... and I haven't found any documentation on what that is, so far.
In the source for <paper-input> there is e.g. the following data binding for the hidden attribute on a label element:
<label hidden\$="[[!label]]" ... >[[label]]</label>
hidden is not even on the list of properties, that require attribute binding (although that list may be incomplete). So I'm feeling really stuck (and confused) here.
Why is this hidden\$=... (i.e. with a backslash) and not just hidden$=..." (without the backslash) or hidden=... (without backslash and without dollar sign)? What does this syntax do? What is it used for?

A data binding appears in the local DOM template as an HTML attribute:
property-name=annotation-or-compound-binding
attribute-name$=annotation-or-compound-binding
The left-hand side of the binding identifies the target property or attribute.
To bind to a property, use the property name in attribute form (dash-case not camelCase), as described in Property name to attribute name mapping:
<my-element my-property="{{hostProperty}}">
This example binds to the target property, myProperty on .
To bind to an attribute instead, use the attribute name followed by $:
<a href$="{{hostProperty}}">
This example binds to the anchor element's href attribute.
https://www.polymer-project.org/3.0/docs/devguide/data-binding

Related

Why can you set a lit public property 'attribute' option to false?

From the Lit documentation: "The component shouldn't change its own public properties, except in response to user input."
Also from the documentation: "Internal reactive state works just like public reactive properties, except that there is no attribute associated with the property."
However, when you declare a property, there is an option of setting attribute to false, which prevents an attribute from being associated with the property.
#property({attribute: false})
data = {};
What would be the purpose of doing this? Wouldn't the property just act like internal state at that point?
For reference, Lit already has several ways of declaring internal state variables, either with the #state decorator or setting the state option to true, so I'm just not sure why they allow this too.
I think the main use case for this is for when you have to pass big complex data to the component but want it to be set directly as a property and still get lit to rerender stuff for you.
I think this is easier to visualize with an example, let's say you're making a component which will render a list out of an array passed as a property.
If the array was set as an attribute, it would look something like this:
<list-renderer items='[{id: "1", name: "John Doe"}, {id: "2", name: "Alice Williams"}]'></list-renderer>
Now, this example only has two items, but it could be something way bigger, and that attribute will eventually need to be serialized into an array using JSON.parse() by lit. So, you're just doing an extra step, especially if you already had the array as a JS object rather than JSON data.
So, for this kind of cases it's easier to just force users to set items as a JS property directly.
This will also apply for when you need to pass complex configuration setting objects or functions to the component.
Then again, for most of the components you'll be making, you will probably stick with either having the attribute or making it a fully internal state property.
This way you are really free to use any combination.
A property which acts also as state and attribute
A property which acts also as state but not as an attribute
A state, which is not a property
A property, which is an attribute
A property which is not an attribute
see also https://javascript.info/dom-attributes-and-properties for the difference between properties and attributes.

How to use Humanize inside #Html,DisplayFor()

How can I display Date Humanizely inside DisplayFor?
I tried like this : #Html.DisplayFor(model =>model.EndDate.Humanize())
but editor shows error like:
an expression tree may not contain a call or invocation that uses
optional arguments
Html.DisplayFor is a templated helper, which just means it returns the value of the model expression using a template. The default template is simply calling ToString() on the property referenced by the model expression. As such, what you pass into it must be a model expression referencing a particular property; you cannot do something like call Humanize on it.
However, you can define your own template. This entails creating a view in Views\Shared\DisplayTemplates, which conforms to one of these naming conventions:
It's named after the type it should be used for, e.g. DateTime.cshtml will be applied to any property that is of type DateTime.
It's named after one of the members of the DataType enum, in which case, it's utilized when that DataType is applied to a particular property, e.g. Date.cshtml will be utilized when you apply the attribute [DataType(DataType.Date)].
It's named whatever you like, but that name is explicitly specified for the property using the UIHint attribute, e.g. if you apply an attribute like [UIHint("MyAwesomeDateTime")] and associated MyAwesomeDateTime.cshtml view will be used.
As such, if you were to create a view like Views\Shared\DisplayTemplates\DateTime.cshtml with the contents:
#model DateTime
#Model.Humanize()
Then, simply calling #Html.DisplayFor(m => m.EndDate) will return the humanized date you're looking for. Since EndDate is a DateTime, DisplayFor will utilize your DateTime.cshtml template, which outputs the model (your DateTime with Humanized chained.
An alternative way to specify a display template is to pass it in directly. This is useful when you have a one-off situation and you don't want the template to apply to everything of a particular type. For example, if you just wanted EndDate humanized, but other dates to display as normal, that could be achieved by doing something like:
#Html.DisplayFor(m => m.EndDate, "HumanizedDate")
You would then of course need an associated HumanizedDate.cshtml. You can also employ UIHint on your property as described above here, if you'd prefer to keep this out of the view, and on your model instead.
All that said, display templates are best utilized when there's complex constructions involved. As Stephen pointed out in the comments below your question, you could easily just do: #Model.EndDate.Humanize(), which not only is more terse and explicit than #Html.DisplayFor(m => m.EndDate), but also doesn't require adding custom views or attributes to your model properties.

ASP.NET Razor Model syntax in the view

a few newbie questions here:
In a strongly typed view, why:
#model MyProj.Models.User
Why do we use lambdas: ? What exactly does that do? Why not just model.Email?
1. #Html.DisplayNameFor(model => model.Email)
Why doesn't this work? Didn't we call the variable model? (I know I should use #html.() but why isn't model recognisable?
2. users name is: #model.Name
Why does it work with an uppercase 'M'? Didnt we name it with a lower case?
3. users name is: Model.Name
Thanks!
Why do we use lambdas: ? What exactly does that do? Why not just
model.Email?
The DisplayNameFor as well as all the other *For helper methods are taking advantage of the ability of lambda expressions to participate in Expression Trees.
By itself this lambda expression is simply taking in a model type and returning a String. But there is more information needed to get the display name.
In pseudo code, that method is doing:
1.) Treat the lambda as an Expression
2.) Parse the expression to get the name of the property
3.) Use reflection to get the DisplayNameAttribute for that property
4.) Extract the value if it exists, otherwise use the name of the property
5.) Generate a label for that property using either the display name, or property name
Without using Expression trees, you would end up having to pass the property name in as a string... and that just sucks. This provides a strongly typed way of using reflection without magic strings.
Why doesn't this work? Didn't we call the variable model? (I know I
should use #html.() but why isn't model recognisable?
#model is a special directive that is treated differently by Razor. It's like a reserved keyword that isn't any different than class or int.
Why does it work with an uppercase 'M'? Didnt we name it with a lower
case?
Model is a property on your view that has the type you defined using the #model directive, and references your model. Without using the #model directive it would have a type of dynamic.

ASP.NET: Can you use server tags to embed C# or VB.NET expressions into a Javascript function?

I have an enum called SiteTypes that contains several values that are all bound to a dropdown list. On the client side, I need to check this dropdown to see if the selected value is one of those enum values. I don't want to hardcode the value of the enum in the script in case it needs to change, so I want to use a server tag to get it directly from the enum itself. Conecptually, I would like to do this:
function SiteIdChanged() {
var x = "<%=SiteTypes.Employee %>";
}
The way I am doing it now is created a protected property in the codebehind that returns that specific enum value and am doing this:
function SiteIdChanged() {
var x = "<%=EmployeeSiteTypeValue %>";
}
I don't like that, though, because I have to create a special property on every page that I need to do such a check.
Is there a way to do what I want here?
Are you getting a "xxx is inaccessible due to its protection level" error when you compile or run the page? enums are public by default, classes are not. My guess is that you've defined your enum inside your page's class and you aren't explicitly marking it with the 'public' access modifier. Explicitly mark it as public or move it outside of the class and see what happens. If you're planning on using it on lots of pages you should stick the enum definition in in a file in the App_Code folder of your project.
If you don't like your current implementation I would consider using a PageMethod to compare the dropdown selection to the enum value. This approach will probably be cleaner, as you can do most of the logic server-side.
Here's a tutorial on PageMethods:
http://blogs.microsoft.co.il/blogs/gilf/archive/2008/10/04/asp-net-ajax-pagemethods.aspx
As long as your enum is marked public, you can just go with your first option. There's no need to put a property on every single page you want to retrieve the value from.
That approach is really the simplest solution for writing out server side values in your JavaScript.
You can use the Enum.IsDefined Method this well tell you if the selected value from the dropdown is actually part of your enum.
Enum.IsDefined(typeof(MyEnum), myValue)
http://msdn.microsoft.com/en-us/library/system.enum.isdefined.aspx

Spring MVC checkbox tag bound to collection expects object but validation expects object.id

In my Spring MVC project I have an update page for Class1 that must display a list of form:checkbox tags that is bound to a collection of entities on Class1.
Class1.java:
class Class1 {
private Set<Class2> set;
//... other fields
}
In updateclass1.jspx:
<c:forEach items="${allClass2Instances}" var="class2">
<form:checkbox label="${class2.name}" path="set" value="${class2}"/><br/>
</c:forEach>
With the checkbox tag as above, when I display the page, the checkbox is ticked if the Class2 instance is part of the Set on class1, and unticked if it isn't. But when I hit submit, I get the following error:
Failed to convert property value of type 'java.lang.String[]' to required type 'java.util.Set' for property 'set'; nested exception is org.springframework.core.convert.ConversionFailedException: Unable to convert value "Name 1" from type 'java.lang.String' to type 'java.lang.Long'; nested exception is java.lang.NumberFormatException: For input string: "Name1"
As far as I can tell when the page is populated, the form:checkbox tag needs an instance to set the correct checked/unchecked value, but on submit the JSP is sending an array of class2.toString() values to a converter that expects the IDs. Conversely when I change the tag to the following:
<form:checkbox label="${class2.name}" path="set" value="${class2.id}"/><br/>
The binding works fine, but when I view the update page the checkboxes are not ticked / unticked correctly because the tag does not know that value being passed in is the object id.
How do I make the binding after submit consistent with what the checkbox tag expects?
In case it matters - this is all inside a scaffolding page generated by Roo.
Figured out how to make this work for now. If anyone comes up with a neater solution please add it and I'll mark it correct instead.
The problem above was being caused by needing ${InstanceOfClass2} to evaluate to different things in different places:
At the time of evaluation of the tag, <form:checkbox> needed an expression that evaluated to an actual instance of Class2
After the tag completed, the value attribute of the generated <input type="checkbox"> tag needs to be equal to a numerical ID field of an instance of Class2
The solution was to add a converter to my Class1Controller, eg:
Converter<Class2, String> getClass2Converter() {
return new Converter<Class2, String>() {
public String convert(Class2 instance) {
return "" + instance.getId();
}
};
}
Thus the expression ${InstanceOfClass2} evaluates to a Class2 instance for the checkbox tag, but when it comes to writing the actual HTML is converted to a numerical ID.
This approach is very messy when working with Roo. All of the other scaffolding relating to Class1 then wants to use this same Converter, so I started seeing a whole bunch of IDs everywhere that you would want to see Class2.name or other such fields. I solved this by modifying the Spring Roo <field:display> custom tag - added an attribute fmtCollectionToString that if present forces the tag to evaluate collections by iterating them and calling toString on each element, instead of calling spring:eval on the whole collection, which also seems to end up with the Converter being invoked.
Like I say, neater solutions greatly appreciated! If there's a way of making converters behave differently in different circumstances, for instance - still want to hear it.

Resources