ASP.NET MVC 3 Treeview - asp.net-mvc

I need to display a Treeview in my MVC3 application. There will be a self referencing hierarchical table (Folders) and another table linked to it (Documents.) (So Folders can have N-subFolders and any folder/sub folder can have many documents.)
I have looked into using third party vendors such as Telerik, DJME and MVC Controls Toolkit. While all nice packages, I'm uneasy about the licences, and since i'm new to MVC (and programming in general,) I find their documentation lacking to get the right display working.
I've also looked at the heavily referenced blogs on TreeViews:
TreeViewHelper
and the Recursive Partial View
In addition to the other less referenced articles (The top 3 are also very informative):
http://tpeczek.com/2010/01/asynchronous-treeview-in-aspnet-mvc.html
http://mikehadlow.blogspot.com/2008/10/rendering-tree-view-using-mvc-framework.html
http://www.tek-tips.com/viewthread.cfm?qid=1637392&page=4
http://weblogs.asp.net/jigardesai/archive/2008/02/04/display-hierarchical-data-in-asp-net-mvc-framework.aspx
http://www.jigar.net/articles/viewhtmlcontent311.aspx
http://help.syncfusion.com/ug_82/ASP.NETMVCUI_Tools/CreatingATreeViewControl.html
I would like to use either the TreeViewHelper or the Recursive Partial View Method.
However, in the TreeViewHelper, I can't make it pull data from the second table (ie. I can only make it list the Files, but I'm not sure how to have it list the Documents for each File.)
For the Recursive Partial View, I'm still at a loss in how to convert this to MVC3 and also general implementation. I did find a post (forums.asp.net/t/1652809.aspx/1?treeview+with+mvc+3) that gives an explanation of how to convert a bit of it to MVC3, but i'm still unclear of what to do with it. I keep getting the error for the Partial view: Cannot implicitly Convert type 'void' to type 'object'
Like I said before I'm new to MVC3 and would like insight in which method would work best for my scenario and how to implement it.

In case anyone is wondering, the way I solved this problem was to use a recursive partial view. The problem I has having with it was that I didn't have the self referencing relationship set up in SQL/EF (I just had the ParentID field which wasn't linked to the Primary Key.) I also integrated jsTree as this has a lot of slick functionality such as search.
Like I said in the comment above, #Html.Action and #Html.Partial work instead of #Html.RenderAction and #Html.RenderPartial.

give a look to the edit/add/delete/node move templated TreeView of my Mvc Controls Toolkit here: http://mvccontrolstoolkit.codeplex.com/wikipage?title=TreeView

$(document).ready(function () {
BindChart();
});
function BindChart() {
$("#org").jOrgChart({
chartElement: '#chart',
dragAndDrop: true
});
}
$(".cardadd").live("click", function ()
{
var data = { id: 0 , ParentId:$(this).parent().data('cardid')};
OpenForminWindow('divfrmChartMember', 'divChartMember', 'frmChartMember', chart.ChartMember, data, '', 400, 1000);
});
$(".cardedit").live("click", function () {
var data = { id: $(this).parent().data('cardid')};
OpenForminWindow('divfrmChartMember', 'divChartMember', 'frmChartMember', chart.ChartMember, data, '', 400, 1000);
});
$(".cardremove").live("click", function () {
});
function OpenForminWindow(popupId, targetDivId, formid, url, data, callbackfunc, heigth, width) {
$.ajax({
type: "GET",
url: url,
data: data,
cache: false,
success: function (data) {
$('#' + targetDivId).html(data);
$('#' + formid).removeData('validator');
$('#' + formid).removeData('unobtrusiveValidation');
$('#' + formid).each(function () { $.data($(this)[0], 'validator', false); }); //enable to display the error messages
$.validator.unobtrusive.parse('#' + formid);
if (callbackfunc)
return callbackfunc();
}
});
$("#" + popupId).dialog({
modal: true,
height: heigth,
width: width,
beforeClose: function (event, ui) {
if (typeof refresh !== 'undefined' && refresh == true)
ReloadCurrentPage();
}
});
}
$('#frmChartMember').live('submit', function (e) {
SubmitAjaxForm($(this).attr('id'), chart.AddMember, ReloadChart);
e.preventDefault();
});
function SubmitAjaxForm(formId, url, callBack) {
$.ajax({
url: url,
type: 'post',
cache: false,
data: $('#' + formId).serialize(),
success: function (data) {
return callBack(data);
},
});
}
function ReloadChart(result) {
ClosePopup('divfrmChartMember');
$.ajax({
type: 'GET',
url: chart.ChartList,
cache: false,
success: function (result) {
$("#orgChart").html(result);
BindChart();
}
});
}
function ClosePopup(divid) {
$("#" + divid).dialog("close");
}
public class ChartController : Controller
{
//
// GET: /Chart/
ChartContext ctx = new ChartContext();
public ActionResult Index()
{
return View();
}
public ActionResult OrgChart()
{
return PartialView("_OrgChart", ctx.Cards.ToList());
}
public ActionResult ChartMember(int id, int? ParentId = null)
{
Card card = new Card();
if (id > 0)
card = ctx.Cards.Find(id);
else
card.ParentId = ParentId;
return PartialView("_ChartMember", card);
}
public ActionResult SaveMember(Card card)
{
if (card.id == 0)
ctx.Cards.Add(card);
else
ctx.Entry(card).State = System.Data.EntityState.Modified;
ctx.SaveChanges();
return Json(true, JsonRequestBehavior.AllowGet);
}
}

