How to improve the solution - asp.net-mvc

This solution works, but I think it can much better be done
JQuery:
$('#addMessage').click(function () {
var textMessage = $('#ticketMessage').val();
var isInternal = $('#isInternal')[0].checked;
var ticketID = $('#TicketID').val();
$.ajax({
url: '/Ticket/AddMessage',
type: 'POST',
data: { textMessage: textMessage, isInternal: isInternal, ticketID: ticketID },
success: function (data) {
var tbody = $('#allMessages').children()[0];
tbody.innerHTML = tbody.innerHTML + data;
$('#ticketMessage').val("");
$('#isInternal')[0].checked = false;
}
});
});
controller
public string AddMessage(string textMessage, bool isInternal, int ticketID)
{
Message message = new Message();
message.IsInternal = isInternal;
message.TicketMessage = textMessage;
message.TicketID = ticketID;
DateTime created=DateTime.Now;
message.CreatedDateTime = created;
message.PersonID = AppSecurity.Security.GetPersonID(Session);
var personRepository = new PersonRepository(_context);
MessageRepository messageRepository = new MessageRepository(_context);
messageRepository.Add(message);
_context.SaveChanges();
string relSrc = (personRepository.GetById((int)message.PersonID) as Employee).Image;
string source = "";
string isInternalStr = "";
if (message.IsInternal)
isInternalStr = "Internal";
if (message.Person is Employee) { source = relSrc != null ? "../../Images/TicketFiles" + relSrc.Replace('\\', '/') : "../../Images/TicketFiles/Employees/no-profile.png"; }
String response = "<tr><td style=\"width: 25%\" valign=\"top\"><table><tr>"
+ "<td><img src=\""+source+"\" alt=\"\" style=\"height: 60px\"/></td>"
+ "</tr><tr><td>"
+ AppSecurity.Security.GetUserFullName(Session)
+ "</td></tr><tr><td>"
+ created.ToString("dd.MM.yyyy") + " - " + created.ToString("HH:mm:ss")
+ "</td></tr></table></td><td style=\"width: 75%; padding:0px;\" valign=\"top\"><table style=\"width: 100%; height: 130px\" cellspacing=\"0\" cellpadding=\"0\">"
+ "<tr><td style=\"height: 20px; padding: 0px\">" + isInternalStr + "</td></tr><tr><td valign=\"top\">" + message.TicketMessage + "</td><tr></table></td></tr>";
return response;
}

Instead of generating the markup in the controller why not do it client side in the JavaScript? Your controller should not be concerned with markup

It seems to me you can benefit a lot by using jquery templates

Your controller should be of type ActionResult and as mr.nicksta said, should be made in a view.
public ActionResult AddMessage(string textMessage, bool isInternal, int ticketID)
{
...
return View(message);
}
and then create a view with the same as the controller, strongly-typed with Message.
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage<Message>" %>
Where you create the display. You could even do this as a partial view to it can be included in other pages more easily.

Related

How to convert array to send as query string?

