MVC Model not updating View - asp.net-mvc

I have an MVC application which is also using Telerik controls. View has the following Kendo Drop down list:
<%= Html.Kendo().DropDownList()
.Name("agents")
.DataTextField("Text")
.DataValueField("Value")
.Enable(Model.IsAdmin)
.DataSource(source =>
{
source.Read(read => read.Action("GetUsers", "Schedule"));
})
.Value(Model.SelectedUser)
.Events(e => e.Select("SelectionChanged"))
%>
When the list is changed it fires the event SelectionChanged which does an Ajax call to method UpdateUser on the controller.
function SelectionChanged(e) {
var dataItem = this.dataItem(e.item.index());
var passeddata = dataItem.Value;
$.ajax({
url: '/Schedule/UpdateUser',
data: { user: passeddata },
success: function () {
}
});
var scheduler = $("#scheduler").data("kendoScheduler");
scheduler.dataSource.read();
}
On my controller The method updates the model and sets a value in the session then calls the Schedule Action to reload the view.
public ActionResult UpdateUser(string user)
{
_taskService.SelectedUser = user;
Session["ScheduledUser"] = user;
return View("Schedule");
}
public ActionResult Schedule()
{
ModelState.Clear();
//Check to ensure the user is not logged out
try
{
if (string.IsNullOrEmpty(Session["LoggedInUser"].ToString()))
return RedirectToAction("LogOut", "Account");
}
catch (Exception) { RedirectToAction("LogOut", "Account"); }
var timeOffset = Session["TimeOffset"].ToString();
_taskService.TimeOffset = Convert.ToInt32(timeOffset);
//Set the last viewed schedule
_taskService.IsAdmin = Convert.ToBoolean(Session["IsAdmin"].ToString());
ViewData["PageName"] = "Schedule";
if (string.IsNullOrEmpty(_taskService.SelectedUser))
{
_taskService.SelectedUser = Session["LoggedInUser"].ToString();
}
_taskService.UserWorkgroups = GetLoggedOnUserWorkgroups(_taskService.SelectedUser);
Session["ScheduledUser"] = _taskService.SelectedUser;
MessageHandler.NewNote("Loading schedule for user: " + _taskService.SelectedUser);
UpdateModel(_taskService);
return View(_taskService);
}
The problem I have is when loaded the view populates an entry on my model called UserWorkgroups and displays the value:
<h4 style="color: #30068b; text-align: Center;">Workgroups: <%:Model.UserWorkgroups%></h4>
Once the drop down has changed it should update the workgroups for the new user and amend the display on the view. I have gone through the code and it is firing the Schedule() action result and populating the correct user and workgroups however the view does not change the workgroups it shows the original value.
Any help here is appreciated.

Related

Html.ActionLink does not pass my id