Related

Print out to a div in the view from the controller

I have a div block in my view like this:
<div id="divStatus" style="margin-top:10px;width: 200px; height: 100px; overflow-y: scroll;">
</div>
then from the view, user clicks on a button that calls the controller. In the controller some tasks are executed so from the controller I want to update a div block within the view. In this div I print out phrasses.
How to do this?
example:
public ActionResult()
{
// Do something
Update_DIV_in_View("some thing has been done"); <--- DIV in the view must be updated by appending this message
// Do another thing
Update_DIV_in_VIEW("another thing has been done");<--- DIV in the view must be updated by appending this message
.... and so on
// All things done
Update_DIV_in_VIEW("All things have been done");<--- DIV in the view must be updated by appending this message
return View();
}
Create a second action in your controller which only shows the updated content of the div and on your normal page when you press the button load the status with an AJAX call (for example the jQuery.load() method).
You can do it as follows:
In your view use Ajax Form as follows:
#using (Ajax.BeginForm("ActionName", "ControllerName", new AjaxOptions { OnBegin = "beforeSubmitFunction()", HttpMethod = "POST",UpdateTargetId = "divStatus", OnComplete = "InsertRow()" }))
{
.. //your Html form Controls
}
function beforeSubmitFunction()
{
//Your code for before submitting...
}
Then in your controller return your partial view as result which will get updated in your div with id divStatus
[HttpPost]
public ActionResult Index(TypeName model)
{
return PartialView("PartialViewName", model);
}
Here are 3 examples what I am using:
example 1:
button (here with telerik css styling):
<a class="t-button t-button-icontext" onclick="ajaxCreateEquipment()"><span
class="t-icon t-add"></span>Create</a>
javascript: #equipment-table-container is the id of the target div:
<script type="text/javascript">
function ajaxCreateEquipment() {
$.ajax({
type: 'GET',
url: '#Url.Action("ShowCreate", "Equipment")',
dataType: 'html',
success: function (data) {
$('#equipment-table-container').html(data);
}
});
}
</script>
EquipmentController.cs:
[HttpGet]
public ActionResult ShowCreate()
{
// some calculation code, fetch model from DB something else
ViewData.Add("FormAction", "Create"); // some ViewData
return PartialView("Create", model); // returns the View html file
}
example 2:
function call here with id argument and Json return:
#{
var supplierQuoteId = Model.ID.ToString();
<a id="#supplierQuoteId" onclick="updateDiv(this.id)"></a>
}
javascript:
function updateDiv(id) {
var strUrl = "/LicenseTerm/UpdateUsedQuantity/" + id;
$.ajax({
type: "GET",
url: strUrl,
cache: false,
dataType: "json",
success: function (data) {
$('#licenseterm-usedquantity').html(data.sum);
}
});
}
LicenseTermController.cs
[HttpGet]
public JsonResult UpdateUsedQuantity(Guid id)
{
var licenseTerm = _repository.GetAll<LicenseTerm>().Where(l => l.ID == id).First();
int sum = 0;
foreach (LicenseAllocation l in licenseTerm.LicenseAllocations.Where(o => o.Deleted == false))
sum = sum + l.LicenseQuantity;
return Json(new { sum = sum }, JsonRequestBehavior.AllowGet);
}
example 3: simple get
function ajaxFieldDefinitionCreate(id) {
var strUrl = '/FieldDefinition/Create' + '/' + id.toString() + '?isRefreshAction=true';
$.get(strUrl, function (data) {
$('#equipmenttype-fielddefinition-createeditarea').html(data);
});
}
[HttpGet]
public ActionResult Create(Guid id, [Optional, DefaultParameterValue(false)] bool isRefreshAction)
{
var equipmentType = _equipmentTypeRepository.GetById(id);
var fieldDefinitionDto = new FieldDefinitionDto
{
ID = Guid.NewGuid(),
ParentName = equipmentType.Name,
};
return PartialView("Create", fieldDefinitionDto);
}
In response to the changes of the question, especially that the questioner would like to have more returns in the same Action:
the concept of HTTP request is to transmit relatively small pieces of data from the server to the client, which invoked the e.g. HTTP GET request.
You can not keep open the HTTP GET request for more transmissions.
I searched the web and extracted that especially HTML5 will address this requirement with the HTTP stream, but this is another topic. e.g.: I got this url: http://socket.io/ .
Bypass:
But as an idea of mine,
I would make a first ajax call to determine the count of the next requests, addressed in the controller Action1.
And then invoke several new requests in the success part of the first ajax request, with the url of the Action2, e.g. Calculate etc., which appends then the several pieces of data to the div content.
here some quickly invented javascript code:
function updateDiv() {
var strUrl = "/Home/RequestCount/";
$.ajax({
type: "GET",
url: strUrl,
cache: false,
dataType: "json",
success: function (count) {
var strUrlCalc = "/Home/Calc/";
for (var i = 0; i < count; i++) {
$.ajax({
type: "GET",
url: strUrlCalc,
cache: false,
dataType: "json",
success: function (data) {
$('#test').append(data);
}
});
}
}
});
}

