Here is the final, which works! Thanks to bob-the-destroyer.
I just had to change this :
<script>
$(function () {
$('#formSearch').ajaxForm(function (result) {
$('#divArefresh').fadeOut('slow').fadeIn("slow").load('/Feed/Search');
});
});
</script>
Into this :
<script>
$(function () {
$('#formSearch').ajaxForm(function (result) {
$('#divArefresh').fadeOut('slow').fadeIn("slow").load(result);
});
});
</script>
EDIT: I thought that this :
// In /Feed/Index.cshtml
#using (Html.BeginForm("Search", "Feed", FormMethod.Post, new { id = "formSearch" }))
{
<input type="text" id="search" name="search" />
<input type="submit" value="save"/>
}
<script>
$(function () {
$('#formSearch').ajaxForm(function (result) {
$('#divArefresh').fadeOut('slow').fadeIn("slow").load('/Feed/Search');
});
});
</script>
With this :
// In Controller/Feed/
public ActionResult Search(string search)
{
if (!String.IsNullOrEmpty(search))
return Content("Variable : " + search);
else
return Content(":(");
return Content("Pas ajax :(");
}
Would do the trick, but it isn't. It always shows ":(". I'm really lost.
//////////////////////
I just wonder how to make this. I've tried many ways but I really don't manage to do it.
It's pretty easy to understand what I want to do :
I have a div which contains all the informations I want to show :
<div id="divArefresh">
<div id="accordion-container">
#foreach (var i in #ViewBag.feedsItem)
{
... Things ....
}
</div>
</div>
And I want to make a « search function », like this :
<!-- le champs de recherche -->
#{var options2 = new AjaxOptions()
{
Url = Url.Action("Search", "Feed"),
LoadingElementDuration = 200000,
OnComplete = "divSearch",
HttpMethod = "GET"
};
using (Ajax.BeginForm(options2))
{
<input type="text" name="search" id="search" placeholder="Votre recherche"/>
<input type="submit" id="submit" value="chercher"/>
}
}
Here is my « divSearch »
function divSearch() {
$('#divArefresh').fadeOut('slow').fadeIn("slow").load('/Feed/Search');
}
When I submit something, it goes fine in my action :
public ActionResult Search(string search)
{
Users user = db.UserProfiles.Find(Session["id"]);
if (Request.IsAjaxRequest())
{
var wordSearched = Request["search"].ToString(); --> NULL
var feeds = (from u in db.Feed
where u.UsersId == user.UsersId
select u).ToList(); --> FINE
var feedsItems = from u in db.FeedsItem
where u.Title.Equals(wordSearched)
select u;
ViewBag.feedsItem = feedsItems.ToList();
return PartialView("Search", feeds);
}
return PartialView("Search");
}
BUT this :
Request["search"].ToString();
is always null when I try with the debugger. Even the « search » variable is null
If someone have an idea on how to make this work...
Kai23
Related
Need to upload files using MVC model biding by AJAX jQuery/JSON.
I was uploading with a normal submit form, but now I need to change to AJAX.
How can I do this? I mean, biding using MVC and AJAX, serializing my form or something like that.
Now, my imagemPro and imagemPre, on Controller, are always 'null'.
At my View:
#model Ri.Models.Produto
<form class="settings-form" id="frmAdd" enctype="multipart/form-data">
<label for="setting-input-1" class="form-label">Título</label>
<input asp-for="#Model.TituloProduto" type="text" class="form-control" required>
<input asp-for="#Model.ImagemProduto" type="file" class="form-control" required>
<label for="setting-input-1" class="form-label">Premio</label>
<input asp-for="#Model.TituloPremio" type="text" class="form-control" required>
<input asp-for="#Model.ImagemPremio" type="file" class="form-control" required>
<input type="button" value="Criar" class="btn app-btn-primary" id="btnAdd">
</form>
#section scripts{
<script src="~/admin/js/produtoAdd.js"></script>
}
At my Controller:
[HttpPost("/api/ProdutoAdd")]
public async Task<IActionResult> ProdutoAdd([Bind("TituloProduto,ImagemProduto,TituloPremio,ImagemPremio")] Produto produto, IFormFile imagemPro, IFormFile imagemPre)
{
if (!ModelState.IsValid)
{
return Json(new { success = false, msg = "1" });
}
if (imagemPro != null)
{
var name = Path.Combine(_enviroment.WebRootPath + "/imgs", System.IO.Path.GetFileName(imagemPro.FileName));
await imagemPro.CopyToAsync(new FileStream(name, FileMode.Create));
produto.ImagemProduto = imagemPro.FileName;
}
if (imagemPro != null)
{
var name = Path.Combine(_enviroment.WebRootPath + "/imgs", System.IO.Path.GetFileName(imagemPre.FileName));
await imagemPro.CopyToAsync(new FileStream(name, FileMode.Create));
produto.ImagemPremio = imagemPre.FileName;
}
_context.Add(produto);
await _context.SaveChangesAsync();
return Json(new { success = true });
}
My script:
$(function () {
$("#btnAdd").click(function (e) {
e.preventDefault();
var _this = $(this);
var _form = _this.closest("form");
var isvalid = _form.valid();
if (isvalid) {
Create();
}
else {
//alert('false');
}
});
Create = function () {
var options = {};
options.type = "POST";
options.url = "/api/ProdutoAdd";
options.dataType = "JSON";
options.cache = true;
options.async = true;
contentType = "application/json; charset=utf-8",
options.data = $('#frmAdd').serialize();
options.beforeSend = function (xhr) {
xhr.setRequestHeader("XSRF-TOKEN", $('input:hidden[name="__RequestVerificationToken"]').val());
};
options.success = function (data) {
};
options.error = function (res) {
};
$.ajax(options);
};
});
I recommend you to create a viewModel
public class ProdutoViewModel
{
public Produto Produto {get; set;}
public IFormFile ImagemPro {get; set;}
public IFormFile ImagemPre {get; set;}
}
action ( remove Bind attribute too)
[HttpPost("/api/ProdutoAdd")]
public async Task<IActionResult> ProdutoAdd(ProdutoViewModel model )
I recommend you to use a submit button instead of ajax, it would be much easier for you
#model ProdutoViewModel
<form class="settings-form" id="frmAdd" enctype="multipart/form-data">
....
<input type="submit" value="Criar" class="btn app-btn-primary">
</form>
1.Model Binding binds the property by name attribute, your parameter name here is imagemPro/imagemPre.
2.The jQuery serialize() method will not include input file elements. So file user selected is not going to be included in the serialized value.
You need create a FormData object, append the files to that then append the form field values as well to this same FormData object. You may simply loop through all the input field and add it.
Here is a whole working demo you could follow:
#model Produto
//add `method="post"` in form tag
//otherwise it will not generate token input
<form class="settings-form" id="frmAdd" enctype="multipart/form-data" method="post">
<label for="setting-input-1" class="form-label">Título</label>
<input asp-for="#Model.TituloProduto" type="text" class="form-control" required>
<input asp-for="#Model.ImagemProduto" type="file" class="form-control" required>
<label for="setting-input-1" class="form-label">Premio</label>
<input asp-for="#Model.TituloPremio" type="text" class="form-control" required>
<input asp-for="#Model.ImagemPremio" type="file" class="form-control" required>
<input type="button" value="Criar" class="btn app-btn-primary" id="btnAdd">
</form>
#section scripts{
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script src="~/admin/js/produtoAdd.js"></script>
}
JS:
<script>
$(function () {
$("#btnAdd").click(function (e) {
e.preventDefault();
var _this = $(this);
var _form = _this.closest("form");
var isvalid = _form.valid();
if (isvalid) {
Create();
}
else {
}
});
Create = function () {
var fdata = new FormData();
var fileInput1 = $('#ImagemProduto')[0];
var file1 = fileInput1.files[0];
fdata.append("imagemPro", file1);
var fileInput2 = $('#ImagemPremio')[0];
var file2 = fileInput2.files[0];
fdata.append("imagemPre", file2);
$("form input[type='text']").each(function (x, y) {
fdata.append($(y).attr("name"), $(y).val());
});
var options = {};
options.type = "POST";
options.url = "/api/ProdutoAdd";
options.dataType = "JSON";
options.contentType = false; //change here...
options.processData= false; //add this...
options.data = fdata;
options.beforeSend = function (xhr) {
xhr.setRequestHeader("XSRF-TOKEN", $('input:hidden[name="__RequestVerificationToken"]').val());
};
options.success = function (data) {
};
options.error = function (res) {
};
$.ajax(options);
};
});
</script>
Your payload and your content type don't match. jQuery.serialize encodes your form data as application/x-www-form-urlencoded, but you're telling the server to expect a content type of application/json. Easiest solution is to change the content type.
Also, you might want to use the form submit event of the form, rather than a 'click' event on a button. The reason for this is that browsers will submit forms in other ways besides clicking the button, (e.g. pressing "enter" key while in a text input). Submit will handle all of these methods.
Another side note: your method of creating your options object will work, but that syntax is a little awkward. The most common "best practice" is to declare the properties inline with the object:
var options = {
type: 'POST',
url: '/api/ProdutoAdd',
// etc ...
success: function (data) {
},
// etc ...
};
That keeps you from having to type options. before every property.
I'm trying to persist a TempData value on a return PartialView(). It works correctly in one Post but not another and I'm stymied.
In the following action it works correctly and the value gets passed, via javascript redirect to the action that is using the value:
[ValidateAntiForgeryToken]
[HttpPost]
public ActionResult DeletetheFile(int attachmentid, string issueId)
{
string response = _adoSqlService.DeleteAttachment(attachmentid);
TempData["ID"]= issueId;
TempData.Keep();
return PartialView("_DeleteFile");
}
In the following action it is getting set properly (see the first image), but by the time it gets to the same action as the first one I showed it has changed (see second image).
[ValidateAntiForgeryToken]
[HttpPost]
public ActionResult EditFile(IFormCollection collection)
{
AttachmentModel model = new AttachmentModel();
model.attachmentId = Convert.ToInt32(collection["attachmentId"]);
model.aIssueAttachmentDescription = collection["aIssueAttachmentDescription"];
string response = _adoSqlService.EditFileDescription(model);
TempData.Remove("ID");
TempData["ID"] = collection["issueId"];
TempData.Keep();
return PartialView("_EditFile");
}
When it gets to the where I need it is now returning [string1] instead of the 20-003.
Both of the above actions run against a partial view in a modal pop-up. The following javascript captures the modal action and redirects the results to the Issue/Edit controller/action.
$('body').on('click', '.relative', function (e) {
e.preventDefault();
var form = $(this).parents('.modal').find('form');
var actionUrl = form.attr('action');
var dataToSend = form.serialize();
$.post(actionUrl, dataToSend).done(function (data) {
$('body').find('.modal-content').html(data);
var isValid = $('body').find('[name="IsValid"]').val() == 'True';
if (isValid) {
$('body').find('#modal-container').modal('hide');
window.location.href = "/Issue/Edit";
}
});
})
The redirection seems to happen since the edit action is being called in both cases. It's just that in the second case it is not getting the correct value of the TempData value. Here is the start of the Edit action which resides in a different controller than the 2 actions above:
public ActionResult Edit(string id)
{
if (id == null)
{
id = TempData["ID"].ToString();
}
----------More Comments
So after working on this for the past 4 hrs I've come up with a work-around. I'm not sure this is the correct technique or not. What I ended up doing was adding a hidden input field on the partial views and then parsing the ajax response for that value.
var issueid = $(response).find('[name="issueidSaved"]').val();
window.location.href = "/Issue/Edit/?id=" + issueid
My concern now is that the issueid is now included in the query string and is visible in the URL.
Is this the correct method or should I go back to using TempData and trying to get that to work?
UPDATE
Hopefully I can explain this better. The goal is to get a TempData value into the following action in my Issues Controller which is tied to my Edit Page (Issues/Edit):
public ActionResult Edit(string id)
{
if (id == null)
{
id = TempData["ID"].ToString();
}
I have a modal on my Edit page that is populated with different partial views depending upon what is populating it. The partial views use the Attachment controller, while the Edit view uses the Issues Controller. I use Javascript/ajax to capture the submit from the partial views to close the modal and redirect to the Edit view so a refresh of the data affected by the partial views is reflected on the Edit view. The TempData value is working correctly when I submit the _DeleteFile, but not when I submit the _EditFile partial views in the modal.
Here is the common Javascript/ajax on the Edit view:
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script>
//Using this to scroll the page on the close of the modal/page refresh
$(document).ready(function () {
var JumpTo = '#ViewBag.JumpToDivId';
if (JumpTo != "") {
$(this).scrollTop($('#' + JumpTo).position().top);
}
});
//Using this to Capture the click that opens the modals
$('body').on('click', '.modal-link', function () {
var actionUrl = $(this).attr('href');
$.get(actionUrl).done(function (data) {
$('body').find('.modal-content').html(data);
});
$(this).attr('data-target', '#modal-container');
$(this).attr('data-toggle', 'modal');
});
//Using this to Capture the click that Submits the _EditFile,_DeleteFile,_CreateEdit forms on the modal
$('body').on('click', '.relative', function (e) {
e.preventDefault();
var form = $(this).parents('.modal').find('form');
var actionUrl = form.attr('action');
var dataToSend = form.serialize();
$.post(actionUrl, dataToSend).done(function (data) {
$('body').find('.modal-content').html(data);
var isValid = $('body').find('[name="IsValid"]').val() == 'True';
var issueid = "";
issueid = $('body').find('[name="issueidSaved"]').val();
var jumpto = $('body').find('[name="jumpto"]').val();
if (isValid) {
$('body').find('#modal-container').modal('hide');
if (issueid == "")
{
window.location.href = "/Issue/Edit/?id=" + issueid + "&jumpto=" + jumpto;
}
}
});
})
//Using this to Capture the click that Submits the _UploadFile form on the modal
$(function () {
$('body').on('click', '.fileupload', function (e) {
e.preventDefault();
var form = $(this).parents('.modal').find('form');
var actionUrl = form.attr('action');
var fdata = new FormData();
$('input[name="file"]').each(function (a, b) {
var fileInput = $('input[name="file"]')[a];
if (fileInput.files.length > 0) {
var file = fileInput.files[0];
fdata.append("file", file);
}
});
$("form input[type='text']").each(function (x, y) {
fdata.append($(y).attr("name"), $(y).val());
});
$("form input[type='hidden']").each(function (x, y) {
fdata.append($(y).attr("name"), $(y).val());
});
$.ajax({
url: actionUrl,
method: "POST",
contentType: false,
processData: false,
data: fdata
}).done((response, textStatus, xhr) => {
var isValid = $(response).find('[name="IsValid"]').val() == 'True';
var issueid = $(response).find('[name="issueidSaved"]').val();
var jumpto = $(response).find('[name="jumpto"]').val();
if (isValid) {
$('body').find('#modal-container').modal('hide');
window.location.href = "/Issue/Edit/?id=" + issueid + "&jumpto="+jumpto;
}
});
})
});
$('body').on('click', '.close', function () {
$('body').find('#modal-container').modal('hide');
});
$('#CancelModal').on('click', function () {
return false;
});
$("form").submit(function () {
if ($('form').valid()) {
$("input").removeAttr("disabled");
}
});
</script>
Here is the _DeleteFile Partial view and the AttachmentController code that handles the submit:
<!--Modal Body Start-->
<div class="modal-content">
<input name="IsValid" type="hidden" value="#ViewData.ModelState.IsValid.ToString()" />
<input name="issueidSaved" type="hidden" value="#ViewBag.ID" />
<input name="jumpto" type="hidden" value="#ViewBag.JumpToDivId" />
<!--Modal Header Start-->
<div class="modal-header">
<h4 class="modal-title">Delete File</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
</div>
<!--Modal Header End-->
<form asp-action="DeletetheFile" asp-route-attachmentid="#ViewBag.id" asp-route-issueId="#ViewBag.issueId" asp-controller="Attachment" method="post" enctype="multipart/form-data">
#Html.AntiForgeryToken()
<div class="modal-body form-horizontal">
Are you sure you want to delete the #ViewBag.title File?
<!--Modal Footer Start-->
<div class="modal-footer">
<button data-dismiss="modal" id="cancel" class="btn btn-default" type="button">No</button>
<input type="submit" class="btn btn-success relative" id="btnSubmit" data-save="modal" value="Yes">
</div>
<div class="row">
</div>
</div> <!--Modal Footer End-->
</form>
</div>
<script type="text/javascript">
$(function () {
});
</script>
<!--Modal Body End-->
[ValidateAntiForgeryToken]
[HttpPost]
public ActionResult DeletetheFile(int attachmentid, string issueId)
{
string response = _adoSqlService.DeleteAttachment(attachmentid);
ViewBag.ID = issueId;
ViewBag.JumpToDivId = "upload";
TempData["ID"]= issueId;
TempData.Keep();
return PartialView("_DeleteFile");
}
Here is the _EditFile partial view and the AttachmentController code:
Edit File
×
<form asp-action="EditFile" asp-controller="Attachment" method="post" enctype="multipart/form-data">
#Html.AntiForgeryToken()
<div class="modal-body form-horizontal">
<input name="issueId" type="hidden" value="#ViewBag.issueId" />
<input name="attachmentId" type="hidden" value="#ViewBag.attachmentId" />
<label class="control-label">#ViewBag.aFileName</label><br />
Make changes to description then select "Save Changes".<br />
<input name="aIssueAttachmentDescription" class="form-control formtableborders" id="titletext" value="#ViewBag.aIssueAttachmentDescription" />
<!--Modal Footer Start-->
<div class="modal-footer">
<button data-dismiss="modal" id="cancel" class="btn btn-default" type="button">No</button>
<input type="submit" class="btn btn-success relative" id="btnSubmit" data-save="modal" value="Save Changes">
</div>
<div class="row">
</div>
</div> <!--Modal Footer End-->
</form>
</div>
<script type="text/javascript">
$(function () {
});
</script>
<!--Modal Body End-->
[ValidateAntiForgeryToken]
[HttpPost]
public ActionResult EditFile(IFormCollection collection)
{
AttachmentModel model = new AttachmentModel();
model.attachmentId = Convert.ToInt32(collection["attachmentId"]);
model.aIssueAttachmentDescription = collection["aIssueAttachmentDescription"];
string response = _adoSqlService.EditFileDescription(model);
ViewBag.ID = collection["issueId"];
ViewBag.JumpToDivId = "upload";
TempData.Remove("ID");
TempData["ID"] = collection["issueId"];
TempData.Keep();
return PartialView("_EditFile");
}
When I inspect the TempData at the Issue/Edit action after the _DeleteFile submit it displays the following (which is correct):
When I inspect the TempData at the Issue/Edit action after the _EditFile submit it displays the following (which is incorrect):
You need to change
TempData["ID"] = collection["issueId"];
to
TempData["ID"] = collection["issueId"].ToString();
Because collection["issueId"] is Type Microsoft.Extensions.Primitives.StringValues rathar than Type String.
Here is a picture to show the Type:
I have some issues with rendering a partial view using jquery. Even if it was asked more than once (here), I didn't found a solution that worked for me:
My view:
#model IEnumerable<OdeToFood.Models.RestaurantListViewModel>
#{
ViewBag.Title = "Home Page";
}
<form method="get" action="#Url.Action("Index")" data-otf-ajax="true" data-otf-target="#restaurantlist">
<input type="search" name="searchTerm" />
<input type="submit" value="Search By Name" />
</form>
#Html.Partial("_Restaurants", Model)
My partial view:
#model IEnumerable<OdeToFood.Models.RestaurantListViewModel>
<div id="restaurantList">
#foreach (var item in Model)
{
<div>
<h4>#item.Name</h4>
<div>#item.City, #item.Country</div>
<div>REviews: #item.CountOfReviews</div>
<hr />
</div>
}
</div>
otf.js:
$(function () {
var ajaxFormSubmit = function () {
var $form = $(this);
var options = {
url: $form.attr("action"),
type: $form.attr("method"),
data: $form.serialize()
};
$.ajax(options).done(function (data) {
var $target = $($form.attr("data-otf-target"));
$target.replaceWith(data);
});
return false;
};
$("form[data-otf-ajax='true']").submit(ajaxFormSubmit);
});
index action of my controller:
public ActionResult Index(string searchTerm = null)
{
var model =
_db.Restaurants
.OrderByDescending(r => r.RestaurantReviews.Average(a => a.Rating))
.Where(r => searchTerm == null || r.Name.StartsWith(searchTerm))
.Select(r => new RestaurantListViewModel
{
Id = r.Id,
Name = r.Name,
City = r.City,
Country = r.Country,
CountOfReviews = r.RestaurantReviews.Count()
}
)
.Take(10);
if (Request.IsAjaxRequest())
{
return PartialView("_Restaurants", model);
}
return View(model);
}
My issue is that nothing happens when I press search button. I've added an test alert like:
$.ajax(options).done(function (data) {
var $target = $($form.attr("data-otf-target"));
alert('test');
$target.replaceWith(data);
});
and the alert showed when I've clicked the search button, but in site nothing happens. Anyone knows what is my issue?
You have #restaurantlist as the target, whereas div id is actually restaurantList. Capital "L".
I am very new to both JQuery and Asp.net MVC 3 (C#), so I apologize if this is trivial. I have an MVC partial view (Index.cshtml) that has a list of tasks. These tasks are contained within indivudal divs that I have in a list style layout. I have a button called "add task" that opens a dialog. This dialog will save the added task to the database via an AJAX Json call to the controller.
This is where I am having trouble - after the dialog closes I would like the list of tasks to reload with the task i just added. I have found examples where the entire page is reloaded, and I found examples where the controller is supposed to return a rendered view. My problem is that the dialog is being opened from the partial I want to reload. Is there a way to accomplish what I am trying to do.
Index.cshtml
#model IEnumerable<TaskManagementApplication.Models.Project>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<div id="ProjectAccordionWrapper">
#foreach (var item in Model)
{
<div class="ProjectWrapper">
<h3>#item.Name</h3>
<div class="column">
<button class="createTaskButton" id="#item.ProjectID">Create New Task</button>
#foreach(var task in item.Tasks) {
var buttonClass = "taskID" + task.TaskID;
<div class="portlet">
<div class="portlet-header">#task.TaskName</div>
<div class="portlet-content">#task.TaskDescription</div>
<button class="editTaskButton" id="#task.TaskID">Edit Task</button>
</div>
}
</div>
</div>
}
</div>
<div id="dialog-form" title="Create new user">
<p class="validateTips">All form fields are required.</p>
<form>
<fieldset>
<label for="TaskName">Task Name</label>
<input type="text" name="TaskName" id="name" class="text ui-widget-content ui-corner-all" />
<label for="TaskDescription">Task Description</label>
<input type="text" name="TaskDescription" id="description" value="" class="text ui-widget-content ui-corner-all" />
<input type="hidden" name="TaskID" id="ID" />
<input type="hidden" name="ProjectID" id="ProjectID" />
</fieldset>
</form>
</div>
Partial Javascript
function GetTask(id) {
if (id.length > 0) {
$.ajax({
url: '/Project/GetTaskFromID',
type: "POST",
data: { "id": id },
success: PopulateDialogFields,
error: HandleError
});
}
}
function PopulateDialogFields(data) {
$("#name").val(data.TaskName);
$("#description").val(data.TaskDescription);
$("#ID").val(data.TaskID);
}
function HandleError(data) {
alert(data.error);
var foo = data;
}
function SaveTask() {
var taskName = $("#name").val();
var taskDescription = $("#description").val();
var id = $("#ID").val();
var projectID = $("#ProjectID").val();
if (id.length > 0) {
$.ajax({
url: '/Project/SaveTask',
type: "POST",
data: { "taskName": taskName, "taskDescription": taskDescription, "taskID": id }
});
}
else {
$.ajax({
url: '/Project/SaveTask',
type: "POST",
data: { "taskName": taskName, "taskDescription": taskDescription, "projectID": projectID }
});
}
}
$("#dialog-form").dialog({
autoOpen: false,
height: 300,
width: 350,
modal: true,
buttons: {
"OK": function () {
SaveTask();
$(this).dialog("close");
},
Cancel: function () {
$(this).dialog("close");
}
},
close: function () {
allFields.val("").removeClass("ui-state-error");
window.location.reload(true);
},
open: function () {
var id = $(this).data("id");
var projectID = $(this).data("projectID");
$("#ProjectID").val(projectID);
var button = $("#" + id);
GetTask(id);
}
});
$(".editTaskButton")
.button()
.click(function () {
$("#dialog-form").data('id', this.id).dialog("open");
});
$(".createTaskButton")
.button()
.click(function () {
$("#dialog-form").data('projectID', this.id).dialog("open");
});
I am relatively new to jQuery and ASP.NET MVC as well, however, here's what first comes to mind.
In order to maintain the AJAX-y aspect of the page, I suggest that you create a method that handles a POST which returns a JSON formatted set of TaskManagementApplication.Models.Project. This method can optionally return filtered results.
The markup would look like this,
<div id="ProjectAccordionWrapper">
<div id="ProjectWrapperTemplate" class="ProjectWrapper" style="display: none;">
<h3 id="itemName"></h3>
<div class="column">
<button class="createTaskButton" id="itemProjectID">Create New Task</button>
<div id="portletTemplate" class="portlet">
<div class="portlet-header" id="taskName"></div>
<div class="portlet-content" id="taskDescription"></div>
<button class="editTaskButton" id="taskID">Edit Task</button>
</div>
</div>
</div>
</div>
Next, you would have jQuery clone the ProjectWrapperTemplate element, and set all of the corresponding fields.
$(function () {
$.ajax({
url: '/Project/GetTasks',
type: "POST",
data: { }
}).done(function (data) {
data.forEach(function (element) {
AppendProjectWrapper(element);
});
});
function AppendProjectWrapper(data) {
var projectAccordionWrapper = $('#ProjectAccordionWrapper');
var projectWrapper = $('#ProjectWrapperTemplate').clone(true, true);
projectWrapper.id = nothing; // remove the id, so as to not have duplicates
projectWrapper.style.display = nothing; // remove the style "display: none"
var itemName = projectWrapper.children('#itemName'); // h3
itemName.id = nothing;
itemName.text(data.ItemName);
var itemProjectID = projectWrapper.children('#itemProjectID'); // button Create New Task
itemProjectID.id = data.ItemProjectID;
var portletTemplate = projectWrapper.children('#portletTemplate'); // div
data.Tasks.forEach(function (element) {
var portlet = portletTemplate.clone();
portlet.id = nothing;
var taskName = portlet.children('#taskName');
taskName.id = nothing;
taskName.text(element.TaskName);
var taskDescription = portlet.children('#taskDescription');
taskDescription.id = nothing;
taskDescription.text(element.TaskDescription);
var editTaskButton = portlet.children('#taskID');
editTaskButton.id = element.TaskID;
portlet.appendTo(projectWrapper);
});
portletTemplate.remove(); // remove the portlet template element
projectWrapper.appendTo(projectAccordionWrapper);
}
}
Finally, have '/Project/SaveTask' return a JSON formatted TaskManagementApplication.Models.Project of the currently saved task.
$.ajax({
url: '/Project/SaveTask',
type: "POST",
data: { "taskName": taskName, "taskDescription": taskDescription, "taskID": id }
}).done(function (data) {
AppendProjectWrapper(data);
});
The return data for '/Project/GetTasks' should look as follows:
[
{
ItemName: '#item.Name',
ItemProjectID: '#item.ProjectID',
Tasks: [
TaskName: '#task.TaskName',
TaskDescription: '#task.TaskDescription',
TaskID: '#task.TaskID'
]
}
]
The return data from '/Project/SaveTask' should follow the same format, except or the outer-most array.
Please note that a lot of this code is untested.
It may be easiest to refactor the list into another action+view. Then, you can call this in both the original Index.cshtml view, and via the .load() method in jQuery. So, assuming this:
Projects controller
[HttpGet]
[ChildActionOnly]
public ActionResult Tasks(int id)
{
// create the appropriate model object as an IEnumerable of your Task type
return View(model);
}
Tasks.cshtml
#foreach(var task in Model) {
var buttonClass = "taskID" + task.TaskID;
<div class="portlet">
<div class="portlet-header">#task.TaskName</div>
<div class="portlet-content">#task.TaskDescription</div>
<button class="editTaskButton" id="#task.TaskID">Edit Task</button>
</div>
}
You would adjust Index.cshtml like so:
#model IEnumerable<TaskManagementApplication.Models.Project>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<div id="ProjectAccordionWrapper">
#foreach (var item in Model)
{
<div class="ProjectWrapper">
<h3>#item.Name</h3>
<div class="column">
<button class="createTaskButton" id="#item.ProjectID">Create New Task</button>
<div id="tasks-#item.ProjectID">
#Html.Action("Tasks", "Project", new { id = item.ProjectID })
</div>
</div>
</div>
}
</div>
//... the rest of the view
And finally,
// this should happen inside the callback of your .ajax() method
$('#tasks-'+projectID).load('/project/tasks/'+ projectID);
I am using CKeditor in MVC 3 app.
Right now i am in need of saving text with HTML tags to DB, problem is that Ckeditors GetData() method returns raw format of data (http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.editor.html#getData)
I need a way to make that raw format to normal string with Html tags
View :
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>TextEditionViewModel</legend>
#Html.DropDownListFor(model=>model.Id, Model.Texts)
<div class="editor-label">
#Html.LabelFor(model => model.Text)
</div>
<div class="editor-field">
#Html.TextAreaFor((model => model.Text), new { #Id = "editor1" })
#Html.ValidationMessageFor(model => model.Text)
</div>
<script type="text/javascript">
CKEDITOR.replace('editor1');
</script>
<script type="text/javascript">
var editor_data = CKEDITOR.instances['editor1'].getData();
var url = '#Url.Action("EditText1", "Admin")';
var data = { CommentText: editor_data };
function Save() {
alert(editor_data);
$.post(url, { CommentText: editor_data }, function (result) {
});
};
$('#Id').change(function () {
var selectedText = $(this).val();
if (selectedText != null && selectedText != '') {
$.getJSON('#Url.Action("Text","Admin")', { Id: selectedText }, function (text) {
CKEDITOR.instances['editor1'].setData(text);
});
}
});
</script>
</fieldset>
<p>
<input type="button" value="Save" onclick="Save()"/>
</p>
}
If editor_data is set to Normal "Text" string everything works!
Controller
public ActionResult EditText1(String CommentText)
{
return null;
}
You could decorate your controller action with the [ValidateInput] attribute to allow POSTing HTML tags which is not authorized by ASP.NET by default:
[HttpPost]
[ValidateInput(false)]
public ActionResult EditText1(string commentText)
{
return null;
}
UPDATE:
The problem is related to the fact that the editor_data variable is not accessible in the Save method.
You could do this:
<script type="text/javascript">
function Save() {
var editor_data = CKEDITOR.instances['editor1'].getData();
var url = '#Url.Action("EditText1", "Admin")';
var data = { CommentText: editor_data };
$.post(url, data, function (result) {
});
}
...
</script>