How do I refresh UI on request? - c#

I've a Web app with a button that makes a call to an API to refresh data:
[AcceptVerbs("GET")]
[Route("refreshFromService/{guid}")]
public HttpResponseMessage RefreshDataFromService(string guid)
{
if (!string.IsNullOrEmpty(guid) && guid.Length == 36 && new Guid(guid) == new Guid("C943F8E4-647D-4044-B19E-4D97FA38EDE0"))
{
new AdData().RefreshCacheAdData(true);
new JdeData().RefreshCacheJdeData(true);
return new HttpResponseMessage(HttpStatusCode.OK);
}
return new HttpResponseMessage(HttpStatusCode.Forbidden);
}
Actually, it's an AJAX call, so in my Network Tab in Google Chrome, I see the request is in pending for 5 minutes.
How can I make this method an async method and how can I refresh my UI to show progress?
EDIT
When I refresh the page, I want the Progress Status to be updated.

First of all, it has nothing to do with a backend. So solution to your problem lies on frontend site. Ajax are asynchronous by nature so you can do something like that.
const loader = document.createElement('span')
// some styles for the loader
// i'm using body just for example purposes
document.body.appendChild(loader)
fetch('refreshFromService/{guid}')
.then(data => {
document.body.removeChild(loader)
// do something with data
return data
})

You have to handle it on UI like this:
function getFlag() {
var option = {
url: '/controllerName/actionName',
data: JSON.stringify({}),
method: 'post',
dataType: 'json',
contentType: 'application/json;charset=utf-8'
};
$.ajax(option).success(function (data) {
$("#picture").append("<img id=\"img1\" src=" + data.img_path + "\ />");
});
};
I am using this code in UI for getting flags at runtime. So you need to write same type of code and get response from the backend.
url: '/controllerName/actionName' is the controller in MVC and then action implemented in that controller.
Request this method in UI with document.ready
I hope I have made sense to you. If still not clear write back i will explain further.
Cheers!

Related

Export iCal from database