MVC - Load [HttpPost] view into Jquery/Ajax modal window

The way my code works currently is when a button is pressed variables are passed and Jquery unhides a modal window and makes an AJAX call to get the [HttpGet] portion of my Controller Action. This loads a form in the modal window. The modal has a save and cancel button, when the save button is pressed the form is submitted with ajaxSubmit. On successful save something is saved to the database, then a url is created and passed back to jquery and user is redirected with window.location.href.
What I'd like to have happen is when the save button is pressed the [HttpPost] part of controller action will make some logic choices, and if a certain condition is met within the action then return PartialView(model) is returned back to the modal window with an error message or pop up so that the user doesn't have to retype their information and understands why the save didn't go through and can edit quickly and try save again.
The Jquery:
var createNewClaimSetup = function () {
$(selectors.btnCreateNewClaim).live('click', function () {
var serialNumber = $(this).attr(attributes.serialNumber);
var spaRegistrationId = $(this).attr(attributes.spaRegistrationId);
var customerNumber = $(this).attr(attributes.customerNumber);
var customerStatementName = $(this).attr(attributes.customerStatementName);
$(selectors.serialNumberHidden).val(serialNumber);
$(selectors.spaRegistationIdHidden).val(spaRegistrationId);
$(selectors.customerNumberHidden).val(customerNumber);
$(selectors.customerStatementName).val(customerStatementName);
$(selectors.dialog).attr(attributes.title, 'Create New Claim');
$.ajax ({
url: inputs.warrantyClaimCreateUrl,
type: "GET",
data: {
serialNumber: serialNumber,
customerNumber: customerNumber
},
cache: false,
success: function (result) {
$(selectors.dialog).html(result.toString());
$(selectors.dialog).dialog ({
resizable: false,
height: 600,
width: 850,
modal: true,
buttons: {
'Save': function () {
// check to see if a value has been entered for problem description before submitting form
//
if(!$('#AddProblemModel_ProblemDesc').val().length==0 && (!$('#ClaimPrefix').val().length==0 || $('#chkManualClaimNum').is(':checked')))
$(selectors.createNewClaimForm).ajaxSubmit({
cache: false,
success: function (data) {
if (data.length == 0) {
location.reload();
$(selectors.dialog).dialog('close');
} else {
$(selectors.dialog).dialog('close');
window.location.href = data.redirectToUrl;
}
}
});
else{
return alert('You must select a prefix and enter a description for the problem before saving');
}
},
Cancel: function () {
$(this).dialog('close');
}
}
});
}
});
});
}
Controller Action GET (edited for brevity):
public virtual ActionResult WarrantyClaimCreateNew(string serialNumber, string customerNumber, string customerStatementName)
{
WarrantyClaimModel model = wcs.CreateNewClaimInfo(serialNumber, customerNumber, false);
ViewBag.InvalidPrefix = false;
ViewBag.DupeWaNum = false;
return View(model);
}
Controller Action POST (edited for brevity):
[HttpPost]
public virtual ActionResult WarrantyClaimCreateNew(WarrantyClaimCreateNewModel model)
{
if (ModelState.IsValid)
{
WaNumberDupeModel WAdupe = new WaNumberDupeModel(model.WarrantyClaimNumber);
// Rebuild WarrantyClaimModel object to pass back to view
WarrantyClaimModel model2 = wcs.CreateNewClaimInfo(model.SerialNumber, model.CustomerNumber, false);
// check if prefix is valid
if (WAdupe.DupeType == WaDupeType.InvalidPrefix)
{
ViewBag.InvalidPrefix = true;
return PartialView(model2);
//return Json(new { Url = Url.Action("WarrantyClaimCreateNew", model2) });
}
// check if wa number already exists
if (WAdupe.DupeExists)
{
ViewBag.DupeWaNum = true;
return PartialView(model2);
}
// create/save the new claim
if (wcs.CreateNewWarrantyClaim(model))
{
var redirectString = "WarrantyClaimDetail?warrantyClaimNumber=" + model.WarrantyClaimNumber;
if (Request.IsAjaxRequest())
{
return Json(new { redirectToUrl = redirectString }, JsonRequestBehavior.AllowGet);
}
return RedirectToAction(Actions.WarrantyClaimDetail(newClaimNumber));
}
return RedirectToAction(Actions.WarrantyClaimSaveFail());
}
else
{
return View(model);
}
}
Alerts In View If Conditions In Controller Action Met:
<%if (ViewBag.InvalidPrefix == true) { %>
alert("Please choose a valid prefix for your warranty claim number.");
<% } %>
<%if (ViewBag.DupeWaNum == true) { %>
alert("Warranty Claim Number already exists, please choose another manual WA number or try again.");
<% } %>
I haven't even been able to reload the view refreshed in the modal at all, let alone with changed to the bound model intact.
I know that the first part of the AJAX call (type: "GET") uses the URL to go into the controller action GET and gets back the return View(model) then converts it to HTML and sets the modal window HTML to it. I've been assuming that the ajaxSubmit goes back to the controller and gets the POST version of the action, and that the data part of success: function (data) should be the view return if condition is met and the built JSON URL to redirect to if it's not.
I've tried setting the modal html to data and making sure the controller invalid prefix condition is met, however the content in the modal window never seems to refresh. I'll also need to figure out how to logically tell in JQUERY if the controller returned a view or a URL through JSON but I don't think that will be hard to figure out.
I've read through a dozen or so mvc jquery ajax modal similar questions and they've helped but not
Thanks for any help shedding some light on how I might accomplish what a want here.
Sort of a partial answer to what I was looking for. I realize now my main stumbling block was to have my test alerts before building the modal window as "return alert(whatever)" which was obviously returning the alert and not running the rest of the code, dumb.
The solution I have working now is a pretty bad hack. I have it rebuild the modal and attach the POST view data to it. However this isn't looped so if you save and fail again I've set it to alert the user save failed again and to retry, then the modal closes and they can start again from scratch.
Tomorrow I'll see about turning this into a looping rather than nested structure.
New "working" JQuery code:
var createNewClaimSetup = function () {
$(selectors.btnCreateNewClaim).live('click', function () {
var serialNumber = $(this).attr(attributes.serialNumber);
var spaRegistrationId = $(this).attr(attributes.spaRegistrationId);
var customerNumber = $(this).attr(attributes.customerNumber);
var customerStatementName = $(this).attr(attributes.customerStatementName);
$(selectors.serialNumberHidden).val(serialNumber);
$(selectors.spaRegistationIdHidden).val(spaRegistrationId);
$(selectors.customerNumberHidden).val(customerNumber);
$(selectors.customerStatementName).val(customerStatementName);
$(selectors.dialog).attr(attributes.title, 'Create New Claim');
$.ajax ({
url: inputs.warrantyClaimCreateUrl,
type: "GET",
data: {
serialNumber: serialNumber,
customerNumber: customerNumber
},
cache: false,
success: function (result) {
$(selectors.dialog).html(result.toString());
$(selectors.dialog).dialog ({
resizable: false,
height: 600,
width: 850,
modal: true,
buttons: {
'Save': function () {
// check to see if a value has been entered for problem description before submitting form
//
if(!$('#AddProblemModel_ProblemDesc').val().length==0 && (!$('#ClaimPrefix').val().length==0 || $('#chkManualClaimNum').is(':checked')))
$(selectors.createNewClaimForm).ajaxSubmit({
cache: false,
success: function (data) {
if (data.length == 0) {
location.reload();
$(selectors.dialog).dialog('close');
} else if(data.redirectToUrl) {
$(selectors.dialog).dialog('close');
window.location.href = data.redirectToUrl;
} else {
$(selectors.dialog).html(data.toString());
$('#chkManualClaimNum').attr('checked','checked');
$('#manualClaimInfo').toggle();
$(selectors.dialog).dialog ({
resizable: false,
height: 600,
width: 850,
modal: true,
buttons: {
'Save': function () {
if(!$('#AddProblemModel_ProblemDesc').val().length==0 && (!$('#ClaimPrefix').val().length==0 || $('#chkManualClaimNum').is(':checked')))
$(selectors.createNewClaimForm).ajaxSubmit({
cache: false,
success: function (data) {
if (data.length == 0) {
location.reload();
$(selectors.dialog).dialog('close');
} else if(data.redirectToUrl) {
$(selectors.dialog).dialog('close');
window.location.href = data.redirectToUrl;
} else {
alert('Save failed again. Please start over.');
$(selectors.dialog).dialog('close');
}
}
});
},
Cancel: function () {
$(this).dialog('close');
}
}
});
}
}
});
else{
return alert('You must select a prefix and enter a description for the problem before saving');
}
},
Cancel: function () {
$(this).dialog('close');
}
}
});
}
});
});
}
This does at least show how to pass the POST view back to the modal window.

