I have an html page that using ajax to call HttpHandler which needs to return javascript function back to client, so, when button is clicked this function gets executed.
I'm learning ajax now and using old fashion way to call it.
Here is my .html file:
<script language="javascript">
var XMLHttpRequestObject = false;
try {
XMLHttpRequestObject = new ActiveXObject("MSXML2.XMLHTTP");
} catch (exception1) {
try {
XMLHttpRequestObject = new ActiveXObject("Microsoft.MLHTTP");
}
catch (exception2) {
XMLHttpRequestObject = false;
}
}
if (!XMLHttpRequestObject && window.XMLHttpRequest) {
XMLHttpRequestObject = new XMLHttpRequest();
}
function getData(dataSource) {
if (XMLHttpRequestObject) {
XMLHttpRequestObject.open("GET", dataSource);
XMLHttpRequestObject.onreadystatechange = function () {
if (XMLHttpRequestObject.readyState == 4
&& XMLHttpRequestObject.status == 200) {
eval(XMLHttpRequestObject.responseText);
}
}
XMLHttpRequestObject.send(null);
}
}
function alerter() {
var targetDiv = document.getElementById("targetDiv");
targetDiv.innerHTML = "Got the JavaScript OK.";
}
</script>
</head>
<body>
<h1>Returning JavaScript</h1>
<form>
<input type="button" value="Fetch JavaScript" onclick="getData('http://myserver/DataSources/data.ashx')" />
</form>
<div id="targetDiv">
<p>The fetched data will go here.</p>
</div>
</body>
Than I have my HttpHandler where I need to register client script to call the function:
public class data1 : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
EchoMessage(context);
}
private void EchoMessage(HttpContext context)
{
}
}
What should I write in my EchoMessage to register the script needed to run on a client side?
Thank you
You can use RegisterStartupScript or RegisterClientScriptBlock to declare a script on the server and pass it on to be executed on the client. These are static functions of ClientScript and ScriptManager.
Differences between ScriptManager and ClientScript when used to execute JS?
Difference between RegisterStartupScript and RegisterClientScriptBlock?
After all, I just decided to go with context.Response.Write("alerter()") and it seems to be working. Not sure if this is the right solution though
You can simply use the ScriptManager.RegisterStartupScript, which worked for me as below:
Page page = HttpContext.Current.Handler as Page;
ScriptManager.RegisterStartupScript(page, this.GetType(), "ErrorSave", "$cmsj('.alert-label').html('Maximum 4 quick links are allowed.');", true);
Related
I'm creating an ASP.NET MVC application which uses "SqlDependecy" and "SignalR" technologies to maintain real-time communication with the server based on database changes. It simply inspect a field value changes in specific database record and then display it on the browser.
The attempt works perfectly fine. But when I monitor the network requests through the browsers "Network" performance, the request count increases by 1 in every refresh of the page.
As in the image.
Initial page load only make one request.
First refresh after the initial load and then db change will lead to make 2 requests.
Second refresh after the initial load and then db change will lead to make 3 requests.
so on...
The js code I tried is given below.
It seams as an problem to me. If this is a real problem, Any advice on this will be highly appreciated. Thank you very much.
<script type="text/javascript">
$(function () {
var jHub = $.connection.journeyHub;
$.connection.hub.start();
jHub.client.ListenChange = function () {
getData();
}
jHub.client.ListenChange();
});
function getData() {
$.ajax({
url: 'GetValue',
type: 'GET',
dataType: 'json',
success: function (data) {
if (data == "pending") {
$("#box").css({ "background-color": "orange" });
}
else if (data == "deny") {
$("#box").css({ "background-color": "red" });
}
else if (data == "success") {
$("#box").css({ "background-color": "green" });
}
}
});
}
</script>
<div id="box" style="width:100px; height:100px; background-color: gray;"></div>
[Edit v1]
Here is my Controller where the event handler is located.
public class TravelController : Controller
{
SqlConnection link = new SqlConnection(ConfigurationManager.ConnectionStrings["linkTraveller"].ConnectionString);
// GET: Travel
public ActionResult Listen()
{
return View();
}
public ActionResult GetValue()
{
using (IDbConnection conn = link)
{
string query = #"SELECT [Status] FROM [dbo].[Journey] WHERE [Id]=1";
SqlCommand command = new SqlCommand(query, link);
SqlDependency sqlDep = new SqlDependency(command);
sqlDep.OnChange += new OnChangeEventHandler((sender, e) => sqlDep_OnChange(sender, e));
conn.Open();
string status = command.ExecuteScalar().ToString();
return Json(status, JsonRequestBehavior.AllowGet);
}
}
private void sqlDep_OnChange(object sender, SqlNotificationEventArgs e)
{
JourneyHub.Start();
}
}
Here is the Hub
public class JourneyHub : Hub
{
public static void Start()
{
var context = GlobalHost.ConnectionManager.GetHubContext<JourneyHub>();
context.Clients.All.ListenChange();
}
}
Off the top of my head, I would say you are not decrementing your trigger handlers, sql dependency triggers only fire once and then they are gone, you have to remember the remove the event handler for it or they just keep adding but, but I will know for sure if you can post your sql dependency trigger code.
Here is a sample from something I did many years ago, but the idea is still the same.
try
{
using (
var connection =
new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand(#"SELECT [Id]
,[FName]
,[LName]
,[DOB]
,[Notes]
,[PendingReview]
FROM [dbo].[Users]",
connection))
{
// Make sure the command object does not already have
// a notification object associated with it.
command.Notification = null;
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
command.ExecuteReader();
}
}
}
catch (Exception e)
{
throw;
}
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
SqlDependency dependency = sender as SqlDependency;
if (dependency != null) dependency.OnChange -= dependency_OnChange;
//Recall your SQLDependency setup method here.
SetupDependency();
JobHub.Show();
}
I need to call confirmation message box from codebehind as the user select data from dropdown list and when the selected data is 1 for example a confirmation box will appear to the user to confirm his action
so I did that as below in the code behind I called this JavaScript method:
if (dropdownlist1.SelectedValue == 1)
{
ScriptManager.RegisterStartupScript(this, this.GetType(), "CallConfirmBox", "CallConfirmBox();", true);
}
The script function:
<script type="text/javascript">
function CallConfirmBox() {
if (confirm("هل تريد ان تفصل الباليت؟")) {
alert("سيتم فصل الباليت!");
PageMethods.getdata(onSuccess, onError);
function onSuccess() {
alert(data);
}
function onError() {
alert(errorMessage);
}
}
} else {
//CANCEL – Do your stuff or call any callback method here..
alert("done!");
}
}
And I've added the below line at the beginning of the HTML code:
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true"> </asp:ScriptManager>
and Here is the code behind function that is called from script :
[System.Web.Services.WebMethod()]
[System.Web.Script.Services.ScriptMethod()]
public static void getdata()
{
int nRowsCheck = cMDP.Update_Segregation_PalletPart(nPalletNo);
if (nRowsCheck != 0)
{
nRowsCheck = 0;
nRowsCheck = cMDP.Update_Segregation_Pallet(nPalletNo, nUserID);
if (nRowsCheck != 0)
{
nRowsCheck = 0;
nRowsCheck = cMDP.Delete_Segregation_PalletPart_Delete(nPalletNo);
if (nRowsCheck != 0)
{
nRowsCheck = 0;
nRowsCheck = cMDP.Delete_Segregation_Pallet_Delete(nPalletNo);
}
}
}
}
But I've got the below error:
Page Methods is undefined when run the script !!
Please help as I need some support
First, you'll have to remove one } before the else in your JavaScript.
Change in your code-behind:
if (dropdownlist1.SelectedValue == "1")
For the main problem: Page Methods is undefined:
It seems from your comment that you're using a User Control (ascx). Page Methods cannot be used in a User Control. Please refer to these questions:
PageMethods is not defined
ASP.NET AJAX Page Methods from UserControl
The easiest solution is to use an aspx WebForm instead of an ascx User Control. That's what I've tested and worked.
Or you can use a WebService, as specified in the following question:
Alternate way to use page method inside user control asp.net
But the link to the sample is not working anymore.
Or you can try to use this project that tries to bring ASP.NET AJAX Page Methods to UserControls:
Control Methods for ASP.NET AJAX
You have two problems:
Change you javascript code:
PageMethods.getdata(onSuccess, onError);
function onSuccess(data)
{
alert(data);
}
function onError(data)
{
alert(data);
}
And you code behind getdata method must be a public static string function:
[System.Web.Services.WebMethod()]
[System.Web.Script.Services.ScriptMethod()]
public static string getdata()
{
//Do some things
return " Operations done successfully!";
}
I am seeing odd behavior with the code here.
Client-side (Javascript):
<input type="text" id="userid" placeholder="UserID" /><br />
<input type="button" id="ping" value="Ping" />
<script>
var es = new EventSource('/home/message');
es.onmessage = function (e) {
console.log(e.data);
};
es.onerror = function () {
console.log(arguments);
};
$(function () {
$('#ping').on('click', function () {
$.post('/home/ping', {
UserID: parseInt($('#userid').val()) || 0
});
});
});
</script>
Server-side (C#):
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Web.Mvc;
using Newtonsoft.Json;
namespace EventSourceTest2.Controllers {
public class PingData {
public int UserID { get; set; }
public DateTime Date { get; set; } = DateTime.Now;
}
public class HomeController : Controller {
public ActionResult Index() {
return View();
}
static ConcurrentQueue<PingData> pings = new ConcurrentQueue<PingData>();
public void Ping(int userID) {
pings.Enqueue(new PingData { UserID = userID });
}
public void Message() {
Response.ContentType = "text/event-stream";
do {
PingData nextPing;
if (pings.TryDequeue(out nextPing)) {
var msg = "data:" + JsonConvert.SerializeObject(nextPing, Formatting.None) + "\n\n";
Response.Write(msg);
}
Response.Flush();
Thread.Sleep(1000);
} while (true);
}
}
}
Once I've pressed ping to add a new item to the pings queue, the loop inside the Message method picks the new item up and issues an event, via Response.Write (confirmed using Debug.Print on the server). However, the browser doesn't trigger onmessage until I press ping a second time, and the browser issues another event; at which point the data from the first event reaches onmessage.
How can I fix this?
To clarify, this is the behavior I would expect:
Client Server
-------------------------------------------------------------------
Press Ping button
XHR to /home/ping
Eneque new item to pings
Message loop issues server-sent event
EventSource calls onmessage
This is what is actually happening:
Client Server
-------------------------------------------------------------------
Press Ping button
XHR to /home/ping
Eneque new item to pings
Message loop issues server-sent event
(Nothing happens)
Press Ping button again
New XHR to /home/ping
EventSource calls onmessage with previous event data
(While running in Chrome the message request is listed in the Network tab as always pending. I'm not sure if this is the normal behavior of server-sent events, or perhaps it's related to the issue.)
Edit
The string representation of the msg variable after Response.Write looks like this:
"data:{\"UserID\":105,\"Date\":\"2016-03-11T04:20:24.1854996+02:00\"}\n\n"
very clearly including the newlines.
This isn't an answer per say but hopefully it will lead one. I was able to get it working with the following code.
public void Ping(int id)
{
pings.Enqueue(new PingData { ID = id });
Response.ContentType = "text/plain";
Response.Write("id received");
}
public void Message()
{
int count = 0;
Response.ContentType = "text/event-stream";
do {
PingData nextPing;
if (pings.TryDequeue(out nextPing)) {
Response.ClearContent();
Response.Write("data:" + nextPing.ID.ToString() + " - " + nextPing.Date.ToLongTimeString() + "\n\n");
Response.Write("event:time" + "\n" + "data:" + DateTime.Now.ToLongTimeString() + "\n\n");
count = 0;
Response.Flush();
}
if (!Response.IsClientConnected){break;}
Thread.Sleep(1000);
count++;
} while (count < 30); //end after 30 seconds of no pings
}
The line of code that makes the difference is the second Response.Write. The message doesn't appear in the browser until the next ping similar to your issue, but the ping always appears. Without that line the ping will appear only after the next ping, or once my 30 second counter runs out.
The missing message appearing after the 30 second timer leads me to conclude that this is either a .Net issue, or there's something we're missing. It doesn't seem to be an event source issue because the message appears on a server event, and I've had no trouble doing SSE with PHP.
For reference, here's the JavaScript and HTML I used to test with.
<input type="text" id="pingid" placeholder="ID" /><br />
<input type="button" id="ping" value="Ping" />
<div id="timeresponse"></div>
<div id="pingresponse"></div>
<script>
var es = new EventSource('/Home/Message');
es.onmessage = function (e) {
console.log(e.data);
document.getElementById('pingresponse').innerHTML += e.data + " - onmessage<br/>";
};
es.addEventListener("ping", function (e) {
console.log(e.data);
document.getElementById('pingresponse').innerHTML += e.data + " - onping<br/>";
}, false);
es.addEventListener("time", function (e) {
document.getElementById('timeresponse').innerHTML = e.data;
}, false);
es.onerror = function () {
console.log(arguments);
console.log("event source closed");
es.close();
};
window.onload = function(){
document.getElementById('ping').onclick = function () {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onload = function () {
console.log(this.responseText);
};
var url = '/Home/Ping?id=' + document.getElementById('pingid').value;
xmlhttp.open("GET", url);
xmlhttp.send();
};
};
</script>
Since an eventstream is just text data, missing the double line break before the first event is written to response could affect the client. The example from mdn docs suggests
header("Content-Type: text/event-stream\n\n");
Which could be applied apply to .NET response handling (note the side effects of Response.ClearContent()).
If it feels too hacky, you could start your stream with a keep-alive comment (if you want to avoid timing out you may have to send comments periodically):
: just a keep-alive comment followed by two line-breaks, Response.Write me first
I'm not sure if this will work because I can't try it now, but what about to add an End?:
Response.Flush();
Response.End();
The default behavior of .net is to serialize access to session state. It blocks parallel execution. Requests are processed sequentially and access to session state is exclusive for the session. You can override the default state per class.
[SessionState(SessionStateBehavior.Disabled)]
public class MyPulsingController
{
}
There is an illustration of this in the question here.
EDIT: Would you please try creating the object first and then passing it to Enqueue? As in:
PingData myData = new PingData { UserID = userID };
pings.Enqueue(myData);
There might be something strange going on where Dequeue thinks it's done the job but the the PingData object isn't properly constructed yet.
Also can we try console.log("I made it to the function") instead of console.log(e.data).
---- PREVIOUS INFORMATION REQUESTED BELOW ----
Please make sure that the server Debug.Print confirms this line of code:
Response.Write("data:" + JsonConvert.SerializeObject(nextPing, Formatting.None) + "\n\n");
Is actually executed? Please double check this. If you can capture the server sent response then can we see what it is?
Also could we see what browsers you've tested on? Not all browsers support server events.
Ok here's what I'm trying to do.
I have this custom action (button on my SharePoint-ribbon). This should call a Javascript, which in turn should call a C#-code.
I have the following:
<CustomAction
Id="Ribbon.Documents.DocsetZip"
Title="Download Document Set as ZIP"
RegistrationType="ContentType"
RegistrationId="0x0120D520"
Location="CommandUI.Ribbon"
>
<CommandUIExtension>
<CommandUIDefinitions>
<CommandUIDefinition
Location="Ribbon.Documents.Share.Controls._children">
<Button Id="Ribbon.Document.Share.DownasZip"
Sequence="20"
Command="Ribbon.ManageDocumentSet.MDS.Manage.DownZip"
Alt="Download as ZIP"
Image16by16="/_layouts/images/zipfile16x.png"
Image32by32="/_layouts/images/zipfile32x.png"
LabelText="Download as ZIP file"
ToolTipTitle="Download as ZIP file"
ToolTipDescription="Compress the document set and download"
TemplateAlias="o1"/>
</CommandUIDefinition>
</CommandUIDefinitions>
<CommandUIHandlers>
<CommandUIHandler
Command="Ribbon.ManageDocumentSet.MDS.Manage.DownZip"
CommandAction="javascript:__doPostBack('DownloadZipDelegateEvent', '')" />
</CommandUIHandlers>
</CommandUIExtension>
And i have a class:
public class MyRibbonDelegateClass : WebControl
{
protected override void OnLoad(EventArgs e)
{
this.EnsureChildControls();
base.OnLoad(e);
if (this.Page.Request["__EVENTTARGET"] == "DownloadZipDelegateEvent")
{
using (TextWriter writer = File.CreateText("C:\\temp\\perl.txt"))
{
//
// Write one line.
//
writer.WriteLine("First line");
//
// Write two strings.
//
writer.Write("A ");
writer.Write("B ");
//
// Write the default newline.
//
writer.Write(writer.NewLine);
}
}
}
It seems my code gets executed, but I cannot find my file anywhere.
What am I missing?
you can use __DoPostback to invoke a server side hit from javascript.
<script type="text/javascript">
function ServerPostWithParameter(parameter)
{
__doPostBack('btnSave', parameter)
}
</script>
in server side,
public void Page_Load(object sender, EventArgs e)
{
string parameter = Request["__EVENTARGUMENT"]; // this is your parameters
// Request["__EVENTTARGET"]; // this is your button
}
You can just create an HttpHandler with your server-side code and call it with parameters from JavaScript.
E.g. create an ~sitecollection/_layouts/15/MyCustomHandler.ashx and call it from JavaScript like this (SharePoint 2013 uses virtual path to layouts directory as '_layouts/15', SharePoint 2010 -- just '_layouts'):
$.get(_spPageContextInfo.siteServerRelativeUrl + '/_layouts/15/MyCustomHandler.ashx?Param1=Value1&Param2=Value2');
I've solved it as follows :
function getOutlook() {
var xmlHttpReq = createXMLHttpRequest();
xmlHttpReq.open("GET", _spPageContextInfo.siteServerRelativeUrl + "/_layouts/SendDocuments/MyCustomHandler.ashx?ItemsArray=" + fileRefArray, false);
xmlHttpReq.send(null);
}
function createXMLHttpRequest() {
try { return new XMLHttpRequest(); } catch (e) { }
try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { }
try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { }
alert("XMLHttpRequest not supported");
return null;
}
I am using Following code..
When I click on the link, the javascript Hello() function is invoked
I want to use window.location.href
But when I use this the following __doPostBack('Button2_Click'), it does not work.
But when remove window.location.href from the following code then __doPostBack('Button2_Click') does work.
<script type="text/javascript">
function Hello(clicked_id) {
var abc = "http://localhost:2621/OrgChart.aspx?id" + clicked_id;
window.location.href = abc;
__doPostBack('Button2_Click');
return false;
}
</script>
<a id="A1" href="javascript:Hello();">LINK</a>
This is my code behind code...
public partial class WebForm17 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
ClientScript.GetPostBackEventReference(this, string.Empty);//This is important to make the "__doPostBack()" method, works properly
if (Request.Form["__EVENTTARGET"] == "Button2_Click")
{
//call the method
Button2_Click(this, new EventArgs());
}
}
protected void Button2_Click(object sender, EventArgs e)
{
Label1.Text = "Method called!!!";
EmpInfo emp = new EmpInfo();
DA_EmpInfo da_emp = new DA_EmpInfo();
List<EmpInfo> lei = da_emp.GetAllEmployeeInfoByEmpId("MJ-IB-1");
DetailsView1.DataSource = lei;
DetailsView1.DataBind();
}
}
I guess, __doPostBack is making a request to the server and you break it by using window.location.href = abc;.
You should use some callback from this request to redirect to your url.
try to use setTimeOut function
setTimeout(function () {
window.location.href = abc;
}, 1000);
this will wait 1 second for finish of __doPostBack() function.
Or if you don't want to use timeOut, paste window.location.href = abc; line to end of the __doPostBack() function.