I'm trying to figure out how to be able to export an iCal file from my calendar. I can't get it to work, it does not start to "download" the file.
Right now I just try to get one meeting, but later on I will make a for loop to get all the meetings in the database to the iCal file, but I just want to check if it works, but it does not.
This below is my method in the controller and later the jQuery to call the method.
[HttpPost]
public ActionResult AddToICalendar()
{
var ctx = new OruBloggenDbContext();
var meetings = ctx.Meetings.FirstOrDefault(u => u.MeetingID == 1);
var icalStringbuilder = new StringBuilder();
icalStringbuilder.AppendLine("BEGIN:VCALENDAR");
icalStringbuilder.AppendLine("PRODID:-//MyTestProject//EN");
icalStringbuilder.AppendLine("VERSION:2.0");
icalStringbuilder.AppendLine("BEGIN:VEVENT");
icalStringbuilder.AppendLine("SUMMARY;LANGUAGE=en-us:" + meetings.MeetingTitle);
icalStringbuilder.AppendLine("CLASS:PUBLIC");
icalStringbuilder.AppendLine(string.Format("CREATED:{0:yyyyMMddTHHmmssZ}", DateTime.UtcNow));
icalStringbuilder.AppendLine("DESCRIPTION:" + meetings.MeetingDesc);
icalStringbuilder.AppendLine(string.Format("DTSTART:{0:yyyyMMddTHHmmssZ}", meetings.MeetingStartDate));
icalStringbuilder.AppendLine(string.Format("DTEND:{0:yyyyMMddTHHmmssZ}", meetings.MeetingEndDate));
icalStringbuilder.AppendLine("SEQUENCE:0");
icalStringbuilder.AppendLine("UID:" + Guid.NewGuid());
icalStringbuilder.AppendLine("END:VEVENT");
icalStringbuilder.AppendLine("END:VCALENDAR");
var bytes = Encoding.UTF8.GetBytes(icalStringbuilder.ToString());
return this.File(bytes, "text/calendar", "ical.ics");
}
Javascript:
<script>
$(function () {
$(document)
.on("click", "#icalBtn", function () {
exportiCal();
});
function exportiCal() {
$.ajax({
url: '/MeetingCalendar/AddToICalendar',
type: "POST",
//data: { downloadFileName = "thisEvent.ics" },
success: function (data) {
alert("hejejje");
}
});
}
});
</script>
The reason nothing downloads is because you can't download files via AJAX. Instead of being delivered to a file on your computer's disk, the downloaded content is going into the data variable in your "success" function.
To solve it, instead of using AJAX, make your action method accept GET requests and just user a regular hyperlink to link the user to it:
[HttpGet]
public ActionResult AddToICalendar()
{
//..etc
and
Download to iCalendar
You can remove all your jQuery code.
N.B. Logging that data value to the console (instead of just alerting meaningless junk) - or using your browser's network tools to see what is going on - would have let you see that, and you might have seen the problem sooner...maybe you need to do a bit more debugging in future.

Parse JSON from C# to AngularJS

I have an array of file names:
[HttpPost]
public JsonResult GetJSONFilesList()
{
string[] filesArray = Directory.GetFiles("/UploadedFiles/");
for (int i = 0; i < filesArray.Length; i++)
{
filesArray[i] = Path.GetFileName(filesArray[i]);
}
return Json(filesArray);
}
I need this in AngularJS as a list of objects so I can ng-repeat it out and apply filters. I'm unable to figure out how to get the JSON from the MVC controller to AngularJS.
I've tried the following to make it visible to the view for angular to grab, but I don't know how to make the ng-init see the function to return the list. It erros on "SerializeObject(GetJSONFilesList())" saying it doesn't exist in current context.
<div ng-controller="MyController" data-ng-init="init(#Newtonsoft.Json.JsonConvert.SerializeObject(GetJSONFilesList()),
#Newtonsoft.Json.JsonConvert.SerializeObject(Model.Done))" ng-cloak>
</div>
EDIT:
I've tried using http.get.
Test one:
alert('page load');
$scope.hello = 'hello';
$http.get('http://rest-service.guides.spring.io/greeting').
then(function (response) {
$scope.greeting = response.data;
alert($scope.greeting);
});
alert($scope.hello);
The alert in the http.get never fires, the other alerts do however.
Test two:
$http({
url: '/Home/testHello',
method: 'GET'
}).success(function (data, status, headers, config) {
$scope.hello = data;
alert('hi');
});
[HttpPost]
public string testHello()
{
return "hello world";
}
This causes the angular to break and nothing in the .js works.
Test three
alert('page load');
$scope.hello = 'hello';
$scope.GetJSONFilesList = function () {
$http.get('/Home/testHello')
.success(function (result) {
$scope.availableFiles = result;
alert('success');
})
.error(function (data) {
console.log(data);
alert('error');
});
alert('hi');
};
alert($scope.hello);
[HttpPost]
public string testHello()
{
return "hello world";
}
Alerts nothing from within it, other alerts work.
Fixed:
After some googling, I've found that using .success and .error are deprecated and that .then should be used. So by using .then this resulted in the C# being hit via debug.
Then after using console.log on the returned value found that to have anything be returned I needed to return the value from C# using "return Json(myValue, JsonRequestBehavior.AllowGet); "
And by viewing the object in the console in Chrome by using console.log, I could see my values were in the data part of the returned object.
It was stored in data as an array (as I was passing an array).
I could then get the data out of there by assigning the returned value.data to a scope and could call that in the view {{result[1]}} etc.
return Json(filesArray, JsonRequestBehavior.AllowGet);
$scope.fileList;
$http.get("/Home/GetFileList").then(function (result) {
console.log(result)
$scope.fileList = result.data;
})
Imagine that you divide your front end in three layers (MVC or MVVM) whatever you want.
When you need info from server, the best practice is to separate the logic that makes the request and the logic that manipulates the data.
More info about how to make the request you can find it reading about REST APIS in Consuming a RESTful Web Service with AngularJS.
Normally one of the layers requires the use of services and you can have your controllers and your services (the place where you get the raw data from the server and you make the request. For that you need to use the $http service from angularjs.
$http: The $http service is a core AngularJS service that facilitates communication with the remote HTTP servers via the browser's XMLHttpRequest object or via JSONP.
So basically it shows you how to make get, post and put requests. One example from the documentation is :
// Simple GET request example:
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
Pay attention to the url because there is the place where you let your request knwow which method is going to be hit on the server to take the action. If your request is succesful, then you can use the parameter called response. From there, you can do whatever you want. If you decide to make that request part from your controller, you can assign it directly to a variable on your scope. Pay attention if you need to serialize the data. Something like
$scope.myResponseName = response.name ;
The first documentation link from above shows this example which does exactly what I tell you.
angular.module('demo', [])
.controller('Hello', function($scope, $http) {
$http.get('http://rest-service.guides.spring.io/greeting').
then(function(response) {
$scope.greeting = response.data;
});
});
After all the mentioned above, pay attention to what you want to display. Are you going to display the elements of an object array? The use on your HTML the ng-repeat directive. Are you going to display just a variable (No array nor object) then you use need to use an angular expression {{ }}
In summary:
By making an HTTP request, hit the correct method on server.
Make sure you are sending the JSON correctly and that the data is correct.
Retrieve the data on your response.
Assign the data to a variable on your scope and serialize the data if needed.
Display the data correctly depending if it is within an array, if it´s an object or if its just a variable.
I hope the explanation makes sense and check the documentation if you need more info.
You can build your viewmodel so that it contains the data you'd like to serialize and then pass it to angularJS in your view as follows:
<div ng-controller="MyController" data-ng-init="init(#JsonConvert.SerializeObject(myArrayData),
#Newtonsoft.Json.JsonConvert.SerializeObject(Model.Done))" ng-cloak>
and then in your angular controller have a function to receive the data as follows:
$scope.init = function (myArrayData) {
//do something with data
};
The above assumes you're trying to pass data from mvc to angularjs on page load. If you're trying to hit a controller and get data back to angularjs upon some event such as a button click, then you can write an angularjs function similar to the following (this will be an asynchronous request):
app.controller('MyController', function ($scope, $http, $window) {
$scope.ButtonClick = function () {
var post = $http({
method: "POST",
url: "/SomeController/SomeAjaxMethod",
dataType: 'json',
data: { path: $scope.Path},
headers: { "Content-Type": "application/json" }
});
post.success(function (data, status) {
//do something with your data
});
post.error(function (data, status) {
$window.alert(data.Message);
});
}
}
and your controller action would look something like:
[HttpPost]
public JsonResult SomeAjaxMethod(string path)
{
string[] filesArray = Directory.GetFiles(path);
for (int i = 0; i < filesArray.Length; i++)
{
filesArray[i] = Path.GetFileName(filesArray[i]);
}
return Json(filesArray);
}
other answers say to use .success in the angular function, .success and .error are deprecated, instead .then should be used.
Working result:
MVC:
public JsonResult GetFileList()
{
//form array here
return Json(myArray, JsonRequestBehavior.AllowGet);
}
The function needs to be of type JsonResult, and the returned value of Json using JsonRequestBehavior.AllowGet.
AngularJS:
$scope.fileList;
$http.get("/Home/GetFileList").then(function (result) {
console.log(result)
$scope.fileList = result.data;
})
This is in my AJS controller, using .then instead of .success. If you use console.log the result returned from the mvc controller and view it in the browser inspect you'll see the object with lots of other info and the values you want are in the .data section of the object.
So to access the values you need to do result.data. In my case this gives me and array. I assign this to a scope. Then in my view I can access the values by doing {{fileList[1]}} etc. This can also be used in ng-repeat e.g:
<div ng-repeat="file in fileList">
{{fileList[$index]}}
</div>
Each value in the array in the repeat can be accessed using $index which is the number of the repeat starting at 0.

Push Table to screen

I have an AJAX request when a branch of my JSSTree is clicked
$("#jstree").bind("select_node.jstree", function(evt, data)
{
var idArgument = data.node.text;
$.ajax(
{
type: "POST",
url: "WebForm1.aspx/brancheSelectionnee",
data: JSON.stringify({ id: idArgument }),
contentType: "application/json; charset=utf-8",
success: function(msg)
{
;
}
});
});
So, I call this function, which make a new "page" (because it's static) and call a function that return a System.Web.UI.WebControls.Table.
public static string brancheSelectionnee(string id)
{
var page = (WebForm1)HttpContext.Current.CurrentHandler;
System.Web.UI.WebControls.Table tableau = page.brancheSelectionneeNonStatique(id);
var stringWriter = new StringWriter();
using (var htmlWriter = new HtmlTextWriter(stringWriter))
{
tableau.RenderControl(htmlWriter);
}
string tableauString=stringWriter.ToString();
return "randomstring";
}
Big problem here: My "tableau" is updated, with what I want (I see this with the htmlWriter) but.. I don't know how put it in my screen!
I have it in my C# code, but I want it in the screen, and not just here.
I have "tableauArticle" which is a real System.Web.UI.WebControls.Table, in my ASP.net code.
I tried some things, like putting "tableauArticle" as Static, then
tableauArticles = tableau;
But I didn't see any changement. I think that I updated a table in the page that I don't display
I think that the main problem is that my pagee isn't refresh or I do my tables wrong.
You do an AJAX request, so there is no page refresh. You just get a string (with HTML) back from your server method. You then have to manually put that string on your page. This happens in the success callback function which in your code is empty. As first step try something like this:
success: function(msg)
{
$('<div class="newtable">').html(msg).appendTo('body');
}
On the server-side your method brancheSelectionnee needs the AjaxMethod attribute so that it can be called with AJAX:
[Ajax.AjaxMethod()]
public static string brancheSelectionnee(string id)
(It also should return tableauString; not "randomstring", right?. And I am not sure if you can use the HttpContext.Current.CurrentHandler there, but that is for a second step if the basic AJAX stuff works.)
Here is one tutorial for all this which gives you an overview.
For the answer, it is 100% Raidri solution :
$('#tableauArticles').empty();
$('<div class="newtable">').html(msg.d).appendTo('#tableauArticles');

How can I add a background thread in form load function for notification in ASP.NET?

I want to receive notification from database when table is updated but I need a background thread which call after 1 mint and check database table and show me notification. How can I implement a background thread? I have implemented a thread but when I use while(true) in it, my form is not loaded keep on processing.
Code:
protected void Page_Load(object sender, EventArgs e)
{
t1 = new Thread(new ThreadStart(Function1));
t1.IsBackground = true;
t1.Start();
}
private void Function1()
{
while (true)
{
Thread.Sleep(2000);
count++;
Label1.Text = "Function" + count;
}
}
You have a fundamental misunderstanding of the difference between server-side and client-side code.
For example, your request would require a framework such as SignalR to push a real-time notification to a client.
The easier method is to use Javascript to poll a page of your choice.. for example (jQuery, obviously):
// I'm not a huge jQuery person so theres probably a jQuery way to do this
setInterval(function() {
$.get('/yourPage.aspx', function(response) {
if (response.success) {
alert(response.message);
}
});
}, 5000); // poll a page every 5 seconds.
Then your C# page at /yourPage.aspx can check the database and return a Json object with the properties I've mentioned above.
You need to read up on the difference between Client Side and Server Side.. and how they interact in a stateless protocol such as HTTP.
You can not use background thread on asp.net page. It works on http stateless protocol and the page object is not available after the response it sent. You can only send one response against one request. You can use jQuery ajax , asp.net ajax library timer control or web sockets to fetch data from server periodically.
This post explains how you can fetch data from server using jQuery ajax. This is also very good tutorial to get the data from server using web methods. The example from this tutorial is given below.
Code behind
public partial class _Default : Page
{
[WebMethod]
public static string GetDate()
{
return DateTime.Now.ToString();
}
}
Javascript
$(document).ready(function() {
// Add the page method call as an onclick handler for the div.
$("#Result").click(function() {
$.ajax({
type: "POST",
url: "Default.aspx/GetDate",
data: "{}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(msg) {
// Replace the div's content with the page method's return.
$("#Result").text(msg.d);
}
});
});
});
You can do async tasks in ASP.NET if you are using the 4.5 framework:
http://www.hanselman.com/blog/TheMagicOfUsingAsynchronousMethodsInASPNET45PlusAnImportantGotcha.aspx

How to use custom AuthorizeAttribute with AJAX

With help of fellow friends I managed to find a solution for my problem from this topic: Reusable way to allow an account to be used by a single person at a time
I have a SingleLogin class which inherits from AuthorizeAttribute and implements a custom AuthorizeCore method for the purpose of re-usability of my single-login code:
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
bool isAuthorized = base.AuthorizeCore(httpContext);
if (isAuthorized)
{
int userId = (int)WebSecurity.CurrentUserId;
using (var db = new UsersContext())
{
if ((httpContext.Session.SessionID != db.getSessionId(userId))
|| db.getSessionId(userId) == null)
{
WebSecurity.Logout();
isAuthorized = false;
httpContext.Response.Redirect("/Home/Index");
}
}
}
return isAuthorized;
}
Everything works fine except my JsonResult action:
[HttpPost]
public JsonResult MessageSave(string message)
{
bool messageSaved = false;
int userId = (int)WebSecurity.CurrentUserId;
message = HttpUtility.HtmlEncode(message);
// Model method - adding chat log - db
db.addChatLog(message, userId);
messageSaved = true;
return Json(new { messageSaved = messageSaved });
}
This method is triggered by Ajax POST call which you can see in code example below. Just basic POST.
EDIT 3
Please check these images: http://imgur.com/a/Cjael .. Hm I guess POST does trigger, but have no idea why does my alert not work when I try to test it before $.ajax ... As you can see in response I do get Home/Index page but I am not redirected to home/index immediately(text stays inside of textBox and page just waits..), I have to push enter one more time to be redirected.. Very strange.
EDIT2
Seems like I can't even access my jQuery even after I get logged out. I put some alerts inside of my .js file.
I have a separate .js file which is then put in my View as <script src="~/Scripts/custom/homeChat.js"></script> . I pass the Razor values from View into my JS file via HTML5 data-.
My textBox element #txtMsg, triggers my jQuery event, therefore when I am logged out it probably doesn't recognize my textBox element anymore, and doesn't trigger my jQuery event?
Element that triggers .js in view is:
#Html.TextBox("txtMsg")
JS:
$("#txtMsg").keypress(function (e) {
//when enter
if (e.which == 13) {
alert("ALERT DOESNT TRIGGER");
$.ajax({
type: "POST",
url: url,
data: JSON.stringify({ message: input }),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {
if (data.messageSaved) {
$("#txtMsg").val("");
}
else {
window.location.href = urlhome;
}
}
});
}
}
});
So if you can't even come into your event, how can you even know something went wrong? I have this ˙HandleUnauthorizedRequest but you are required that you can get into your jQuery event(in my case .keypress in the js code above) for this to work if I understand right.
EDIT: Additional explanation
So let me explain the scenario. If I login with my username "john" from Firefox and again with username "john" from chrome, next action I do in Firefox, it will log me out and redirect me to Home/Index, because someone else made a new login in Chrome.
That is ok. Since you are not logged in anymore, you get redirected normally to your Home/Index if your action is normal ActionResult and returns view.
The problem I have is, that I have some other functionality in the page, which uses Ajax POST, and since you are logged out you can't POST to that JsonResult action therefore you can't even receive callback of error, which redirects you to Home/Index. I put some alerts into my JS, but no alert triggers which is normal, because I am not allowed on that page anymore anyway. If I want that my onEnter textbox redirects me to Home/Index I have to press enter twice. Is that all that could be done?
I am interested in best approach for this AJAX problem. I don't know how I should call it, but as I read from my previous topic it is called "handling AJAX timeouts"?
Thank you very much.
You can handle errors on AJAX request this way
$.ajax({
type: "POST",
url: url,
data: JSON.stringify({ message: input }),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {
if (data.messageSaved) {
$("#txtMsg").val("");
}
else {
window.location.href = urlhome;
}
},
error: function(xhr, status, error) {
// TODO: may be check error or status or xhr.statusCode()
window.location.href = urlhome;
}
});
jQuery $.ajax() docs
If understand it correctly you want to handle the unauthorized ajax request.
In that case you can override the HandleUnauthorizedRequest method in your attribute:
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
filterContext.Result = new JsonResult();
}
else
{
filterContext.Result = new HttpStatusCodeResult((int)HttpStatusCode.Forbidden);
}
}

Categories