getting a return value upon ajax save of new form

I am using ajax to add form data to the database. My add code works great, but I need somehow to grab the newly created ID and use it in my jquery after the save has succeeded.
Here is my save jquery:
$(document).ready(function() {
$("#saveChanges").on("click", function(e) {
var formData = $("form").serialize();
e.preventDefault();
$.ajax({
url: "newListing/",
data: formData,
type: "POST",
success: function (resp) {
debugger;
alert($('#id').val());
$('#listChosen').html("");
$('#listChosen').load("/Listing/SearchListing/" + $('#id').val());
alert("listing added");
},
error: function(xhr, ajaxoptions, thrownError) {
alert(xhr.responseText);
}
});
});
What I am trying to do is expose the newly created ID from newListing and use it when calling the /SearchListing/.
The controller action I call with new listing is as follows:
[HttpPost]
public ActionResult newListing(Listing newList)
{
_listingservice.NewListing(newList);
return View();
}
This action filters through my service layer via interfaces to my repository and eventually makes this call:
try
{
_entities.Listings.Add(newList);
_entities.SaveChanges();
}
catch (Exception)
{
return false;
throw;
}
return true;
upon the savechange() the new ID is available but i dont know how to filter it back down to my success code in the jquery.
Can anyone offer any advice on how I might do this.
Many thanks
You could return the newly saved entity from NewListing. You don't seem to be using the View() that you're currently rendering, so you could just respond with the Id instead:
[HttpPost]
public ActionResult newListing(Listing newList)
{
var entity = _listingservice.NewListing(newList);
return Content(entity.Id.ToString());
}
Then your ajax would receive the id:
...
success: function (id) {
debugger;
alert(id);
$('#listChosen').html("");
$('#listChosen').load("/Listing/SearchListing/" + id);
alert("listing added");
},
...

Passing IEnumerable through Json

I'm making a "Like" button in a simple comment database MVC program.
I'm passins the ID of the comment through to a ActionResult in the HomeController when I hover over the "Like" button. The problem (I think) is that I don't know how to pass the IEnumerable list of Likes to the ajax.
The script and HTML part:
HTML:
Like this
Script:
$(".likes").hover(function (event) {
var Liker = { "CID": event.target.id };
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "/Home/ShowLike/",
data: JSON.stringify(Liker),
dataType: "json",
success: function (data) {
$.each(data.Name, function (value) {
alert(value);
});
},
error: function (xhr, err) {
// Note: just for debugging purposes!
alert("readyState: " + xhr.readyState +
"\nstatus: " + xhr.status);
alert("responseText: " + xhr.responseText);
}
});
});
HomeController -> ShowLike
[HttpPost]
public ActionResult ShowLike(Liker ids)
{
LikesRepository lkrep = new LikesRepository();
IEnumerable<Like> list = lkrep.GetLikes(ids.CID);
return Json(list);
}
LikesRepository
public class LikesRepository
{
CommentDBDataContext m_db = new CommentDBDataContext();
public IEnumerable<Like> GetLikes(int iden)
{
var result = from c in m_db.Likes
where c.CID == iden
orderby c.Name ascending
select c;
return result;
}
public void AddLike(Like c)
{
m_db.Likes.InsertOnSubmit(c);
m_db.SubmitChanges(); //This works
}
}
After diving into the problem more, we found it was actually triggering an internal server error (500). This was caused by a circular reference from serializing the LINQ to SQL objects to JSON. This issue is discussed several times...
How to remove circular reference in Entity Framework?
How did I solve the Json serializing circular reference error?
Circular Reference exception with JSON Serialisation with MVC3 and EF4 CTP5w
An alternate solution is to return the data as lists of strings as they only required the names.

