Whenever i want to add a javascript library programatically, say jquery for example, it generally involves making sure there is a placeholder at the footer of my page, then calling a codebehind method that will take a link to the src as a parameter and return an htmlgeneric control, which is then added to this placeholder.
Is this still the neatest way to do it, even with .net 4.0 out?
I think a better way is to use the RegisterStartupScript method:
http://msdn.microsoft.com/en-us/library/z9h4dk8y.aspx
And even better in your case RegisterClientScriptInclude:
http://msdn.microsoft.com/en-us/library/kx145dw2.aspx
EDIT:
Here's a sample of RegisterClientScriptInclude:
if (!Page.ClientScript.IsClientScriptIncludeRegistered("myJsInclude"))
Page.ClientScript.RegisterClientScriptInclude("myJsInclude", "myJsFile.js");
EDIT2:
Here's a sample of an include with RegisterStartupScript:
string jsBlock = "<script src='myJsFile.js'></script>";
if (!Page.ClientScript.IsStartupScriptRegistered("myJsInclude"))
Page.ClientScript.RegisterStartupScript(typeof(string), "myJsInclude", jsBlock, false);
You should add things like language="text/javascript" to the script tag, but for readability I didn't add them.
Sorry... I decided to move my comment to an answer.
I personally add all of my JS to the ScriptManager. It helps lower the number of Http calls that the page has to make.
ScriptManager1.CompositeScript.Scripts.Add(New ScriptReference("~/Page/To/Jquery.js"))
But this is only if you're already using a ScriptManager on your page
Also, if you don't want to add it from CodeBehind, you can do it right in your page.
<ScriptManager>
<CompositeScript>
<Scripts>
<-- your scripts in here -->
</Scripts>
</CompositeScript>
</ScriptManager>
So by doing this, you're able to add all of your JS to a single HTTP Request rather than having a bunch of different requests all at once.
Then in the ScriptManager tag, you can add LoadScriptsBeforeUI="false" to have them put to the bottom of the page.
Sorry but that was never the cleanest way to inject script into an asp.net page.
Look at the ClientScript object. There are several methods that will suit your needs without resorting to placeholders.
ScriptManager is a good way to do this, as mentioned above. If you are not using MS Ajax and ScriptManager, then I suggest you write your own control. It should be very simple control at that. Add a public variable List and override RenderContents method to walk through your list of strings and render on the page. Sample code:
public class CustomScriptManager : WebControl
{
private List<string> scripts = new List<string>();
public List<string> Scripts
{
get { return scripts; }
set { scripts = value; }
}
protected override void RenderContents(HtmlTextWriter writer)
{
foreach (string script in scripts)
{
writer.Write("<script language=\"JavaScript\" type=\"text/javascript\" src=\"" + script + "\"></script>");
}
}
}
P.S. I haven't verified above code, but I thing you get the idea.
Related
I'm developing a webapp built in C# with Blazor WASM that is Asp.Net hosted. I'm making a blazor component that through the use of a library already in production, will generate a HTML fragment (or full embed) that is then displayed in this way
...
<div>
#((MarkupString)document)
</div>
...
with document containing the markup generated by the library.
As long as we're doing it with static content all is fine and dandy, but now we need to have some input in there that will then be sent back to the server to execute some actions.
In a MarkupString there is no way to include <InputFile /> or <InputText /> components in such a way that they are shown in the fragment and I can read their contents, and I can find no way to actually interact with the standard HTML tags, especially regarding the file upload.
Moreover we'll probably soon need to have a specific image uploader with preview which would be a custom Blazor component and this led me to the CustomElements .NET 7 feature that looks like what I need for both problems.
However I couldn't find how to actually implement this in my app, and the documentation I found is still very partial in that way. Is there a way to do what I need?
EDIT: Managed to fix this partially, with Chen's answer. I still have trouble with the binding though, as the #bind-Value directive is not working with
Unhandled exception rendering component: Microsoft.AspNetCore.Components.Forms.InputText requires a value for the 'ValueExpression' parameter. Normally this is provided automatically when using 'bind-Value'.
with this markup:
...
<custom-input-text #bind-value="$field1" name="$field1"></custom-input-text>
...
(the capital V in bind-Value becomes lowercase all by itself)
Am I doing something wrong again?
CustomElements should meet your requirements, you can create your own logic in Blazor components, and then use it in your application.
To use the component, you need to add the following JavaScript script references to your host app in this specific order.
<script src="_content/Microsoft.AspNetCore.Components.CustomElements/BlazorCustomElements.js"></script>
<script src="_framework/blazor.webassembly.js"></script>
You also need to add the corresponding middleware:
app.UseBlazorFrameworkFiles();
And use app.UseWebAssemblyDebugging(); for debugging.
Then you need to register the corresponding component in the Blazor program:
builder.RootComponents.RegisterCustomElement<Counter>("my-counter");
Then you can call this component in your application, including passing parameters, etc.
<my-counter title="Khalid" increment-amount="2" />
Here is a complete example with detailed explanation, you can use it as a reference.
Helpful links:
Blazor Custom Elements.
Using .NET 7's Blazor Custom Elements to render dynamic content.
ASP.NET Core Razor components.
Hope this can help you.
Edit1:
It looks like you can't do two-way binding between Razor Page and Razor Component. The official document mentions that parameters can be passed through JavaScript properties, but it doesn't seem to be able to read the parameters.
For example:
<my-counter ></my-counter>
<button onclick="Test()">click</button>
<script>
function Test()
{
const elem = document.querySelector("my-counter");
//get parameter failed, it's undefined
var text = elem.incrementAmount;
//successfully set parameter
elem.incrementAmount = "test";
}
</script>
So I'm guessing that CustomElements only have writable properties in Razor Page.
Edit2:
I found that JQuery can be used to detect the value of the input box. When getting this value and performing certain operations, can it also achieve the same effect as two-way binding?
<my-counter ></my-counter>
<button onclick="Test()">click</button>
<script>
function Test()
{
const elem = document.querySelector("my-counter");
//get the value of input
var value = $("my-counter").find("input").val();
elem.incrementAmount = "test";
}
</script>
I have a simple Blazor component that take some inputs (some lists of object and few strings) and formats them into simple HTML for display, (tables generated from the lists of objects, simple text, etc.).
This HTML is a report that is intended to be both displayed to users in the app and also emailed to various people (via SendGrid). For compatibility, we are keeping the email HTML as simple as possible.
The component works fine, however I am not sure how to translate a component's markup portion into a simple string of escaped HTML so that I can pass the string to SendGrid and fire off an email.
I am aware of MarkupStrings, but I have only used them in reverse--to write a string containing HTML tags that will be properly displayed in my app. I can't find any suggestions for doing the conversion the way that I need it done.
Is there any simple way to have a component write all of its markup into a string so that I can email it out?
Or, would I be better off writing a .cs file with a static method that takes in the parameters in question, renders it into a MarkupString, and then passes the string both to SendGrid for email and also to a Blazor component for in-app display?
The simplest way to do that is to employ JSInterop to retrieve the Html markup for the component, produced by the browser. Let's say You've defined a child component, and you want to retrieve its html source. You can do that like this:
Define the child...
SelectGender.razor
<div id="selectGender">
<h1>Select Gender</h1>
<select>
#foreach (var gender in genders)
{
<option>#gender</option>
}
</select>
</div>
#code {
private List<string> genders = new List<string> { "Male", "Female", "Other" };
}
Usage
#page "/"
#inject IJSRuntime JSRuntime
<div>#((MarkupString) html)</div>
<SelectGender />
<button #onclick="GetHtml">Get Html</button>
#code{
private string html;
protected async Task GetHtml()
{
html = await JSRuntime.InvokeAsync<string>("myJsFunctions.getHtml");
}
}
_Host.cshtml
<script>
window.myJsFunctions =
{
getHtml: function () {
return document.getElementById("selectGender").innerHTML;
}
};
</script>
Is there any simple way to have a component write all of its markup into a string so that I can email it out?
No, your C# code has no simple way to do this - you could use JS Interop to get the rendered HTML from the dom, but nothing built in for it.
Or, would I be better off writing a .cs file with a static method that takes in the parameters in question, renders it into a MarkupString, and then passes the string both to SendGrid for email and also to a Blazor component for in-app display?
That is a possibility - I can't comment on the value of that to you, but it is a technique that could work if the component you are rendering is static,
I want to build a webpage containing jquery draggables in asp.net (c#).
How can I add my jquery code to my page in an object oriented way?
So I want to make a c# myDraggables.cs class that I add to the asp page with the htmlgenericcontrol. How do I add this jquery code (eg: $('#mydraggables').draggable(); ) in my class.
I could do it with the htmlgenericcontrol by adding a "script"-tag. But isn't there a way to do it with a jquery helper (can't find that)?
tx
A sample:
Basicly, in my myDraggables-class I could have;
public HtmlControl Draw()
{
HtmlGenericControl aDiv= new HtmlGenericControl("div");
aDiv.Attributes.Add("id", "dragtest");
return aDiv;
}
public HtmlControl DrawJquery()
{
HtmlGenericControl js = new HtmlGenericControl("script");
js.Attributes["type"] = "text/javascript";
js.InnerHtml = "$('#dragtest').draggable();";
return js;
}
And the code behind of my asp.page would look like;
protected void Page_Load(object sender, EventArgs e)
{
myDraggables md = new myDraggables ('..some props ..');
Page.Header.Controls.Add(md.DrawJquery());
myPlaceHolder.Controls.Add(md.Draw());
}
That works, but jquery-code will become more complex. And will not be readable anymore...
how can this be achieved in json? Or another more readable way?
Thanks
If I understand you correctly, you want to write a strongly-typed .NET class in the back-end which also functions as a jQuery object on the front-end, right? If so, the best way to do it is probably to serialize your .NET object into JSON format, store it in a variable and print it out in a script tag when generating the markup. Then, on the front-end, parse the JSON string into a Javascript object and you can use it from there.
Can you post any .NET and JS code you have already?
With ASP.NET Webforms, I could drag and drop a control on a form and be able to access its properties:
if (number == 0)
AddButton.Enabled = false;
Although I cannot drag and drop a control on a ASP.NET MVC View template, can I change its attributes?
For instance:
disable a button in some conditions and be able to enable it if conditions change.
Be able to change the text of a button from "Next->" to "Finish"
etc.
There are (at least) two Methods to do this:
Method 1: The Simple Way
You would do this by adding logic in your view:
<input type="button" disabled=<%= Model.number <= 0 %> />
Where Model.number is the count of items passed to your view by your controller. If it's less than or equal to zero, disabled will be true.
The syntax may not be exact, I haven't tried this, but this is the path I would go down to do what you want.
This will work for the initial setting of the value; changing it without refreshing the page is a matter of using JavaScript, as other answers have pointed out.
Method 2: The overly complex but more 'MVC' way
If you want the logic in the controller rather than the view, you should set up a specific ViewModel object that you can add the logic to:
ViewModel
public class MyObjectViewModel
{
MyObject MyObject {get; private set; }
bool Enabled; {get; set; }
public MyObjectViewModel(MyObject obj)
{
MyObject = obj;
}
string IsEnabled
{
get
{
if (Enabled)
{
return "";
}
else return "disabled=disabled";
}
}
Controller
public ActionResult Show(int id)
{
MyObject myObject = repository.GetMyObjectById(id)
MyObjectViewModel movm = myObject;
movm.Enabled = myObject.number > 0;
return View(movm);
}
View
<input type="button" <%= Model.IsEnabled %> />
Again, the syntax and usage may be a little off, I'm prototyping this off the top of my head, and am not in a location where I can test this for you.
If you're interested in ViewModels, here are some good resources:
View Model Best Practices
ASP.NET MVC Tip# 50: Create View Models
I've updated it to return disabled=disabled using the string if it is actually disabled.
All client side behaviour is scripted through javascript. MVC default ships with jQuery for this (www.jquery.com).
I've outlined how you could go about your examples:
<input id="nextFinishBtn" type="button" value="Next ->"/>
Assume you want to change this to "Finish" if the user unchecks a checkbox named "Configure Advanced settings". which was true by default
<%= Html.CheckBox("DoAdvancedSettings", "true", new { onclick='changeNextButton()' }); %>
<script langauge="javascript">
function changeNextButton() {
$('#nextFinishBtn').val('Finish');
}
</script>
In general you can access any attribute of any element in jQuery using the .attr construct:
$('#nextFinishBtn').attr('disabled','disabled');
You call it with two parameters to set a value, and with just one to fetch the value. So to see if the button is disabled, you'd do:
if ($('#nextFinishBtn').attr('disabled')=='disabled') { alert('button is disabled'); }
ASP.NET MVC is a lightweight programming model. It does not create a control object model for you at the server the way plain ASP.NET does. Typically, you would use client-side javascript (possibly with help from JQuery) to manipulate the properties of controls that are put on the page by the markup in your view.
If you want to get a quick start with ASP.NET MVC, check out Sharp Architecture (open source). They have guidance and all sorts of goodies to help you get productive with ASP.NET MVC quickly.
Generally, you rarely want to do this. The MVC way is:
Let your controller populate the model with the objects needed to generate the HTML
Pass the model to the appropriate view
Let the view output the HTML based on the values of the objects in the model.
If you often find yourself in need of modifying HTML attributes after the HTML has been generated, you're probably not applying this pattern correctly.
I would check out the HTML helpers.
<%= Html.Button("AddButton","Button Text",HtmlButtonType.Submit,"SomeJavaScriptFunction()",new {disabled="disabled"} ) %>
The only real gotcha is the anonymous class at the end gets funny when you add attributes that are keywords. For example, to add a Css class you need an anonymous class that looks like this new {#class="myCssClassName"}.
As I replied to George Stocker, I've noticed that the disabled attribute can get only 1 value (disabled = "disabled"). Also, anything else disable the input control as well. For instance, disabled = true and disable = false will still disable the control.
It looks like (I'm not sure) having disabled attribute disables the control and not having it enables the control. So I decided to write a extension method to the HtmlHelper class.
public static class MyHelperClass
{
public static string InputDisable(this HtmlHelper html, string name, string myValue, bool isEnabled)
{
string show = "";
if(!isEnable)
show = "disabled = \"disabled\"";
return "<input type = \"submit\" value = \"" + myValue + "\"" + show + " />";
}
}
Now I can access the method this way
<% = Html.InputDisable("myInput", "My Button", false)%>
So the last param determines weather the input control is visible.
Now using the Goerge Stocker logical, I can define the value of isEnabled .
Thanks for all your answers
I have a javascript method looks like this
JSMethod(JS_para_1,JS_para_2)
{
......
,,,,,
}
and I have an ASP.NET method like this
ASP_Net_Method(ASP_Para_1,ASP_Para_2)
{
....
,,,
}
Now I want to call this ASP_Net_Method from my JSMethod by passing some parameters over there..
Just to be clear:
Your javascript is executed by the user's browser on the user's laptop
Your ASP.NET method is executed on your server
So, what you probably want to do is to send a message from the browser to the server saying "Hey, run this method and give me the result back".
If you are doing traditional ASP.NET development (not ASP.NET MVC), I think the normal approach would be to create an ASPX page which, when requested, executes the method you want executed. Then, in your javascript you just need to request this page. To do this, you can use jQuery (either jQuery ajax, jQuery get or jQuery post).
You will need to download the jQuery library and include it in your page for this to work.
Give it a go and if you can't get it to work, come back for more specific advice.
EDIT: You can also take a look at ASP.NET AJAX. The home page has a lot of tutorials and videos.
What you really want to do is execute server-side code (sometimes called "Code-behind", which was the term I used when googling this.) from javascript.
This post shows several options. The better ones are toward the bottom.
http://forums.asp.net/t/1059213.aspx
Basically, every function that fires a server side event uses a javascript method called __doPostBack and here is an example of what you want to do.
Ajax's PageMethods is very useful for this if you don't want to do a full postback and just need to call 1 method.
First I decorate a method in my aspx.cs file like so:
[System.Web.Services.WebMethod]
public static string getVersions(string filePath)
{ ...
return myString;
}
Notice the "static" too. Then in javascript I can call this like:
PageMethods.getVersions(_hfFilePath.value, LoadVersionsCallback);
You can have as many parameters as you need of different data types. The last parameter is the JavaScript function that gets called when the call returns. Looks something like:
function LoadVersionsCallback(result) {
...
// I make a drop down list box out of the results:
parts = result.split('|');
for (var i = 0; i < parts.length; i++) {
_ddl.options[_ddl.options.length] =
new Option(parts[i].replace(/~/g, ", "), parts[i]);
}
...
}
Finally, you must have the script managers property "EnablePageMethods" set to "true".
<ajaxToolkit:ToolkitScriptManager ID="ScriptManager1"
runat="server" EnablePageMethods="true"
EnablePartialRendering="true"
OnAsyncPostBackError="ScriptManager1_AsyncPostBackError">
</ajaxToolkit:ToolkitScriptManager>
So from JavaScript you can call a static function on your page's behind code.