In my view I have a ActionLink that passes an Id to another View. I used this multiple times but for some reason it does not work on this ActionLink. I even tried with a AJAX POST call in javascript but with no success either. Am I doing something wrong? If so, I am not seeing what.
Controller:
The parameter Id in this function is 0 and should be filled.
public ActionResult NieuwPriveBericht(int Id)
{
TblPER_Personeelslid Sender = BCL.GetEmployeeByLoginName(Session["LoginName"].ToString());
TblPER_Personeelslid Receiver = BCL.GetEmployeeById(Id);
var Model = new TblALG_PrvBericht();
Model.Datum = DateTime.Now.Date;
Model.Zender = Sender.IDPersoneelslid;
Model.Ontvanger = Receiver.IDPersoneelslid;
ViewBag.ReceiverName = Receiver.Voornaam + " " + Receiver.Naam;
return View(Model);
}
public ActionResult PriveBerichten()
{
ViewBag.SelectedEmployee = "";
var Model = new PriveBerichten();
return View(Model);
}
View:
If I debug my view I clearly see #Model.SelectedOption filled.
#using (Html.BeginForm("PriveBerichten", "Home", #Model, FormMethod.Post))
{
#Html.ActionLink("Nieuw bericht maken", "NieuwPriveBericht", new { Id = #Model.SelectedOption }, new { #class = "button-add" })
}
AJAX CALL
$("#DdlEmployees").change(function () {
var SelectedEmployee = $('#DdlEmployees option:selected').val();
$.ajax({
type: "POST",
url: 'PriveBerichten?SelectedEmployee=' + SelectedEmployee, // this works
dataType: "json",
data: $('form').serialize(),
success: function () {
alert("test"); // does not show
},
error: function () {
}
});
})
If you didn't set up the id of route is "Id", you need to use "id". Also delete "#Model" in the BeginForm.
Action
public ActionResult NieuwPriveBericht(int id)
{
//
}
View:
#using (Html.BeginForm("PriveBerichten", "Home", FormMethod.Post))
{
#Html.ActionLink("Nieuw bericht maken", "NieuwPriveBericht",
new { id = #Model.SelectedOption }, new{ #class = "button-add" })
}
Thanks for showing the ActionResult that generates the view. I think this is your problem:
var Model = new PriveBerichten();
return View(Model);
I assume your class PriveBerichten contains the SelectedOption property? If you do not change/initialize this property value in the constructor of PriveBerichten it is 0 by default, and so it will be 0 in your actionlink.

AngularJS - how to send int[] to an action?

Ok, I have a checkbox list showing roles data on my project.
To retrieve these roles, I'm doing this below:
$scope.Roles = [];
$scope.init = function() {
$.get('/Navigation/NavigationNewDependencies', {}, function(result) {
for (var I = 0; I < result.Roles.length; ++I) {
var splited = result.Roles[I].split(";");
$scope.Roles.push({ id: splited[0], name: splited[1], checked: false });
}
$scope.$apply();
});
}
And showing this way:
<div>
Roles:
<ul data-ng-repeat="role in Roles">
<li><input type="checkbox" data-ng-model="role.checked" />{{role.name}}</li>
</ul>
</div>
Ok, it works properly. I want to send to an action these checked roles on the form, you know? How can I do it?
I'm trying to do the POST this way below:
$scope.getSelectedRoles = function () {
var selectedRoles = [];
for (var I = 0; I < $scope.Roles.length; ++I) {
if ($scope.Roles[I].checked == true) {
var role = $scope.Roles[I];
selectedRoles.push({ RoleId: role.id, RoleName: role.name });
}
}
return selectedRoles;
}
$scope.submit = function () {
$.post('/Navigation/New', {
title: $scope.model.NavigationName,
description: $scope.model.NavigationDescription,
controller: $scope.model.NavigationController,
action: $scope.model.NavigationAction,
roles: $scope.getSelectedRoles()
}, function (result) {
if (result.IsValid) {
window.location.href = result.ReturnUrl;
} else {
alert(result.Message);
}
});
}
... But I can't get the right selected roles on the form.
First: How can I get the selected roles with some computed property or something else?
Second: the right parameters to receive this selected roles are params int[] roles?
Thank you all!
Something like this:
//Ends up being the array of ints for the action.
//You can put this in any event handler you want, like another function attached
//to a ngCLick directive
var selectedRoles = $scope.Roles.filter(function(role) { return role.checked}).
.map(function(role) { return role.id});
$.put('/Navigation/NavigationNewDependencies', {roles: selectedRoles})
.success(function(){//Do something cool on successfully sending to server});
This assumes a few things:
Your '/Navigation/NavigationNewDependencies' action can handle 'put's
The array of ints you are passing to the action is an array of the selected role ids
I'm assuming you want to do a standard, non-ajax form submit and hit an MVC controller action. If that's the case, update your checkbox markup to this:
<input type="checkbox" data-ng-model="role.checked" value="{{ role.id }}" name="roles" />
Then wrap it all in a form tag with its action attribute pointing to an action method with a single parameter named "roles" of type int[].
UPDATE:
#Kiwanax, this should do it:
Angular Controller:
function RolesCtrl($scope, $http) {
// use Angular's $http service to communicate with the server
// won't need to call $scope.$apply();
$http.get('/Navigation/NavigationNewDependencies').success(function (result) {
// try to send json object array from the server (instead of a string)
$scope.roles = result;
// but you don't have control over it,
// process your string result the way you are currently doing it
});
$scope.getSelectedRoleIds = function () {
var result = [];
angular.forEach($scope.roles, function (value, key) {
if (value.checked) result.push(value.id);
});
return result;
};
$scope.postData = function () {
$http.post(
'/Navigation/New',
{ roles: $scope.getSelectedRoleIds() }
).success(function () {
alert('done');
});
};
}
View (use the ng-submit attribute to offload form submission handling to Angular):
<div ng-app ng-controller="RolesCtrl">
<form ng-submit="postData()">
<ul data-ng-repeat="role in roles">
<li><input type="checkbox" ng-model="role.checked"/>{{role.name}}</li>
</ul>
<button>submit roles</button>
</form>
MVC Controller's (Navigation) Action:
[HttpPost]
public ActionResult New(int[] roles)
{
// process roles Ids
return View();
}

ASP.NET MVC Sort Listing by Selected item in DropDownList

I have a List of Listings within my Database. On my View, I have a DropDownList which contains categories. A category contains many listings.
When I select a specific category, I wish to only display those listings which have the category selected.
My main worry is how to generate the JQuery to call my SortListing method in my ListingController.
Here is the current HTML (Razor):
#Html.DropDownListFor(m => m.Categories, Model.Categories, "Select a Category")
My SortListing Method:
public List<Listing> SortListing(string categoryId)
{
var listings = new List<Listing>();
foreach (var listing in _service.ListAllEntities<Listing>())
{
if (listing.CategoryId == categoryId)
{
listings.Add(listing);
}
}
return listings;
}
EDIT I have the following. Put the categoryGuid is coming in null.
This is my code:
$(function () {
$('#SelectedCategoryGuid').change(function () {
var url = $(this).data('url');
var categoryId = $(this).val();
$.ajax({
url: url,
type: 'GET',
cache: false,
data: { categoryId: categoryId },
success: function (result) {
// TODO: manipulate the result returned from the controller action
}
});
});
});
</script>
<h2>Index</h2>
#Html.ActionLink("Create New Listing", "Create")
<br/>
<strong>Filter Listings</strong>
#Html.DropDownListFor(
m => m.SelectedCategoryGuid,
Model.Categories,
"Select a Category",
new {
id = "SelectedCategoryGuid",
data_url = Url.Action("SortListing", "Listing")
}
)
My main worry is how to generate the JQuery to call my SortListing
method in my ListingController.
Actually you should be having other worries as well that I will try to cover throughout my answer.
There's an issue with your DropDownListFor helper. You have used the same property on your model for both binding the selected category value and the list of categories which is wrong. The first parameter of the DropDownListFor helper represents a lambda expression pointing to a primitive type property on your view model:
#Html.DropDownListFor(
m => m.CategoryId,
Model.Categories,
"Select a Category",
new {
id = "categoryDdl",
data_url = Url.Action("SortListing", "Listing")
}
)
and then subscribe to the .change() event and trigger the AJAX request:
$(function() {
$('#categoryDdl').change(function() {
var url = $(this).data('url');
var categoryId = $(this).val();
$.ajax({
url: url,
type: 'GET',
cache: false,
data: { categoryId: categoryId },
success: function(result) {
// TODO: manipulate the result returned from the controller action
}
});
});
});
Now let's take a look at your SortListing controller action because there are issues with it. In ASP.NET MVC standard convention dictates that controller actions must return ActionResults. In your case you seem to be returning some List<Listing>. So the first thing you have to decide is the format you would like to use. One possibility is to return those listings as JSON formatted values:
public ActionResult SortListing(string categoryId)
{
var listings = _service
.ListAllEntities<Listing>()
.Where(x => x.CategoryId == categoryId)
.ToList();
return Json(listings, JsonRequestBehavior.AllowGet);
}
In this case inside your AJAX success callback you will receive this collection of listings and you will have to update your DOM:
success: function(result) {
// result represents an array of listings
// so you could loop through them and generate some DOM elements
}
Another possibility is to have your controller action return a partial view:
public ActionResult SortListing(string categoryId)
{
var listings = _service
.ListAllEntities<Listing>()
.Where(x => x.CategoryId == categoryId)
.ToList();
return PartialView("Listings", listings);
}
and then you will have a corresponding partial view:
#model List<Listing>
#foreach (var listing in Model)
{
<div>#listing.SomeProperty</div>
}
and then inside the success callback you will refresh some containing placeholder:
success: function(result) {
$('#SomeDivIdThatWrapsAroundTheListingsPartial').html(result);
}
So to recap you could either have the controller action return JSON and then manually build the corresponding DOM tree using javascript or return a partial view which will already contain the corresponding markup and simply refresh some containing div with this partial.

Dynamic telerik TabStrip

I'm working on a dynamic Telerik Tab Strip.
Each tab have a Grid and I need to pass a parameter to the action for filter my model but that parameter is allways null.
View:
#{ Html.Telerik().TabStrip()
.Name("TabStripDetailArticle")
.Items(tabstrip =>
{
//Know how Many Zones are there
var zones = Model.Articles.GroupBy(e => e.Zone);
//For each Zone I need a Tab,
foreach (var inZone in zones)
{
tabstrip.Add()
.Text(inZone.Key)
.Content(() =>
{
//On each tab there's a Grid and I need to pass the zone to filter my model.
#Html.Action("TabStripSelected", Controllers.Valoration, new { idZone = inZone.Key });
});
}
}
)
.SelectedIndex(0)
.Render();
}
Controller:
public ActionResult TabStripSelected(string idZone)
{
return PartialView("_GridArticlesByZone",CurrentHvm.Articles.Where(e => e.Zone == idZone));
}
I would like to know if ther's another way to do that, or if I'm missing something.
Thank's!
I have found the problem! :)
Controller:
foreach (var inZone in zones)
{
//!! Missing!
**IGrouping<string, Article> zone = inZone;**
tabstrip.Add()
.Text(inZone.Key)
.Content(() =>
{
#Html.Action("TabStripSelected", Controllers.Valoration, new { id = **zone.Key** });
});
}
Thank's!