In my view, I have checkboxes and some data displayed and button on each row to approve or reject requests put up.
I want to send my integer array to my action method but this cannot be done by just sending it to action query parameters and it would be like the picture below:
public int[] Ids
{
get { return new int[] { 1, 2, 3 }; }
set {}
}
public ActionResult Approval([ModelBinder(typeof(IntArrayModelBinder))] int[] ids)
{
...
return View(...);
}
#Html.ActionLink("Approve", "Approval", new {id = item.Ids, approvalAction = "approve"})
How do I implement the checkboxes to be checked and hover of the approve/reject actionlink will show the url with ../ids=1&ids=2&ids=3 instead of System.Int32[]?
Option 1: Send your array as a comma-separated string and then split them in your action like this :
#Html.ActionLink("Approve", "Approval", new { id = string.Join("," , Ids), approvalAction = "approve" } )
your action :
public ActionResult YourAction(string id , string approvalAction)
{
var ids = id.Split(',');
//rest of your action method business
}
Option 2:
another way to achieve your exactly url is to create your URL like this :
var baseUrl = Url.Action("YourAction", "YourController", null, Request.Url.Scheme);
var uriBuilder = new UriBuilder(baseUrl);
uriBuilder.Query = string.Join("&", Ids.Select(x => "ids=" + x));
string url = uriBuilder.ToString();
url += "&approvalAction=approve"
and your action would be like this :
public ActionResult YourAction(int[] ids , string approvalAction)
{}
<script>
function multiSelect(selectedArray, action) {
if (selectedArray[0] === undefined) {
alert("You have not selected any employee");
}
//example url
//..?ids=1&ids=2&ids=3&aprovalAction=approve
else {
var param = "";
var currUrl = "";
var idUrl = "";
idUrl = "ids=" + selectedArray[0];
for (var i = 1; i < selectedArray.length; ++i) {
idUrl += "&ids=" + selectedArray[i];
}
switch (action) {
case "Approve":
param = "approve";
break;
case "Reject":
param = "reject";
break;
}
currUrl = "approvalAction=" + param;
window.location.href = "?" + idUrl + "&" + currUrl;
}
}
</script>
<script>
$('#MultiApproveBtn').click(function () {
var selected = $('input[type=checkbox]:checked').map(function (_, el) {
return $(el).val();
}).get();
var x = document.getElementById("MultiApproveBtn").value;
//alert(selected);
multiSelect(selected, x);
})
</script>
<script>
$('#MultiRejectBtn').click(function () {
var selected = $('input[type=checkbox]:checked').map(function (_, el) {
return $(el).val();
}).get();
var x = document.getElementById("MultiRejectBtn").value;
//alert(selected);
multiSelect(selected, x);
})
</script>

url.action doesn't generate url in asp.net mvc

I am trying to pass image url and id number to SaveImage() function in DataController with #Url.Action() but it is not working.
I debugged the program and it seems that after the url.action method, a method named Dispose is being activated.
I saw other answers to similar questions but they didn't help me because I already use this: ' ' and not this: " " and I didn't use Jquery or Ajax.
my js function:
<script language="JavaScript">
var obj = #Html.Raw(Json.Encode(Model));
var uri;
function take_snapshot() {
Webcam.snap(function (data_uri) {
document.getElementById('results').innerHTML =
'<img src="' +
data_uri +
'"/>';
//uri = data_uri;
var id = obj.id;
alert("debug 2");
var url = '#Url.Action("SaveImage","Data")';
window.location.href = url + '?im_url=' + data_uri + '&id=' + id;
});
}
</script>
SaveImage() method:
public ActionResult SaveImage(String im_url, String id)
{
int x = Int32.Parse(id);
string s = im_url;
return View();
}
the Dispose function that was created automatically:
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
It seems that your url is not generating fine. Please use this code to generate your url.
var link = '#Url.Action("SaveImage","Data", new{ im_url="-1", id="-2"})'.replace("-1",data_uri).replace("-2",id);
window.location.href = link;
Hopefully it will resolve your problem.
I don't know what the problem was but I solved it, I moved the line that generates the url outside the webcam function.
from:
function take_snapshot() {
Webcam.snap(function (data_uri) {
document.getElementById('results').innerHTML =
'<img src="' +
data_uri +
'"/>';
//uri = data_uri;
var id = obj.id;
alert("debug 2");
var url = '#Url.Action("SaveImage","Data")';
window.location.href = url + '?im_url=' + data_uri + '&id=' + id;
});
}
to:
function take_snapshot() {
var url = '#Url.Action("SaveImage","Data")';
Webcam.snap(function (data_uri) {
document.getElementById('results').innerHTML =
'<img src="' +
data_uri +
'"/>';
//uri = data_uri;
var id = obj.id;
alert("debug 2");
window.location.href = url + '?im_url=' + data_uri + '&id=' + id;
});
}

