I'm wondering if anybody has any views or knowledge on where the future of TagHelpers & Razor Components lay. My question is only with regard to initial rendering. Not using C# code on the client-side.
I love the razor component model of working in the sense that the basic component is laid out in markup, then there is code behind.
This being compared to TagHelpers generating the markup.
Take this TagHelper label component I use for example...
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace gMIS.TagHelpers
{
[HtmlTargetElement("glabel", TagStructure = TagStructure.WithoutEndTag)]
public class glabelTagHelper : TagHelper
{
#region Attributes
[HtmlAttributeName("id")]
public string id { get; set; }
[HtmlAttributeName("class")]
public string cls { get; set; }
[HtmlAttributeName("xy")]
public string xy { get; set; }
[HtmlAttributeName("style")]
public string style { get; set; }
[HtmlAttributeName("value")]
public string value { get; set; }
#endregion Attributes
public override void Process(TagHelperContext context, TagHelperOutput output)
{
//START with the container DIV
output.TagName = "div";
output.TagMode = TagMode.StartTagAndEndTag;
if (id != null) output.Attributes.Add("id", id);
if ((xy != null) && (xy != null)) output.Attributes.Add("class", "xy" + xy);
if (style != null) output.Attributes.Add("style", "display:inline-block; font-weight:bold;" + style); //*** Any subsequent WIDTH passed in with supercede the default provided here!
else output.Attributes.Add("style", "display:inline-block; font-weight:bold;");
if (cls != null) output.Attributes.Add("class", cls);
//Insert data value
if (value != null) output.Content.AppendHtml(value);
}
}
}
I would love it if this could be coded something like...
<div id="{inject id}" class="{inject class}" style="{inject style}">{inject text}</div>
...rather than using C# to create the basic markup.
output.TagName = "div";
output.TagMode = TagMode.StartTagAndEndTag;
Then the code behind interacts (only server-side) to finish the configuration of the basic component markup. That way, you can see the basic markup rather than the more difficult to read and visualize C# construction of the markup.
With a basic label this seems like not much of a gain, but with more complex components, being able to visualize the markup would be a real boon.
<div class="{inject class}" id="{inject id}" style="{inject style}">
<input name="{inject name}" id="{inject id}" style="{inject style}" type="text" value="-4" data-val-required=" Req!" data-val="true">
<input id="{inject id}" style="{inject style}" type="text" placeholder="Start typing here to search..." autocomplete="off">
<select id="{inject id}" style="{inject style}" size="1">
</select>
<script type="text/javascript">
$(document).ready(function () { $('#ext_IT_Task_Reported_By_ID').glookupInit('/TagHelpers/InternalContactLookup'); });</script>
<span class="field-validation-valid" data-valmsg-replace="true" data-valmsg-for="{inject id}"></span>
</div>
Does anybody know where Taghelpers is heading the longer term? Towards the Razor Component way of construction perhaps. Apologies if my post isn't clear enough. I have thought hard about how to communicate my question. I do hope it's not confusing.
Not sure exactly what you're asking here, but I think you're not understanding how tag helpers work. You don't have to add each attribute manually to the output and you certainly don't need backing properties if you don't actually need the value for the purposes of the tag helper. Razor leaves any additional attributes unmolested.
[HtmlTargetElement("glabel")]
public class glabelTagHelper : TagHelper
{
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "div";
output.TagMode = TagMode.StartTagAndEndTag;
var style = "display:inline-block; font-weight:bold";
if (output.Attributes.TryGetAttribute("style", out var attr))
{
style += $";{attr.Value}";
}
output.Attributes.SetAttribute("style", style);
}
}
Based on your code, the only thing you're modifying is the style attribute, so everything else, including the content, can and will just come over as-is. Then, for the style, you just need to see if the attribute already exists, and if so, append that to your custom style. With just the code above:
<glabel id="foo" class="foo" style="width:100px">
<!-- child content here -->
</glabel>
Will result in:
<div id="foo" class="foo" style="display:inline-block;font-weight:bold;width:100px">
<!-- child content here -->
</div>
The child elements would need to be their own tag helpers if you need to do special stuff there. A tag helper only operates on the immediate tag itself, not an entire block of HTML. You can get the child content and manually modify it, but you'd be doing so as just a string, having to use stuff like regex. Suffice it to say, that would be less than ideal.
Chris Pratt (How do I reference another user properly?)
Unfortunately, you do misunderstand the question. I'm not having problems creating TagHelpers or understanding how they work. We have some 20-25 TagHelpers that we have developed, ranging from Dynamic lookups to our own fairly extensive and powerful grid. Example of grid usage below...
<ggrid id="grdTaskSearchResults"
url="Task_Search?handler=results&userguid=#Model.UserGUID&manager=#Model.Manager"
sortby="#Model.Opt_SortCol"
sortdir="#Model.Opt_SortDir"
pagesize="100"
style="height:100%; width:100%;">
<cols>
<coltemplate style="white-space:nowrap;">
<a onclick="gNewWindowAndTitle('View IT Call Log', null, null, 'Task_Summary?userguid=#Model.UserGUID&taskId={Task_ID}')" title="View IT Call Log"><img class="smallIcon" src="/Icons/Magnify.gif" /></a>
<gpdialog btnexit="Cancel" btnsubmitandclose="Save" title="Edit Call Log" url="Task_CRUD?userguid=#Model.UserGUID&manager=#Model.Manager&crud=u&taskId={Task_ID}" callback="ggridReload('grdTaskSearchResults')" linkicon="/Icons/Edit.gif" hideif="('#Model.Manager' !== 'true')" suppresstitle="true" />
<gpdialog btnexit="Cancel" btnsubmitandclose="Save" title="Add Note to Call Log" url="Task_Note_CRUD?userguid=#Model.UserGUID&taskId={Task_ID}" callback="ggridReload('grdTaskSearchResults')" linkicon="/Icons/NewAddition.gif" suppresstitle="true" />
<img class='smallIcon' src="/Icons/Exclamation.gif" alt='Not Responded yet!' hideif="{Responded}===true" />
</coltemplate>
<col heading="ID" fld="Task_ID" />
<col heading="Type" fld="Task_Type" style="white-space:nowrap;" />
<col heading="Status" fld="Task_Status" />
<coltemplate heading="Reported By">
<a target="_blank" href="https://gmis.info/entity/entity.asp?ec=1&code={Reported_By_ID}">{Reported_By_Name}</a>
</coltemplate>
<col heading="Date Entered" fld="Date_Entered" style="white-space:nowrap;" format="ddMMMyy" />
<col heading="NAB" fld="NAB_Initials" style="white-space:nowrap;" />
<coltemplate heading="Severity">
<span hideif="{Task_Status_ID}===3" style="color:{Severity_Color}; font-family:Webdings; font-size:10pt;">n</span><span hideif="{Task_Status_ID} == 3"> {Task_Severity}</span>
</coltemplate>
<col heading="Description" fld="Description" />
</cols>
<footer>
<gpdialog btnexit="Cancel" btnsubmitandclose="Save" title="Add New Call Log" url="Task_CRUD?userguid=#Model.UserGUID&manager=#Model.Manager&crud=c" callback="ggridReload('grdTaskSearchResults')" linkicon="/Icons/NewAddition.gif" />
</footer>
</ggrid>
That said, there's a couple of things in your code sample that I would like to look at in my own code. A potential improvement for us I think I can envisage in there. So in that regard, I thank you very much for your input.
No, my question is purely about the next step in the TagHelper story.
HtmlHelpers were written in c# and called in c#.
Taghelpers are written in c# and called in html markup.
Razor Components are written in html markup with some c# code inline/behind. However best implemented in Blazor using html markup. When called from within Razor pages, the call is made in c# code a little like HtmlHelpers were (a backward step for me).
What I would like to see somewhere in this story for the future, is a scenario wherby TagHelpers are written in html markup, with some c# code inline/behind, just like Razor components are now. And in use, the Taghelper called in markup as they are currently. I'm not suggesting that TagHelpers start to use c# on the client side. This is purely about how TagHelpers in their current guise and functionality... are written.
In essence, I am asking if anybody knows what the future holds for in terms of the next step for TagHelpers? Does this make sense?
I think a couple of years on, and I know the answer I was looking for. Moving forward at some point, blazor components are the next thing in line. I'm now considering converting all my taghelpers to blazor components in the future. At the moment, the only thing stopping me is the fact that you have to use some very verbose c# code within a razor page to use a blazor conponent. I'm hanging fire till they inevitably I feel, make it so you can use Html markup to consume blazor components within razor pages.
Related
I created a custom tagHelper.
But I have encountered a problem to develop the code snippet displayed(input asp-for="files" Exactly transmitted to the browser).
public override void Process(TagHelperContext context, TagHelperOutput output){
var form = #"<form id='uploadFileForm' method='post' enctype='multipart/form-data' >
<input asp-for='files' />
<input type = 'button' id ='btnUpload' value =' upload' />
</form > ";
output.PreContent.AppendHtmlLine(form);}
and my model :
[Required(ErrorMessage = "Please select a file.")]
[DataType(DataType.Upload)]
public IList<IFormFile> files { get; set; }
More explanation : When using
<input asp-for='files' />
in a view, the following code is finally generated in
browser:
<input type="file" data-val="true" data-val-required="Please select a file." id="files" multiple="multiple" name="files" />
Now, I do not know what to do in tagHelper to generate the same output in the above line by taghelper.
please help me
Take a look the docu (change the version if its not the right one): https://learn.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/authoring?view=aspnetcore-2.2
Gathering from the example there, what you are looking for should look like this
(untested example):
public override void Process(TagHelperContext context, TagHelperOutput output){
// Should already be input, but in case you want to change it to something else
output.TagName = "input";
// remove the old attribute
// You probably need to do something different if you want to carry over the attribute
output.Attributes.RemoveAll("asp-for");
output.Attributes.SetAttribute("type", "file");
output.Attributes.SetAttribute("data-val", "true");
output.Attributes.SetAttribute("data-val-required", "Please select a file.");
output.Attributes.SetAttribute("id", "files");
output.Attributes.SetAttribute("multiple", "multiple");
output.Attributes.SetAttribute("name", "files");
}
I am not sure whether you actually can use this for a predefined tag (like input), you might be better of if you create a custom one (something that does not exist already).
As you haven`t posted any example for the Annotation you use, lets say you change it to:
<custominput asp-for='files' />
then there should be something like this on you TagHelper Class.
[HtmlTargetElement("custominput", TagStructure = TagStructure.WithoutEndTag)]
The example above ignores your model, so I assume that if you want to use it you need to somehow provide it to your TagHelper Class, and change the SetAttribute lines accordingly.
I am trying to implement jQuery with PrimeFaces and JSF components, but it's not working properly. When I tried to do the same with HTML tags it;s working properly.
Here is the code with HTML tags which works properly with jQuery:
<input type="checkbox" id="check2"></input>
<h:outputText value="Check the box, if your permanent address is as same as current address."></h:outputText>
<h:message for="checkbox" style="color:red" />
with
$("#check2").change(function() {
if ($("#check2").is(":checked")) {
$("#p2").hide();
} else {
$("#p2").show();
}
});
Here is the code with PrimeFaces/JSF which doesn't work properly with jQuery:
<p:selectManyCheckbox >
<f:selectItem itemLabel="1" value="one" id="rad" ></f:selectItem>
</p:selectManyCheckbox>
with
$("#rad").change(function() {
if ($("#rad:checked").val() == "one") {
$("#p2").hide();
} else {
$("#p2").show();
}
});
You should realize that jQuery works with the HTML DOM tree in the client side. jQuery doesn't work directly on JSF components as you've written in the JSF source code, but jQuery works directly with the HTML DOM tree which is generated by those JSF components. You need to open the page in webbrowser and rightclick and then View Source. You'll see that JSF prepends the ID of the generated HTML input elements with the IDs of all parent NamingContainer components (such as <h:form>, <h:dataTable>, etc) with : as default separator character. So for example
<h:form id="foo">
<p:selectManyCheckbox id="bar" />
...
will end up in generated HTML as
<form id="foo" name="foo">
<input type="checkbox" id="foo:bar" name="foo:bar" />
...
You need to select elements by exactly that ID instead. The : is however a special character in CSS identifiers representing a pseudo selector. To select an element with a : in the ID using CSS selectors in jQuery, you need to either escape it by backslash or to use the [id=...] attribute selector or just use the old getElementById():
var $element1 = $("#foo\\:bar");
// or
var $element2 = $("[id='foo:bar']");
// or
var $element3 = $(document.getElementById("foo:bar"));
If you see an autogenerated j_idXXX part in the ID where XXX represents an incremental number, then you must give the particular component a fixed ID, because the incremental number is dynamic and is subject to changes depending on component's physical position in the tree.
As an alternative, you can also just use a class name:
<x:someInputComponent styleClass="someClassName" />
which ends up in HTML as
<input type="..." class="someClassName" />
so that you can get it as
var $elements = $(".someClassName");
This allows for better abstraction and reusability. Surely those kind of elements are not unique. Only the main layout elements like header, menu, content and footer are really unique, but they are in turn usually not in a NamingContainer already.
As again another alternative, you could just pass the HTML DOM element itself into the function:
<x:someComponent onclick="someFunction(this)" />
function someFunction(element) {
var $element = $(element);
// ...
}
See also:
How can I know the id of a JSF component so I can use in Javascript
How to use JSF generated HTML element ID with colon ":" in CSS selectors?
By default, JSF generates unusable IDs, which are incompatible with the CSS part of web standards
Integrate JavaScript in JSF composite component, the clean way
You also can use the jQuery "Attribute Contains Selector" (here is the url http://api.jquery.com/attribute-contains-selector/)
For example If you have a
<p:spinner id="quantity" value="#{toBuyBean.quantityToAdd}" min="0"/>
and you want to do something on its object you can select it with
jQuery('input[id*="quantity"]')
and if you want to print its value you can do this
alert(jQuery('input[id*="quantity"]').val());
In order to know the real html tag of the element you can always look at the real html element (in this case spinner was translated into input) using firebug or ie developer tools or view source...
Daniel.
If you're using RichFaces you can check rich:jQuery comonent. It allows you to specify server side id for jQuery component. For example, you have component with specified server id, then you can apply any jQuery related stuff to in next way:
<rich:jQuery selector="#<server-side-component-id>" query="find('.some-child').removeProp('style')"/>
For more info, please check doumentation.
Hope it helps.
look this will help you when i select experience=Yes my dialoguebox which id is dlg3 is popup.and if value is No it will not open
I am trying to get to grips with Razor, and have hit a basic snag. I generate a small menu based on the users authenticated status. But, I am doing it wrong.
<div>
Home
List
#if (Request.IsAuthenticated)
{
Upload
Logout
}
</div>
It doesn't like the HTML within my {} section. I think it expects an HTML tag... Something like <div>, but because I am using  , it's not happy. How do I do this?
Additionally, I am trying to handle an image tag. But this is failing dismally.
#using GalleryPresentation.Models
#model IndexModel
#{
ViewBag.Title = "Craig and Melanie's Digital Moments";
}
<br/>
<div style="text-align: center">
<img src="#Html.Raw(m => m.RandomImageUrl) />
</div>
where my model is simply:
{
public class IndexModel
{
public string RandomImageUrl { get; set; }
}
}
Razor cannot detect that you are entering HTML mode. Use the special <text> Razor tag to indicate you are in HTML mode.
If you want to display content from HTML, you may use for example #Html.Raw() method. So your code migth be
<div>
Home
List
#if (Request.IsAuthenticated)
{ #Html.Raw("
Upload
Logout ")
}
</div>
nbsp means non breaking space. It is used in html to insert spaces in the middle. Since you are in the middle of a razor code, the compiler doesn't know when you use an html code, at once without a directive. Since it'll give a compile error. This is more like preprocessor directives in c. (if you know #include ). Here you simply have to use the . The correct code will look as follows.
<div>
Home
List
#if (Request.IsAuthenticated)
{
<text>
</text>
Upload
<text>
</text>
Logout
}
</div>
I have the same problem as posted here
I have a <button> element that triggers "A potentially dangerous request.form value..." error in asp.net MVC. For instance:
<button type="submit" name="logon" value="ok">Confirm</button>
<button type="submit" name="cancel" value="ok">Cancel</button>
And this javascript (with jquery UI 1.8.5)
<script type="text/javascript">
$(document).ready(function() {
$("button").button();
});
</script>
The issue is that I can't remove the name property (as the given solution in the link I posted) because I capture which button is pressed in the controller side this way:
public ActionResult Logon(FormCollection form, string logon, string cancel)
{
if (!string.IsNullOrEmpty(logon))
{
DoLogon();
}
if (!string.IsNullOrEmpty(cancel))
{
Cancel();
}
//etc
}
Is there any workaround for this? Thanks. Note that I don't have this problem in IE8 or firefox.
Have you seen this?
Cause
The .NET framework is throwing up an error because it detected something
in the entered text which looks like an HTML statement. The text doesn't
need to contain valid HTML, just anything with opening and closing
angled brackets ("<...>").
The solution proposed there is to disable the request validation on the server-side:
<pages validateRequest="false" />
Be sure to read through the warnings and explanations as well.
I'm working on a demo that makes use of Spring.NET IoC capability in ASP.NET MVC . It's kind of like the MyBlog application presented on pair programming video tutorial on www.asp.net site. I've completed the same demo using Microsoft's Unity framework and now want to try out the Spring container.
To that end I've implemented a simple IControllerFactory that first creates the Spring object factory like that:
IObjectFactory factory;
(....)
factory = new XmlObjectFactory(new FileSystemResource(application.Server.MapPath("objects.xml")))
and next it gets the controller from that factory like that:
public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName) {
IController result = context.GetObject(controllerName) as IController;
return result;
}
(error handling stripped for simplification purposes).
Now somewhere in my HomeController I have this kind of action:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult AddEntry([Bind] BlogEntry entry, int id) {
entry.EntryDate = DateTime.Now;
....
And here's the part of AddEntry.aspx view that defines the editors for entry parameter (really basic stuff):
<form method="post" action="/Home/AddEntry/<%= ViewData["id"] %>">
<table>
<tr>
<td><label for="Title">Title</label></td>
<td><input name="entry.Title" type="text"/></td>
</tr>
<tr>
<td><label for="Text">Content</label></td>
<td><input name="entry.Text" type="text"/></td>
</tr>
</table>
<br />
<input type="submit" value="Add entry" />
<input type="button" value="Cancel" onclick="history.back(-1);" />
</form>
Now here's the deal:
When I'm using the Unity IoC it works like a charm. "entry" parameter gets deserialized from my form like it should and the line
entry.EntryDate = DateTime.Now;
completes without problems.
However when I switch to Spring.NET object factory (like described above) things begin to go nuts. First of all the parameter "entry" turns null so an exception is thrown.
To track the possible problem on my end I've implemented a sort of custom IModelBinder that looks like that:
public class BlogEntryBinder : IModelBinder {
public ModelBinderResult BindModel(ModelBindingContext bindingContext) {
ModelBinderResult result = ModelBinders.DefaultBinder.BindModel(bindingContext);
return result;
}
}
When I come here using the Unity framework and drill down from bindingContext to HttpRequest I see that the Request.HttpMethod is "POST" and Request.Form is properly filled. When I do the same using Spring.NET the method is "GET" and Request.Form is empty.
When however I step to my controller action (AddEntry) and drill down to the Request in both situations I see that the Request.HttpMethod and Request.Form have their proper values.
Now the question is how do I fix the version with Spring.NET so that it works just like the one that uses Unity framework?
I've found the answer!
My object definition looked like that:
<!-- Controlers -->
<object name="Home" type="MyBlog.Controllers.HomeController">
<property name="BlogService" ref="BlogService" />
<property name="BlogEntryService" ref="BlogEntryService" />
<property name="BlogEntryCommentService" ref="BlogEntryCommentService" />
</object>
Nevermind the properties being set what it actually does is it returns the same instance every time I ask for this object using
IController result = context.GetObject(controllerName) as IController;
So when I've changed the definition to
<!-- Controlers -->
<object name="Home" type="MyBlog.Controllers.HomeController" singleton="false">
<property name="BlogService" ref="BlogService" />
<property name="BlogEntryService" ref="BlogEntryService" />
<property name="BlogEntryCommentService" ref="BlogEntryCommentService" />
</object>
everything started work just fine.
Best regards,
Matthias.
just an additional note for clarifying this behavior: Controllers are treated as prototypes by the ASP.NET MVC framework, but Spring defaults to singleton mode. Therefore you need to explicitly mark your object as non-singleton in the container configuration. The upcoming Spring MVC integration module will take care of this, so you won't run into this then.
hth,
Erich