I have created a usercontrol for having two buttons one below the other as
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="ViewItemsGroup.ascx.cs" Inherits="SDL.Examples.UI.Controls.ViewItemsGroup" %>
<%# Import Namespace="Tridion.Web.UI" %>
<c:RibbonItemsGroup runat="server" ID="RibbonItemsGroup">
<c:RibbonButton runat="server" CommandName="ViewStaging" Title="View in Staging" Label="View In Staging" IsSmallButton="true" ID="ViewStagingBtn" />
<c:RibbonButton runat="server" CommandName="ViewLive" Title="View in Live" Label="View in Live" IsSmallButton="true" ID="ViewLiveBtn" />
</c:RibbonItemsGroup>
In the extension.config i am refering to the usercontrol as shown below.
<ext:extension assignid="ViewItemsGroup" groupid="EditGroup" name="View" pageid="HomePage" insertbefore="PublishGroup">
<ext:group>~/Controls/ViewItemsGroup.ascx</ext:group>
<ext:dependencies>
<cfg:dependency>My.Theme</cfg:dependency>
</ext:dependencies>
<ext:apply>
<ext:view name="DashboardView">
<ext:control id="DashboardToolbar" />
</ext:view>
<ext:view name="PageView">
<ext:control id="ItemToolbar" />
</ext:view>
</ext:apply>
</ext:extension>
Do i need to include my button(s) details (mentioned in the usercontrol like ID) also in extension.config? Is it required to be added in <ext:command> ---- </ext:command> or any other place.
The usercontrol created, I am just placing along with the extension.config, .js and refering to ascx in my extension.config file. Do i need to place my usercontrol in any of the other folders?
In your extension.config, you specify the dependencies for the ItemsGroup under the ext:dependencies node. This should point towards your cfg:group where you reference a commandset which in turn points to your JavaScript files. You need to make these references correctly else the SDL Tridion UI framework won't know where to find your code. The system is just like us unable to perform magic and also does not come with a crystal ball.
Same goes for the user control, if you don't place it alongside your other extension files like the configuration, JavaScript and CSS, how would the UI framework be able to find it. The location of where you placed it, you specify in the ext:group node. For example ~/Controls/MyItemsGroup.ascx then you placed it in a sub directory Controls in the root of your extension.
Your configuration should look something like this then, with the dependencies pointing to the group you have specified above it under the resources node:
<ext:ribbontoolbars>
<ext:add>
<ext:extension assignid="MyGroup" ...>
<ext:group>~/Controls/MyItemsGroup.ascx</ext:group>
<ext:dependencies>
<cfg:dependency>My.Commands</cfg:dependency>
</ext:dependencies>
...
</ext:extension>
</ext:add>
</ext:ribbontoolbars>
Then in your control, you reference the command directly in its attributes, this is the JavaScript command under which the _isAvailable and _isEnabled methods are etc. The control will have something like:
<c:RibbonItemsGroup runat="server" ID="MyItemsGroup">
<c:RibbonButton runat="server"
CommandName="MyBtn"
Title="My Title" Label="My Label"
IsSmallButton="true" ID="MyId" />
...
</c:RibbonItemsGroup>
Lastly the JavaScript which implements your button command will then use the CommandName in its namespace:
Type.registerNamespace("Extensions");
Extensions.MyBtn = function Extensions$MyBtn() {
Type.enableInterface(this, "Extensions.MyBtn");
this.addInterface("Tridion.Cme.Command", ["MyBtn"]);
};
Extensions.MyBtn.prototype._isAvailable = function MyBtn$_isAvailable(selection) {
return true;
};
Extensions.MyBtn.prototype._isEnabled = function MyBtn$_isEnabled(selection) {
return this._isAvailable(selection);
};
Extensions.MyBtn.prototype._execute = function MyBtn$_execute(selection) {
// this is where your code comes
};
Related
My scenario:
I have a validator in an UpdatePanel.
I want to combine my scripts so I am using CompositeScript in ScriptManager, and including a reference to WebUIValidation.js
I am using .NET 4.0
My problem:
When I asynchronously update the panel, .NET loads WebUIValidation.js (in a Scriptresource.axd file) in the async response, even though it has been loaded in the initial CompositeScript-generated script. This is a problem because I have custom code that hijacks some functions in WebUIValidation.js, and the async response overrides my hijacks.
If you move the reference to WebUIValidation.js to Scripts in ScriptManager, there is no problem.
If you were to have WebUIValidation.js as the only item in CompositeScript (pointless I know) then there is no problem.
This async reload does not happen with other .NET library scripts, e.g. WebForm.js
What I want to find out:
is there a reason why WebUIValidation.js is loaded in the async response when it is already included in the CompositeScript?
Someone has posted a similar (but not duplicate) issue today, and is veering towards saying that WebUIValidation.js might not be handled by ScriptManager. Can anyone verify this?
To replicate use the following two files
test1.js
// To be added to the composite script
console.log('Test 1 Loaded');
test.aspx
<%# Page Language="vb" AutoEventWireup="false" %>
<!DOCTYPE html>
<html>
<head runat="server">
<title></title>
</head>
<body>
<script language="VB" runat="server" runat="server">
Protected Sub ButtonClicked(ByVal sender As Object, ByVal e As System.EventArgs) Handles TestButton.Click
ButtonClickFeedback.Text = "Button clicked At " & Date.Now.ToString & "; Look at the scripts in your Developer Tools, there is now a separate script for WebUIValidation.js loaded, in spite of the composite script."
End Sub
</script>
<form runat="server">
<asp:ScriptManager runat="server">
<CompositeScript>
<Scripts>
<asp:ScriptReference Path="~/test.js" />
<asp:ScriptReference Name="WebUIValidation.js" Assembly="System.Web" />
</Scripts>
</CompositeScript>
</asp:ScriptManager>
<h1>WebUIValidation.js, CompositeScript and UpdatePanel test</h1>
<asp:UpdatePanel runat="server" ID="ButtonUpdatePanel">
<ContentTemplate>
<asp:Label runat="server" >This is a section with a validator that is within an UpdatePanel. If you look at the scripts loaded, you will see the composite script in the detail.</asp:Label>
<asp:Textbox ID="TestInputForValidator" runat="server" Text="This is populated so it will validate"/>
<asp:RequiredFieldValidator runat="server" ControlToValidate="TestInputForValidator" ErrorMessage="You must write something" /><br />
<asp:Button ID="TestButton" Text="Click Me!" runat="server" /><br />
<asp:Literal ID="ButtonClickFeedback" runat="server" />
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="TestButton" />
</Triggers>
</asp:UpdatePanel>
</form>
</body>
</html>
If you use your Developer Tools to inspect what scripts are being loaded in, you should see an additional Scriptresource.axd (that contains WebUIValidation.js) getting loaded in after clicking the button, in spite of the existence of a Scriptresource.axd with the composite script. test.js is just a sample js file to simulate the idea of composite scripts.
Sharing my investigation on why it's loading these scripts again (WebUIValidation.js or Focus.js). I've just found about Focus.js for now.
First, the origination of the http request is in the partial update generated by the update panel. If you look at the reponse to the asynchronous xhr POST request, you'll have something like this. Note almost at the end the ScriptResource.axd url. This is processed by the ajax framework on the client side and since it's a script block with a path, it gets loaded:
1|#||4|2999|updatePanel|ctl00_LoginContent_ctl00|
<div id="ctl00_LoginContent_...">[...html content here]</div>|
0|hiddenField|__LASTFOCUS||
0|hiddenField|__EVENTTARGET||
0|hiddenField|__EVENTARGUMENT||
904|hiddenField|__VIEWSTATE|UdWhNvH6wpBcPOigY[...]SIphbw==|
8|hiddenField|__VIEWSTATEGENERATOR|25748CED|
176|hiddenField|__EVENTVALIDATION|q+FUEVGVj+t[...]AzAm|
0|asyncPostBackControlIDs|||
0|postBackControlIDs|||
26|updatePanelIDs||tctl00$LoginContent$ctl00,|
0|childUpdatePanelIDs|||
25|panelsToRefreshIDs||ctl00$LoginContent$ctl00,|
2|asyncPostBackTimeout||90|
14|formAction||./Default.aspx|
119|scriptBlock|ScriptContentNoTags|function PopulateTimeZoneOffset(){[my own js here...]}|
154|scriptBlock|ScriptPath|/ScriptResource.axd?d=Uup1Lt[...]q450&t=ffffffffd4ee116f|
31|focus||ctl00_LoginContent_LoginControl|
Now debugging server side code, loading the .net assemblies symbols from https://referencesource.microsoft.com/ (with VS configuration as described there).
PageRequestmanager.cs
private void ProcessFocus(HtmlTextWriter writer) {
// Roughly stolen from Whidbey Page.cs
if (_requireFocusScript) {
Debug.Assert(ClientSupportsFocus, "If ClientSupportsFocus is false then we never should have set _requireFocusScript to true.");
string focusedControlId = String.Empty;
// Someone calling SetFocus(controlId) has the most precedent
if (!String.IsNullOrEmpty(_focusedControlID)) {
focusedControlId = _focusedControlID;
}
else {
if (_focusedControl != null && _focusedControl.Visible) {
focusedControlId = _focusedControl.ClientID;
}
}
if (focusedControlId.Length > 0) {
// Register focus script library
string focusResourceUrl = _owner.GetScriptResourceUrl("Focus.js", typeof(HtmlForm).Assembly);
EncodeString(writer, ScriptBlockToken, "ScriptPath", focusResourceUrl);
// *********** THIS ENCODESTRING OUTPUTS THE PROBLEM !!!
// Send the target control ID to the client
EncodeString(writer, FocusToken, String.Empty, focusedControlId);
}
}
}
We are deep inside Page.ProcessRequest, and now in Page.Render, RenderPageCallback and ProcessFocus. The highlighted EncodeString near the end writes directly to the writer things like "len|type|id|content|", including writer.Write(content); where content is "/ScriptResource.axd?d=Uup1IW...q450&t=ffffffffd4ee116f". There is no check to see if this script is already registered with the ScriptManager, it's not calling ScriptManager.RegisterXXXX.
So it seems to me that's the cause of getting another http request for something that's already loaded. ProcessFocus is done as part of "Render", far too late to use any ScriptManager functinality.
I can't think of a way to avoid this http request (apart from not using any SetFocus type thing from the .net framework).
(Running VS 2015, .net framework 4.6.2, ajax control toolkit 17.1)
I have a standard input:
<asp:TextBox type="text" runat="server" id="txtSearchTerm" />
I'd like to have this render with a dynamic HTML5 placeholder. Something like:
'Code Behind
txtSearchTerm.**placeholder** = "Search " + Site.Name
So that it outputs the following HTML:
<input type="text" runat="server" id="txtSearchTerm"
placeholder="Search Site #1" />
where Site.Name = "Site #1".
txtSearchTerm.placeholder is not a property. I have it set to text and then run javascript to show/hide on focus BUT I would much rather just use the HTML5 placeholder value. How can I render this?
Please no JS/client side solutions.
You could use the Attributes collection. So you would have something like
txtSearchTerm.Attributes.Add("placeholder", "Search" + Site.Name);
or
txtSearchTerm.Attributes["placeholder"] = "Search" + Site.Name; // or Attributes("placeholder") if you're using vb.net
And if you're using resources for localization/translation:
txtSearchTerm.Attributes["placeholder"] = GetLocalResourceObject("YourLocalResourceName").ToString();
Because I find it annoying/tiresome to add all the placeholders from the code behind. You can create a new TextBox Class that inherits the WebControls TextBox and then you can add the placeholder from the CodeBehind or from the HTML Side.
TextBox.cs (Placed in Project/Controls/)
namespace Project.Controls
{
public class TextBox : System.Web.UI.WebControls.TextBox
{
public string PlaceHolder { get; set; }
protected override void OnLoad(EventArgs e)
{
if(!string.IsNullOrWhiteSpace(PlaceHolder))
this.Attributes.Add("placeholder", PlaceHolder);
base.OnLoad(e);
}
}
}
Register Control In the Web.Config:
<system.web>
<pages>
<controls>
<add tagPrefix="ext" assembly="Project" namespace="Project.Controls" />
</controls>
</pages>
</system.web>
(use whatever tag prefix you want)
Usage:
<ext:TextBox runat="server" id="SomeId" PlaceHolder="This is a PlaceHolder" />
or from the code behind
SomeId.PlaceHolder="This is a PlaceHolder";
I just put placeholder property in HTML code and works:
<asp:TextBox placeholder="hola mundo" ID="some_id" runat="server"/>
There is also the TextBoxWatermark extender included in Microsoft's Ajax Control toolkit. It's not HTML5, but it's backwards compatible (I believe).
http://www.asp.net/AjaxLibrary/AjaxControlToolkitSampleSite/TextBoxWatermark/TextBoxWatermark.aspx
<ajaxToolkit:TextBoxWatermarkExtender ID="TBWE2" runat="server"
TargetControlID="TextBox1"
WatermarkText="Type First Name Here"
WatermarkCssClass="watermarked" />
I have the following obout control on my page:
<cc1:ComboBox ID="ActivityTypeComboBox" runat="server" Width="100" AllowEdit="False">
<ClientSideEvents OnSelectedIndexChanged="alert('x')" OnItemClick="alert('y')" />
</cc1:ComboBox>
Both of the ClientSideEvents fire when the page first loads but not after that when I actually do the events.
Any idea why or what I am missing or doing wrong?
Thanks!
Don't know about "Obout" controls, but at least for Infragistics ones the ClientSideEvents only contain the function names, not actual JavaScript code.
If I'm correct, you'd have to do something like this:
<cc1:ComboBox ID="ActivityTypeComboBox" runat="server" Width="100" AllowEdit="False">
<ClientSideEvents OnSelectedIndexChanged="onActivityTypeChanged" OnItemClick="onActivityTypeClicked" />
</cc1:ComboBox>
Then in JS:
function onActivityTypeChanged()
{
//...
}
function onActivityTypeClicked()
{
//...
}
The JS functions might also get some additional parameters from the control, but you'd have to consult the documentation for that.
I need to send the actual control id that .NET generates for an UpdatePanel's div to a javascript function. How do I rewrite the ScriptAction line below to accomplish this?
<asp:UpdatePanel ID="update1" runat="server" UpdateMode="Conditional">
...
</asp:UpdatePanel>
<cc1:UpdatePanelAnimationExtender runat="server" TargetControlID="update1">
<Animations>
<OnUpdating>
<Parallel duration="0">
<ScriptAction Script="doSomething(**update1's ID**);" />
</Parallel>
</OnUpdating>
...
</Animations>
</cc1:UpdatePanelAnimationExtender>
EDIT:
I would like to have update1.UniqueId be placed in doSomething's parameters.
EDIT:
The following fails:
<ScriptAction Script="alert('<%= update1.ClientID %>');" />
With
Exception type: System.Web.HttpParseException
Exception message: The 'Animations' property of 'cc1:UpdatePanelAnimationExtender' does not allow child objects.
Have you tried to use its ClientID?
Script="doSomething('<%= update1.ClientID %>');"
According to your updated question: add this ClientID as a javascript variable at the top of the page. Then you can access it from your js-function.
Something like this:
<script type="text/javascript"> var update1ClientID = '<%= update1.ClientID %>';</script>
and then:
Script="doSomething('' + update1ClientID );"
If this also doesn't work, try to use the same approach but from Codebehind(Page_Load)
ScriptManager.GetCurrent(Me.Page).RegisterClientScriptBlock(Me, Me.GetType, "update1ClientID", "var update1ClientID='" & Me.update1.ClientID & "';", True)
<ScriptAction Script="doSomething('<%=update1.ClientID %>');" />
** Edit *
To add to Tim's solution, if you have more than one custom control per page, iterate through the page controls and add a register script for each control that is of the type of your custom control.
Is it possible to set the ValidationExpression of a RegularExpressionValidator using JavaScript? I'm using ASP.NET 3.5.
Here's why I want to do this...
On a payment page I have a DropDownList that allows my user to select their card type. Beneath that is a TextBox in which they type their card number.
I want to use a RegularExpressionValidator to validate that their card number is valid for their given card type. The card payment processing is performed manually in a different system, so I cannot rely on this to catch incorrect card details.
Therefore I need to use a different ValidationExpression for each card type. I would like to set the ValidationExpression using JavaScript, firing off the DropDownList onchange event.
My DropDownList is bound to an XML document:
<asp:DropDownList ID="ddlCardType" runat="server"
DataTextField="Text" DataValueField="Value"
DataSourceID="xdsCardTypes" AppendDataBoundItems="True">
<asp:ListItem Text="(select)" Value=""></asp:ListItem>
</asp:DropDownList>
<asp:XmlDataSource ID="xdsCardTypes" runat="server"
DataFile="~/App_Data/PaymentCards.xml">
</asp:XmlDataSource>
Here's the XML doc:
<?xml version="1.0" encoding="utf-8" ?>
<PaymentCards>
<PaymentCard Text="American Express" Value="AmericanExpress" RegEx="3[47](\d{13})" />
<PaymentCard Text="MasterCard" Value="MasterCard" RegEx="5[1-5](\d{14})" />
<PaymentCard Text="Maestro" Value="Maestro" RegEx="(5018|5020|5038|6304|6759|6761)\d{8,15}" />
<PaymentCard Text="Visa" Value="Visa" RegEx="4(\d{15}|\d{12})" />
</PaymentCards>
In code-behind I'm creating a JavaScript function call and adding it to the onchange event of the DropDownList:
XDocument paymentCards = XDocument.Parse(xdsCardTypes.GetXmlDocument().InnerXml, LoadOptions.None);
List<string> regExes = paymentCards.Descendants("PaymentCard")
.Select(pc => pc.GetAttribute("RegEx").Value).ToList();
string setRegExValidatorScript = string.Format("setRegExValidator('{0}', '{1}', {2});",
ddlCardType.ClientID,
txtCardNumber_RegularExpressionValidator.ClientID,
regExes.ToJavaScriptArgumentList());
ddlCardType.AddAttribute("onchange", setRegExValidatorScript);
And in a referenced .js file I have the following:
function setRegExValidator(ddlCardTypeID, regExValidatorID, regExes)
{
var regEx = regExes[$get(ddlCardTypeID).selectedIndex];
var val = $get(regExValidatorID);
// TODO: Set the ValidationExpression!
}
So my one missing link is the ability to set the ValidationExpression from JavaScript. Yes I could use a postback to achieve this, but that seems unnecessary.
(Suggestions on an alternate approach are also welcomed!)
function setRegExValidator(ddlCardTypeID, regExValidatorID, regExes)
{
var regEx = regExes[$get(ddlCardTypeID).selectedIndex];
var val = $get(regExValidatorID);
val['validationexpression'] = regEx;
}
NB: You need to ensure that the card number is validated properly on the server-side too.