Pulling sql data into jquery autocomplete

I've looked for an answer to this, but not finding exactly what I'm looking for. So, please excuse if this has been answered any another thread.
I have a very large sql table that I'd like to use in a jquery autocomplete input field. I know I need to return that data as json formatted, just not sure the best way to accomplish this. This field is in an ASP.net MVC application.
I know I could probably do a php page that returns the result, but that seems a bit messy to me. Is the best way to go is by creating a web service that I call?
Thanks in advance for any help.
Here's some code as to how I have accomplished this. I am using the jquery UI plugin
The javascript on my View
$(function () {
$("#suburb").autocomplete({
source: function (request, response) {
$.ajax({
url: '#Url.Action("ManufacturerAutoComplete", "AutoComplete")', type: "POST", dataType: "json",
data: { searchText: request.term, maxResults: 10 },
success: function (data) {
response($.map(data, function (item) {
return { label: item.DisplayValue, value: item.Suburb, id: item.SuburbID, postcode: item.Postcode, state: item.State }
}))
}
})
},
select: function (event, ui) {
$("#state").val(ui.item.state);
$("#postcode").val(ui.item.postcode);
}
});
The input on my view
<input type="text" id="suburb" />
And finally the code from my Controller
[HttpPost]
public JsonResult ManufacturerAutoComplete(string searchText)
{
if (searchText.Length > 2)
{
var service = new SuburbSearchServiceClient();
var results = service.SearchSuburb(searchText, "Australia");
List<Suburbs> sList = new List<Suburbs>();
foreach (var item in results)
{
sList.Add(new Suburbs() { SuburbID = item.SuburbID, Suburb = item.Suburb, State = item.State, Postcode = item.Postcode, DisplayValue = item.Suburb + " - " + item.State + " - " + item.Postcode });
}
return Json(sList);
}
else
{
var data = string.Empty;
return Json(data);
}
}
You need to include the js and css from the jquery-ui plugin and the results of your auticomplete will be shown underneath the input element. As you can see from the JsonResult method, I am calling a web service to get my suburb data, but this could come from anywhere in reality.
Hope this helps.

Resources