Stop process if condition is null and return message

I have a controller like this:
public ActionResult CargaCSV(HttpPostedFileBase archivo)
{
List<string[]> registros = new List<string[]>();
confService = new ConfiguracionService();
if (archivo.ContentLength > 0)
{
StreamReader sR = new StreamReader(archivo.InputStream);
string linea;
string resultado = "";
var f = 0;
while ((linea = sR.ReadLine()) != null)
{
if (f == 0)
{
f++;
continue;
}
else
{
String[] registros_ = linea.Split(',');
registros.Add(registros_);
}
}
sR.Close();
ViewBag.Message = resultado;
confService.GuardarMatrizCsv(registros,usuarioCaptura);
}
return Json(registros);
}
As you can see I return result into div:
ViewBag.Message = resultado;
View:
<div class="" style="height: 150px; overflow: auto; margin-bottom:10px;">
<pre id="resultado">#ViewBag.Message</pre>
</div>
That I want to do is to cancel all proccess if (archivo.ContentLength > 0) return null and return: ViewBag.Message = "you need attach file to load it"; and stop all process
How can I achieve this?
Update:
I call CargaCSV method with ajax call:
$("#btnCargarCsv").on("click", function () {
var data = new FormData();
data.append("archivo", $("#archivo")[0].files[0]);
$.ajax({
"type": "POST",
"url": "/Configuracion/CargaCSV",
"contentType": false,
"processData": false,
"data": data,
success: function (s) {
var result = $("#resultado");
result.text("");
result.append("<table>");
result.append("<tr>");
result.append("<th> Unidad </th>");
result.append("<th> CR Mínimo </th>");
result.append("<th> CR Máximo </th>");
result.append("<th> % Mínimo </th>");
result.append("<th> % Máximo </th>");
result.append("</tr>");
for (var i = 0; i < s.length; i++) {
var val = s[i];
result.append("<tr>");
result.append("<td> " + val[0] + " </td>");
result.append("<td> " + val[1] + " </td>");
result.append("<td> " + val[2] + " </td>");
result.append("<td> " + val[3] + " </td>");
result.append("<td> " + val[4] + " </td>");
result.append("</tr>");
}
result.append("</table>");
}
});
});
So here I append data into table and show data into pre html tag "resultado"
Your returning a JsonResult, not a ViewResult, so setting a ViewBag property is pointless (ViewBag is for passing data to a View) and your
<pre id="resultado">#ViewBag.Message</pre>
element will only ever display the initial value of ViewBag.Message when the page is first rendered.
You need to return the message in your JsonResult and test it in the ajax success method.
Change the controller code to return different values based on your condition
public ActionResult CargaCSV(HttpPostedFileBase archivo)
{
if (archivo.ContentLength = 0)
{
string message = "you need attach file to load it";
return Json( new { message = message });
}
.... // your code to process the file
return Json(new { registros = registros });
}
and change the code in the success callback to test the result
success: function (data) {
if (data.message) {
$('#resultado').text(data.message);
} else if (data.registros) {
var result = $("#resultado");
....
for (var i = 0; i < data.registros.length; i++) {
var val = data.registros[i];
....
}
result.append("</table>");
}
}
As a side note, you could simplify your script by generating the <table> and its headings (which should be in a <thead> element) in the view (and style it using display: none;and just add the <tr> elements inside its <tbody> element (and then show/hide the table in the success callback), for example
<table id="resultstable" style="display:none">
<thead>
....
</thead>
<tbody id="results"></tbody>
<table>
Script
var table = $('#resultstable');
var results = $('#results');
var message = $('#resultado');
$('btnCargarCsv').on('click', function () {
....
$.ajax({
....
success: function (data) {
if (data.message) {
message.text(data.message);
table.hide();
} else if (data.registros) {
results.empty(); //remove any existing rows
table.show();
for (var i = 0; i < data.registros.length; i++) {
var val = data.registros[i];
var row = $('<tr></tr>');
for(int j = 0; j < val.length; j++) {
row.append($('<td></td>').text(val[j]));
}
results.append(row);
}
}
}
});
});
In addition, you should be testing if the value of the file input is null before making the ajax call (and cancel the ajax call and display the message if it is) to prevent an unnecessary call to the server.

how i could iterate over json array of array

i want to iterate over a json array of arrays from th controller i want to know how i could iterate over it
i need help: nay could help me i'm new to json and mvc
//server code
var jsonData = new
{
rows =
(from bathymetrie in bathymetries
select new
{
count = bathymetries.Count,
Id = bathymetrie.Id,
date = (bathymetrie.displayedDate != null) ?
bathymetrie.displayedDate.ToString() : ""
}).ToArray()
};
//client code
success: function (data) {
bathyms = "{";
for (var i = 0; i < data[1].count; i++) {
bathyms += el[i].Id + " : " + el[i].date;
alert(el[i].Id);
alert(el[i].date);
console.log(el[i].date);
if (i != data[0].count) {
bathyms += ",";
}
}
bathyms += "}";
}
Your data is an object with single field row, which contains an array of objects. Thus, iteration should look like this:
for (var i = 0; i < data.rows.length; i++) {
var element = data.rows[i];
// use element.Id, element.count and element.date
Say if you have your model like this -
public class Data
{
public int Id { get; set; }
public int Count { get; set; }
public string Date { get; set; }
}
And you are returning JsonResult of Array object like this -
public ActionResult GetJson()
{
Data[] a = new Data[2];
a[0] = new Data() { Count = 10, Id = 1, Date = "2/19/2014" };
a[1] = new Data() { Count = 20, Id = 2, Date = "3/19/2014" };
return new JsonResult() { Data = a };
}
Then you can invoke this action in JQuery in the following way -
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script>
function submitForm() {
jQuery.ajax({
type: "POST",
url: "#Url.Action("GetJson")",
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function (data) {
$.each(data, function (key, value) {
alert(value.Id + ' ' + value.Count + ' ' + value.Date);
});
},
failure: function (errMsg) {
alert(errMsg);
}
});
}
</script>
<input type="button" value="Click" onclick="submitForm()" />
Please observe following code which will iterate array -
success: function (data) {
$.each(data, function (key, value) {
alert(value.Id + ' ' + value.Count + ' ' + value.Date);
});
Output would be N number of alerts based on N elements in array like below -

Image Upload using MVC API POST Multipart form

My View :-
<form class="commentform commentright" enctype="multipart/form-data" >
<textarea class="simpleta required" name="Body" placeholder="Discuss this vehicle with other members of Row52."></textarea>
<input type="file" id="fileuploadfield" name="fileuploadfield"/>
<input type="submit" value="Submit" class="btn btn-inverse btn-small"/>
<input type="hidden" name="ParentId" value="0"/>
<input type="hidden" name="VehicleId" value="#Model.VehicleId"/>
<span class="commentcount"><span class="numberofcomments">#Model.Count</span>#count</span>
<div class="feedback"></div>
<img id="img" src="" />
</form>
JQuery :-
submitComment: function (form, type) {
var self = this;
var $this = $(form);
var formData = $this.serialize();
var $message = $this.find('.feedback');
$message.hide();
$message.html('');
var val = validation({ $form: $this });
if (val.checkRequired()) {
$this.indicator({ autoStart: false, minDuration: 100 });
$this.indicator('start');
var files = $("#fileuploadfield").get(0).files;
if (files.length > 0) {
if (window.FormData !== undefined) {
var data = new FormData();
for (var i = 0; i < files.length; i++) {
data.append("file" + i, files[i]);
}
}
else {
alert("This browser doesn't support HTML5 multiple file uploads!");
}
}
else {
alert("This");
}
$.ajax('/api/v2/comment/Post/', {
type: 'POST',
contentType: 'multipart/form-data',
// I have also use contentType: false,
processData: false,
data: formData
}).done(function (d) {
Controller :-
public Task<HttpResponseMessage> Post()
{
var provider = new MultipartMemoryStreamProvider();
var task1 = Request.Content.ReadAsMultipartAsync(provider);
var userId = User.Id;
return task1.Then(providerResult =>
{
var file = GetFileContent(providerResult);
var task2 = file.ReadAsStreamAsync();
string originalFileName = file.Headers.ContentDisposition.FileName.Replace("\"", "");
string extension = Path.GetExtension(originalFileName);
string fileName = Guid.NewGuid().ToString() + extension;
return task2.Then(stream =>
{
if (stream.Length > 0)
{
var kernel = WindsorContainerFactory.Create(KernelState.Thread, ConfigurationState.Web);
CloudBlobContainer container = GetContainer();
var userRepo = kernel.Resolve<IUserRepository>();
var logger = kernel.Resolve<ILogger>();
logger.Fatal("Original File Name: " + originalFileName);
logger.Fatal("Extension: " + extension);
logger.Fatal("File Name: " + fileName);
var user = userRepo.FirstOrDefault(x => x.Id == userId);
var path = CreateImageSize(stream, 500, fileName, userId, container, logger);
if (user != null)
{
user.AvatarOriginalFileName = fileName;
user.AvatarOriginalAbsolutePath =
ConfigurationManager.AppSettings["CdnUserEndpointUrl"] + path;
user.AvatarOriginalRelativePath = path;
user.AvatarCroppedAbsolutePath = "";
user.AvatarCroppedFileName = "";
user.AvatarCroppedRelativePath = "";
userRepo.Update(user);
}
else
{
Logger.Error("User is null. Id: " + userId.ToString());
}
kernel.Release(userRepo);
kernel.Release(logger);
kernel.Dispose();
}
var response = Request.CreateResponse(HttpStatusCode.Moved);
response.Headers.Location = new Uri("/Account/Avatar", UriKind.Relative);
return response;
});
});
}
Following Error get :-
"Invalid 'HttpContent' instance provided. It does not have a content type header starting with 'multipart/'. Parameter name: content"
There's no standard Task.Then method in .NET framework, and I couldn't find any implementation details in the code you posted. I'd assume it was taken from here:
Processing Sequences of Asynchronous Operations with Tasks
If that's the case, you may be loosing AspNetSynchronizationContext context inside your Task.Then lambdas, because the continuation may happen on a different ASP.NET pool thread without proper synchronization context. That would explain why HttpContent becomes invalid.
Try changing the implementation of Task.Then like this:
public static Task<T2> Then<T1, T2>(this Task<T1> first, Func<T1, Task<T2>> next)
{
if (first == null) throw new ArgumentNullException("first");
if (next == null) throw new ArgumentNullException("next");
var tcs = new TaskCompletionSource<T2>();
first.ContinueWith(delegate
{
if (first.IsFaulted) tcs.TrySetException(first.Exception.InnerExceptions);
else if (first.IsCanceled) tcs.TrySetCanceled();
else
{
try
{
var t = next(first.Result);
if (t == null) tcs.TrySetCanceled();
else t.ContinueWith(delegate
{
if (t.IsFaulted) tcs.TrySetException(t.Exception.InnerExceptions);
else if (t.IsCanceled) tcs.TrySetCanceled();
else tcs.TrySetResult(t.Result);
}, TaskScheduler.FromCurrentSynchronizationContext());
}
catch (Exception exc) { tcs.TrySetException(exc); }
}
}, TaskScheduler.FromCurrentSynchronizationContext());
return tcs.Task;
}
Note how the continuations are now scheduled using TaskScheduler.FromCurrentSynchronizationContext().
Ideally, if you can use .NET 4.5, you should be using async/await: Using Asynchronous Methods in ASP.NET 4.5. This way, the thread's synchronization context is automatically captured for await continuations.
The following may facility porting:
Implementing Then with Await.

Resources