Persist CheckBox State in Telerik MVC Grid While Paging in ASP.NET MVC Application

I am using Telerik MVC Grid where one of the columns is checkboxes. If I select checkboxes and then go to page 2 and then come back to page 1 all the checkboxes are gone. Which is of course the way HTTP works. Now, I put all the selected checkboxes inside the hidden field but since the grid does some sort of postback my hidden field is cleared next time.
If you're using Client Side data binding you can use the javascript/jquery below to maintain checkbox state.
Maintain checkbox state:
var selectedIds = [];
$(document).ready(function () {
//wire up checkboxes.
$('#YOUR_GRID_ID :checkbox').live('change', function (e) {
var $check = $(this);
console.log($check);
if ($check.is(':checked')) {
//add id to selectedIds.
selectedIds.push($check.val());
}
else {
//remove id from selectedIds.
selectedIds = $.grep(selectedIds, function (item, index) {
return item != $check.val();
});
}
});
});
Restore checkbox state after data binding:
function onDataBound(e) {
//restore selected checkboxes.
$('#YOUR_GRID_ID :checkbox').each(function () {
//set checked based on if current checkbox's value is in selectedIds.
$(this).attr('checked', jQuery.inArray($(this).val(), selectedIds) > -1);
});
}
A more verbose explanation available on my blog:
http://blog.cdeutsch.com/2011/02/preserve-telerik-mvc-grid-checkboxes.html
You need to save the state of the checkboxes to your database, and then retrieve them again from the database when you reload the page.
During paging, you need to reload only those records that pertain to a particular page. You can do that using the Skip() and Take() methods from Linq.
to preserve checked /unchecked checkbox state using telerik grid clientemplate across postbacks and async postbacks and in refreshing grid and (auto)paging, I tried the solution above with no avail and so went up with a bit harder solution; as I could not save the state in db, I used a session variable and an hiddenfield:
first, a way to do ajax postback (see function DoAjaxPostAndMore , courtesy of somebody herearound), where in success we take care of client values of selections, adding and removing as checked /unchecked
I also had to manually check / uncheck the checkboxes inside the manual ajax post
second, an hidden field (see 'hidSelectedRefs') to preserve clientactions, as the Session variable I am using will not be seen clientside in partial rendering
#model IEnumerable<yourInterfaceOrClass>
#{
ViewBag.Title = "Select Something via checkboxes";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Select Something via checkboxes</h2>
<!-- we need a form with an id, action must be there but can be an empty string -->
<form id="frm" name ="frm" action="">
<p>
<!--we need this as Session new values will not be takein in aajax requests clientisde, so it is easier to mange this field, which, in first and subsequent complete postbacks can have the value of the Session variable -->
<input type="hidden" name="hidSelectedRefs" id="hidSelectedRefs" value= '#Session["SelectedReferencesToPrint"]' />
</p>
<br />
<script type="text/javascript">
//ajax manual post to a custom action of controller, passing the id of record and the state of the checkbox
//to adjust the Session value
//data: $form.serialize() will have the single checbox value
//but only if checked. To make my life eaasier, I added the value (is id ) and the checked/unchecked
//state of checkbox (is the $(chkClicked).attr('checked'))
function DoAjaxPostAndMore(chkClicked) {
var $form = $("#frm");
$.ajax({
type: "POST",
url: 'SelectReferences',
data: $form.serialize() + '&id=' + $(chkClicked).val() + '&checked=' + $(chkClicked).attr('checked'),
error: function (xhr, status, error) {
//do something about the error
alert("Sorry, we were not able to get your selection...");
},
success: function (response) {
//I also needed to check / uncheck manually the checkboxes:
$(chkClicked).attr('checked', !$(chkClicked).attr('checked'));
//and now put correct values in hidSelectedRefs hidden field:
if ($(chkClicked).attr('checked')) {
$('input[name=hidSelectedRefs]').val($('input[name=hidSelectedRefs]').val() + '|' + $(chkClicked).val() + '|');
} else {
var tmp = $('input[name=hidSelectedRefs]').val();
$('input[name=hidSelectedRefs]').val(tmp.toString().replace('|' + $(chkClicked).val() + '|', ''));
}
}
});
return false; // if it's a link to prevent post
}
Then I handled the OnRowDataBound, to ensure the checboxes would be correctly checked on postbacks,
function onRowDataBound(e) {
var itemsChecked = $('input[name=hidSelectedRefs]').val();
if (itemsChecked)
{
if (itemsChecked.indexOf('|' + $(e.row).find('input[name=checkedRecords]').val() + '|') >= 0)
{
$(e.row).find('input[name=checkedRecords]').attr('checked', true);
}
}
}
</script>
The telerik mvc Grid is as follows:
(you can see I also handled OnDataBinding and OnDataBound, but thats's only to show a
"Loading" gif. The controller is named "Output" and the action that normally would be called "Index" here is callled "PrintReferences". The correspondenting Ajax action is called "_PrintReferences")
Of interest here is the ClientTemplate for checkbox (cortuesy of someone else herearound, where onclick
we call our custom ajax action (named "SelectReferences") on our Output controller via a call to the
DoAjaxPostAndMore() javascript/jquery function
#(Html.Telerik().Grid<yourInterfaceOrClass>()
.Name("Grid")
.ClientEvents(e => e.OnDataBinding("showProgress").OnDataBound("hideProgress").OnRowDataBound("onRowDataBound"))
.DataBinding(dataBinding =>
{
dataBinding.Server().Select("PrintReferences", "Output", new { ajax = ViewData["ajax"]});
dataBinding.Ajax().Select("_PrintReferences", "Output").Enabled((bool)ViewData["ajax"]);
})
.Columns( columns =>
{
columns.Bound(o => o.ID);
columns.Bound(o => o.ID)
.ClientTemplate(
"<input type='checkbox' name='checkedRecords' value='<#= ID #>' onclick='return DoAjaxPostAndMore(this)' />"
)
.Width(30)
.Title("")
.HtmlAttributes(new { style = "text-align:center; padding: 0px; margin: 0px;" });
columns.Bound(o => o.TITLE);
columns.Bound(o => o.JOBCODE);
columns.Bound(o => o.ORDERCODE );
//columns.Bound(o => o.AUTHOR);
columns.Bound(o => o.STATE);
columns.Command(commands =>
commands
.Custom("Details")
.ButtonType(GridButtonType.Image)
.HtmlAttributes(new { #class = "t-icon-details" })
.DataRouteValues(route => route.Add(o => o.ID)
.RouteKey("ID"))
.Ajax(false)
.Action("Details", "Referenza")
);
})
.Pageable(paging =>
paging.PageSize(10)
.Style(GridPagerStyles.NextPreviousAndNumeric)
.Position(GridPagerPosition.Bottom))
.Sortable()
.Filterable()
.Resizable(resizing => resizing.Columns(true))
.Reorderable(reorder => reorder.Columns(true))
.NoRecordsTemplate("No Reference found. Please review your filters...")
.ColumnContextMenu()
)
</form>
that's all for the View. Now, to the controller:
//Get : this is the usally called "Index" action
//here we can load data, but we also must ensure the Session variable is fine
public ActionResult PrintReferences(bool? ajax, string msgInfo, string selectedRef)
{
if (Session["SelectedReferencesToPrint"] == null)
{
Session["SelectedReferencesToPrint"] = string.Empty;
}
if (string.IsNullOrEmpty(selectedRef))
{
selectedRef = "|0|";
}
string msgOut = string.Empty;
//this is where I get data to show
List<yourInterfaceOrClass> ret = LoadData(out msgOut);
if (!string.IsNullOrEmpty(msgInfo) && !string.IsNullOrEmpty(msgInfo.Trim()))
{
msgOut = msgInfo + ' ' + msgOut;
}
ViewBag.msgOut = msgOut;
ViewData["ajax"] = ajax ?? true;
return View(ret);
}
//GridAction: here is telerik grid Ajax get request for your "_Index"
[GridAction]
public ActionResult _PrintReferences(string msgInfo)
{
//again, we must take care of Session variable
if (Session["SelectedReferencesToPrint"] == null)
{
Session["SelectedReferencesToPrint"] = string.Empty;
}
string msgOut = string.Empty;
List<yourInterfaceOrClass> ret = LoadData(out msgOut);
return View(new GridModel(ret));
}
//Post: this is where our custom ajax post goes
//we are here if a checkbox is cheched or unchecked
//in the FormCollection parameter we get the checkbox value only if checked, and also
//(and always) the parms we passed (id of record and state of checkbox: we cannot simply add,
//we must also subtract unchecked)
[HttpPost]
public ActionResult SelectReferences(FormCollection collection)
{
//we need a session variable
if (Session["SelectedReferencesToPrint"] == null)
{
Session["SelectedReferencesToPrint"] = string.Empty;
}
//use a local variable for calculations
string wholeSelectionToPrint = Session["SelectedReferencesToPrint"].ToString();
//get value passed: id
string selectedRefId = collection["id"];
if (!string.IsNullOrEmpty(selectedRefId))
{
selectedRefId = "|" + selectedRefId + "|";
}
bool cheked = (collection["checked"].ToString()=="checked");
//get vcalue passed :checked or unchecked
if (cheked)
{
//the element is to add
wholeSelectionToPrint += selectedRefId;
}
else
{
//the element is to remove
wholeSelectionToPrint = wholeSelectionToPrint.Replace(selectedRefId, "");
}
//set session variable final value
Session["SelectedReferencesToPrint"] = wholeSelectionToPrint;
return null;
}
//normal postback:
//we will be here if we add a button type submit in our page,
//here we can collect all data from session variable to do
//something with selection
[HttpPost]
public ActionResult PrintReferences(FormCollection collection)
{
//get selected references id
if (Session["SelectedReferencesToPrint"] == null)
{
Session["SelectedReferencesToPrint"] = string.Empty;
}
//use a local variable for calculations
string wholeSelectionToPrint = Session["SelectedReferencesToPrint"].ToString();
wholeSelectionToPrint = wholeSelectionToPrint.Replace("||", "|");
string[] selectdIDs = wholeSelectionToPrint.Split(new char[] { '|' });
foreach (string id in selectdIDs)
{
if (!string.IsNullOrEmpty(id))
{
//do something with single selected record ID
System.Diagnostics.Debug.WriteLine(id);
}
}
//omitted [....]
ViewData["ajax"] = true;
return View(ret);
}

Resources