I've been stumped for days.
I have an index page that contains a renderpartial view. A viewmodel is passed to the index page from its controller, then passed from inside the index.cshtml to the renderpartial view as an extension. The renderpartial view is automatically updated every 10 seconds (via jquery function to the controller from the index page) to update its content, which works fine. The index page contains several checkboxfor's that filter out the contents of the renderpartial view. The problem arises when the initial renderpartial view is called when the time period has elapsed, the controller for the renderpartial view does not have the correct model data the controller for the index had prior. Boolean values in the model which were set to true while in the index controller now are false when we get to the renderpartial view. Lets begin...
My Index View:
#model SelfServe_Test2.Models.NGTransCertViewModel
...
<div class="Services_StatusTable" id="refreshme">
#{
Html.RenderPartial("_Data", Model);
}
</div>
...
#Html.CheckBoxFor(m => m.NGTransServicesModel.filter_NJDVSVR24, new { onclick = "test(id)" }) #Html.Label("NJDVSVR24", new { })
...
<script src="~/Scripts/jquery-1.12.4.js"></script>
<script type="text/javascript">
$(function () {
setInterval(function () { $('#refreshme').load('/NGTransCertServices/Data'); }, 10000); // every 10 seconds
function test(filter) {
alert(filter);
var serviceChecked = document.getElementById(filter).checked;
$.ajax({
type: "POST",
url: "/NGTransCertServices/ToggleVisibleService",
data: { 'filterOnService': filter, 'serviceChecked': serviceChecked, 'model': #Model },
//success: function (result) {
// if (result === "True")
// alert("yup");
// else
// alert("nope");
//}
});
}
</script>
The PartialView _Data.cshtml:
#model SelfServe_Test2.Models.NGTransCertViewModel
...
<table>
foreach (var item in Model.NGTransServicesList)
{
if (Model.NGTransServicesModel.filter_EBT == true)
{
if (item.Description.Contains("EBT"))
{
}
}
}
</table>
My ViewModel:
namespace SelfServe_Test2.Models
{
public class NGTransCertViewModel
{
public NGTransCertViewModel()
{
NGTransServicesModel = new NGTransCertServicesModel();
NGTransServicesList = new List<NGTransCertServicesList>();
NGTransServices = new NGTransCertServices();
}
public NGTransCertServicesModel NGTransServicesModel { get; set; }
public List<NGTransCertServicesList> NGTransServicesList { get; set; }
public NGTransCertServices NGTransServices { get; set; }
}
}
The Controller:
public class NGTransCertServicesController : Controller
{
NGTransCertViewModel NGT_VM = new NGTransCertViewModel();
NGTransCertServicesModel certServicesModel = new NGTransCertServicesModel();
public ActionResult Index()
{
NGTransCertServices certServices = new NGTransCertServices();
NGT_VM.NGTransServicesModel = certServices.InitServiceTypeCheckBoxes(certServicesModel); // sets all checkboxes to true initially.
return View(NGT_VM);
}
[OutputCache(NoStore = true, Location = System.Web.UI.OutputCacheLocation.Client, Duration = 10)] // in seconds
public ActionResult Data()
{
NGTransCertDBHandle certDBHandle = new NGTransCertDBHandle();
List<NGTransCertServicesList> List_certServices = certDBHandle.GetService();
return PartialView("_Data", NGT_VM);
}
}
Finally, the model where the values are lost:
public class NGTransCertServicesModel
{
...
public bool filter_NJDVSVR24 { get; set; }
...
}
Now then, when the Index.cshtml page is called, i run the InitServiceTypeCheckBoxes method that sets the checkbox values to true, pass the viewmodel to the index page and pass that same model to the renderpartial. All is happy until the 10s timeout is reached and _Data.cshtml is rendered. The checkbox values are now all false.
Let me add a visual element. Below is the model when returning from the controller to the index view with the Boolean set to true as desired. (stepping through)
Below is the model when the index view
Again, in the _Data.cshtml partial view
Now with a breakpoint in the Data action in the controller, that same bool value is now false
The bool does not have the true value even before the first line of code in the Data action.
NGTransCertDBHandle certDBHandle = new NGTransCertDBHandle();
I think the issue is that you're not populating your view model correctly in the Data method of your controller.
In both methods you're sending the NGT_VM property to the view, but you only populate some of the data in the Index method - this data will not be persisted or created by default when you call the Data method.
Each time a request hits a controller method, that controller is created afresh, and only the constructor and requested method are called. In the case of a request to Data the controller is created, the NGT_VM property is set back to the default NGTransCertViewModel object, with a default NGTransCertServicesModel object (the boolean property filter_NJDVSVR24 will default to false). You then create and ignore a variable List_certServices, but at no point have you updated the NGTransServicesModel property on the view model to match the values you had from the Index method.
You should probably assign the NGTransServicesList variable to the NGT_VM.NGTransServicesList after you populate it:
[OutputCache(NoStore = true,
Location = System.Web.UI.OutputCacheLocation.Client,
Duration = 10)]
public ActionResult Data()
{
NGTransCertDBHandle certDBHandle = new NGTransCertDBHandle();
List<NGTransCertServicesList> List_certServices = certDBHandle.GetService();
NGT_VM.NGTransServicesList = List_certServices;
return PartialView("_Data", NGT_VM);
}
You could either call same methods to update the NGTransServicesModel as required in the Data method, but I'm not sure that's really the behaviour you're after?
Related
I am trying to reload my Partial View _Home when I close my dropdown. Currently I can get that dropdown value to call another method and do the logic that will change the model then send the model back to the index. However, I cannot get my _Home Partial View to reload and actually take in this new model. I cannot seem to be able to actually call the public ActionResult _Home(Model model) method. I am wondering how to pass in this new model and refresh the partial view.
I have tried to pass in the model to the partial view but this seems to have no effect, having no model being passed in does not change anything either. I have seen a few examples online of how to refresh partial views but none of them pass in any parameters.
JavaScript
function OnClose() {
var chart = $("#safetyIncident-chart").data("kendoChart");
var chart2 = $("#qualityError-chart").data("kendoChart");
chart.dataSource.read();
chart2.dataSource.read();
var selectProductionLine = $("#productionLine-dropdown").data("kendoDropDownList").value();
$.ajax({
url: '#Url.Action("UpdateView", "Home")', //Updates model and then calls Index
type: 'post',
dataType: "json",
data: { "selectProductionLine": JSON.stringify(selectProductionLine) }, //Passes in DropDownValue correctly
success: function (data) {
$("#partial").html(result);
$("#Dashboard").load("/Home/_Home"); //Trying to reload the partial view
}
});
}
View
<div id="Dashboard">
#Html.Partial("~/Views/Home/_Home.cshtml")
</div>
Controller
[HttpGet]
public ActionResult Index()
{
DisplayViewModel dvm = (DisplayViewModel)TempData["dvm"];
if(dvm != null)
{
return View(dvm);
}
//Initializes new dvm if previous was null
return View(dvm);
}
I am trying to refresh my partial view once DropDown closes and the ViewModel is refreshed.
EDIT:
UpdateView method in Controller
[HttpPost]
public ActionResult UpdateView(string selectProductionLine)
{
if(selectProductionLine == null)
{
selectProductionLine = _productionLineService.Collection().FirstOrDefault().Id;
}
var serializer = new JavaScriptSerializer();
dynamic jsondata = serializer.Deserialize(selectProductionLine, typeof(string));
DisplayViewModel dvm = new DisplayViewModel();
ProductionLine pl = _productionLineService.Find(jsondata);
dvm.ProdLine = new ProductionLineViewModel
{
Id = pl.Id,
CreatedAt = pl.CreatedAt,
Name = pl.Name,
ActiveLine = pl.ActiveLine,
ComputerName = pl.ComputerName,
UPE = pl.UPE
};
TempData["dvm"] = dvm;
return Index();
//PartialView("~/Home/_Home.cshtml", dvm);
}
I am submitting some info using an Ajax post which is supposed to refresh a partial view. The info gets sent to the database however on return of the partial view, the Fees.Feetype data can't be rendered as it is null.
On manual refresh, the Fees.FeeType is rendered with no problem.
How do I get the FeeType to be included in the partial view after the ajax post?
Models
public class Members
{
public virtual ICollection<Fees> Fees { get; set; }
}
public class Fees
{
public int FeeTypesId { get; set; }
public virtual FeeTypes FeeType { get; set; }
public virtual Members Members { get; set; }
}
public class FeeTypes
{
public int Id { get; set; }
public string FeeTypeName { get; set; }
}
Ajax Post
var token = $('[name=__RequestVerificationToken]').val();
var postData = {
__RequestVerificationToken: token,
FeeOccurence: feeOccField.val(),
FeeAmount: amountField.val(),
FeeStatus: statusField.val(),
FeeTypesId: typeField.val(),
FeeDate: dateField.val(),
MemberNo: memberNo
};
$.ajax({
url: '/Members/AddFees',
type: 'POST',
data: postData,
success: function (members) {
alert(result);
$("#details").html(members);
},
error: function (result) {
alert(result);
},
traditional: true
Controller
public ActionResult AddFees (Fees fees)
{
if (ModelState.IsValid)
{
db.Fees.Add(fees);
db.SaveChanges();
Members members = new Members();
members = db.Members.Find(fees.MemberNo);
return PartialView("~/Views/Members/_MemberDetails.cshtml", members);
}
}
Views
#model AccPortal.Models.Members
<div class="col-md-7 md-fees" id ="details">
#Html.Partial("_MemberDetails", Model)
</div>
//This is the partial view.
#model AccPortal.Models.Members
#foreach (var item in Model.Fees)
{
//The error happens on this line but all other Fees properties are rendered without issue.
<td class="md-fee-value"data-type="status">#item.FeeType.FeeTypeName</td>
}
These are the things you need to change in your code,
First, return a partial view with model from your Action method like this,
return PartialView("_MemberDetails", members);
Datatype should be html in your ajax method. So add this bellow line inside ajax method. You can add this line after this line data: postData,
dataType: "html",
Now in your ajax success method, change like this,
success: function (members) {
//alert(result); what is result here, that you have written
$("#details").html(''); //first make it blank then render
$("#details").html(members);
},
In your partial view, you need to check whether FeeType is there or not before render the value,
#model AccPortal.Models.Members
#foreach (var item in Model.Fees)
{
if(item.FeeType != null)
<td class="md-fee-value"data-type="status">#item.FeeType.FeeTypeName</td>
else
<td class="md-fee-value"data-type="status"></td> //You can write NA here
}
The issue with the null type was lazy loading in the end.
I had to requery the db after I had saved the changes and include Fees.FeeTypes to return the correctly populated model.
members = db.Members.Find(fees.MemberNo);
members.Fees = db.Fees.Include(x => x.FeeType).Where(x => x.Members.MemberNo.Equals(x.MemberNo)).ToList();
you need to check for null values before populating in the list to avoid null exception
#model AccPortal.Models.Members
#foreach (var item in Model.Fees)
{
if(item.FeeType != null)
<td class="md-fee-value"data-type="status">#item.FeeType.FeeTypeName</td>
}
because some data might be missing which is causing problem so with if condition it will populated only that data which is not null
I am trying to make an online bank website(for learning ASP.NET MVC).I have a class Account
class Account
{
int account_id;
String account_number;
decimal balance;
}
and I have a model for transaction.
public class MakeTransactionModel
{
[Required]
public String AccountFrom { get; set; }
[Required]
public String AccountTo { get; set; }
public Decimal OrignalBalance { get; set; }
[Required]
public Decimal Amount { get; set; }
[Required]
public String TransactionFor { get; set; }
}
Then in controller, i am putting accounts in ViewBag.
ViewBag.account_from = new SelectList(db.Accounts, "account_id", "account_number");
In View, I created a drop down for showing all accounts
#Html.DropDownListFor(u => u.AccountFrom, (SelectList)ViewBag.account_from, htmlAttributes: new { #class = "form-control", #id = "AccountFrom", onchange=#"
#Model.OrignalBalance = 1000; // I tried this but did not work
" })
Now , I am trying to show balance of selected account in an EditorFor
#Html.EditorFor(model => model.OrignalBalance, new { htmlAttributes = new { #id="OrignalBalance", #class = "form-control", disabled = "disabled", #readonly = "readonly" } })
I have all accountsin ViewBag and I am showing that accounts number in drop down (those accounts also have balance in it). I am trying to change value of EditorFor on DropDownFor value change but still unable to do that. I tried to do that using jquery, but i don't know can I use LINQ in jquery
My jquery code is
<script type="text/javascript">
$(document).ready(function () {
$(function () {
$('#AccountFrom').change(function () {
var selectedValue = $('#AccountFrom').text();
$('#OrignalBalance').val(#{new BankEntities().Accounts.SingleOrDefault(acc => acc.account_number == $('#AccountFrom').text())}); // I am trying to do this
});
});
}
)
</script>
It will be good if i find a good solution to do that, so I can update EditorFor on change event.
Thank you.
You should make an ajax call and pass the account number and get the amount from the server.
$(function()
{
$('#AccountFrom').change(function() {
var accountId= $('#AccountFrom').val();
var url="#Url.Action("Balance","Account")";
$.post(url+"?accountNumber="+accountId,function(response){
if(response.Status==="success")
{
$("#OrignalBalance").val(response.Balance);
}
else
{
alert("Invalid Account");
}
});
});
});
Assuming you have an action method to return the balance
[HttpPost]
public ActionResult Balance(string accountNumber)
{
//Of course you want to authorize the call
var db=new BankEntities();
var a= db.Accounts.FirstOrDefault(x=> x.account_number ==accountNumber);
if(a!=null)
{
return Json( new { Status="success", Balance=a.Balance });
}
else
{
return Json( new { Status="error"});
}
}
If you do not wish to make an action method and the ajax way, What you can do is, Create a dictionary of your account number and the balance and pass that as part of your view model and in your razor view, set that to a js object and in the change event you can query the js dictionary to get the value.
Also, I recommend to NOT use ViewBag to transfer data between your action method and your view for rendering the dropdown. You should add a strongly typed property to handle that.
So let's add some new properties to your view model.
public class MakeTransactionModel
{
// Your other existing properties here
public Dictionary<string,decimal> AccountBalances { set; get; }
// These 2 properties are for rendering the dropdown.
public int FromAccountId { set; get; }
public List<SelectListItem> FromAccounts { set; get; }
}
And in your GET action, fill this property with account number and corresponding balance value.
public ActionResult Transfer()
{
var vm = new MakeTransactionModel();
vm.AccountBalances = new Dictionary<string, decimal>();
// Hard coded for demo. You may read it from your db tables.
vm.AccountBalances.Add("CHECKING0001", 3450.50M);
vm.AccountBalances.Add("SAVINGS0001", 4450.50M);
//load the data for accounts.pls change to get from db
vm.FromAccounts = new List<SelectListItem>
{
new SelectListItem { Value="CHECKING0001", Text="Checking" },
new SelectListItem { Value="SAVINGS0001", Text="Saving" }
};
// to do : Load other properties also
return View(vm);
}
And in your razor view, serialize this property and set to a js object.
#model MakeTransactionModel
#using(Html.BeginForm())
{
#Html.DropDownListFor(s=>s.FromAccountId,Model.FromAccounts,"Select")
#Html.EditorFor(model => model.OrignalBalance,
new { #id="OrignalBalance", #class = "form-control",
disabled = "disabled", #readonly = "readonly" } )
<input type="submit" />
}
#section Scripts
{
<script>
var balanceDict = #Html.Raw(Newtonsoft.Json.JsonConvert
.SerializeObject(Model.AccountBalances));
$(function () {
$('#FromAccountId').change(function() {
var accountId= $('#AccountFrom').val();
var v = balanceDict[accountId];
$("#OrignalBalance").val(v);
});
});
</script>
}
It may not seem like it, but this is pretty broad. Basic rundown, you'll either have to:
Serialize all accounts and balances into JSON and store them client-side:
This is more code than is appropriate here, but you could use JSON.net to get JSON for new BankEntities().Accounts.ToList() (something you should be getting from your controller code, by the way, not in your view), then set a window variable to that in your JavaScript, and call upon that whenever the value changes.
Untested, but something like:
var balances = #Html.Raw(JsonConvert.SerializeObject(new BankEntities()
.Accounts
// Filter by logged in user
.ToDictionary(c => c.account_number, c.balance));
$(document).ready(function () {
$(function () {
$('#AccountFrom').change(function () {
var selectedValue = $('#AccountFrom').text();
$('#OrignalBalance').val(balances[selectedValue]);
});
});
}
Introduce an API call performed through AJAX to get balances whenever the value changes.
Shyju beat me to this one, but it's probably a better way to do it, as long as you're comfortable introducing an API element. It's kind of advanced for first learning MVC, but it's not too complicated.
This is what I'd do in a production application (although I'd do it with Web API), but for just playing, the first option is a little quicker, and probably easier to understand and debug fully if you're just getting started.
The confusion here comes from where code is executed. Your script can't refer to the BankEntities because it's running client-side, as compared to server side.
JQuery knows nothing about LINQ, since it is client based. So, I suggest making an ajax request when the account from gets changed.
for example, in the view, make the ajax call
<script type="text/javascript">
$(document).ready(function() {
$('#AccountFrom').change(function() {
var selectedAccountNumber = $('#AccountFrom option:selected').text();
$.ajax({
url: "/Accounts/GetAccountBalance",
type: "POST",
dataType: "json",
data: { accountNumber: selectedAccountNumber },
success: function (
$('#OrignalBalance').val(data.Balance);
}
});
});
});
</script>
and have the following in the controller (let's say that you have a controller called Accounts)
public ActionResult GetAccountBalance(string accountNumber)
{
var account = db.Accounts.SingleOrDefault(a => a.account_number == accountNumber);
// add validation logic for account not exits
return Json(new { AccountNumber = accountNumber, Balance = account.balance });
}
[HttpPost]
public ActionResult AddToCart(int phoneListingID, string sellerSKU)
{
ShoppingBasket shoppingBasket = new ShoppingBasket();
BasketItem currentItem = new BasketItem
{
sellerID = 1,
Price = 100,
Quantity = 1,
sellerSKU = "testsku"
};
shoppingBasket.AddtoBasket(currentItem, this.HttpContext);
var viewModel = new BasketViewModel
{
basketItems = ShoppingBasket.GetBasketItems(this.HttpContext),
basketTotal = ShoppingBasket.GetBasketTotal(this.HttpContext)
};
return View(viewModel);
}
My form:
#using (Html.BeginForm("AddToCart","ShoppingBasket",new { phoneListingID = 12345, sellerSKU = "test"}, FormMethod.Post ))
{
<input type="submit" value="AddToCart" />
}
The expected result is that my BasketViewModel page is returned, however the view being returned is ShoppingBasket/AddToCart?PhoneID=xxxx&sellerSKU=xxxx
What am I doing wrong?
In MVC Suppose your action is like
public ActionResult MyAction()
{
return View();
}
In this scenerio it will point to the view named 'MyAction'. If you want to send it to another view make it like
public ActionResult MyAction()
{
return View("MyViewName");
}
If you want to pass some model to make it like
public ActionResult MyAction()
{
return View("MyViewName",model); // Here model is your object of model class
}
In you snippet your are returning default i.e. 'AddToCart' view because you are not describing explicitly. Make your code like
return View("BasketViewModel",viewModel); // where BasketViewModel is your view name
You're returning that controller's View, if you wish to transfer to another view try
return BasketViewActionResult(viewmodel)
Then access your 'BasketViewActionResult'
Function BasketViewActionResult(model as BasketViewModel) as ActionResult
return View(model)
End Function
Sorry if you don't get VB, I can translate it to C# for you if you wish.
Edit:
You can also simply change the form's action.
#using (Html.BeginForm("BasketView","ShoppingBasket",...
and make all your manipulations within that actionresult
Problem is:
I am using a textbox to get a string q and want to pass it to 3 different actions in search controller. i.e. action1(string q), action2(string q) and so on
Now syntax of my action:
public ActionResult action1(string q)
{
var mydata = from p in fab //LINQ logic
select new action1class
{ data1=p //assignment };
return View("_partialAction1", mydata);
}
Similarly there are two other actions.
I am using 3 different actions because my LINQ logic gets data from 3 different sources so there different mydata needs to be created.
My problem is: I am trying that when I click on 'search' Button of textbox then all the 3 actions should run and generate partial view one below other in some <div id="action1"> tags.
I tried to use ajax.BeginForm but it can only call one action at a time
#using (Ajax.BeginForm("action1", "Search", new AjaxOptions
{
HttpMethod = "GET",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "action1",
LoadingElementId="progress"
}))
Also I tried to use ViewModel but the problem is that I was unable to pass a bigger model to the view along with these mydata kind of data obtained in LINQ's in the action. I have no clear idea of how to use viewmodel in this case.
Is the approach that I am using correct? Or can there be any other way? I want to show result of all actions with button click.
There are two types of actions are in MVC framework. The first ones are the main actions and they are invoked from the browser one at a time. The second type are called as Child Actions and these actions can't be invoked from the browser but from the views returned by the main actions. Multiple child actions can be called under a main action. So you have to look into child actions whether they help or not.
Ex.
// main action that returns a view
public ViewResult Index()
{
var model = ...
return View(model);
}
// couple of child actions each returns a partial view
// which will be called from the index view
[ChildActionOnly]
public PartialViewResult ChildAction1()
{
var model = ...
return PartialView(model);
}
[ChildActionOnly]
public PartialViewResult ChildAction2()
{
var model = ...
return PartialView(model);
}
// index view
Index.cshtml
#model ...
#Html.Action("ChildAction1");
#Html.Action("ChildAction2");
...
http://msdn.microsoft.com/en-us/library/ee839451.aspx
You can only have one action per request. If you want to have 3 different partial views for a singular click, you will need to construct a layout page that includes the 3 partial views how you want them and make sure that your action receives the proper parameters to perform all of the partial view rendering.
Why not pass the ViewModel to the partialViews. Make sure you have different properties in the ViewModel to hold the PartialView Specific data plus the search text. Here is an example:
Model
public class Product
{
public string Name { get; set; }
public string Type { get; set; }
public string Class { get; set; }
}
ViewModel
public class ProductSearch
{
public ProductSearch()
{
q = string.Empty;
Product1 = new Product();
Product2 = new Product();
}
public string q { get; set; }
public Product Product1 { get; set; }
public Product Product2 { get; set; }
}
_Partial1.cshtml
#model Test1.Models.ProductSearch
<div>Product1</div>
#Html.TextBoxFor(a => a.Product1.Name)
_Partial2.cshtml
#model Test1.Models.ProductSearch
<div>Product2</div>
#Html.TextBoxFor(a => a.Product2.Name)
ActualView.cshtml
#model Test1.Models.ProductSearch
#{
ViewBag.Title = "ActualView";
}
<h2>ActualView</h2>
#using (Html.BeginForm())
{
#:SearchText
#Html.TextBoxFor(m => m.q)
Html.RenderAction("_Partial1", Model);
Html.RenderAction("_Partial2", Model);
<input type="submit" runat="server" id="btnSubmit" />
}
Temp Data (you will be getting it from DB/ any other source)
private List<Product> ProductsToSearch()
{
return new List<Product>() { new Product() { Name = "Product One", Class = "A", Type = "High" }, new Product() { Name = "Product Two", Class = "A", Type = "Low" }, new Product() { Name = "Product Three", Class = "B", Type = "High" } };
}
Controller Actions
public ActionResult _Partial1(ProductSearch search)
{
Product Product1 = ProductsToSearch().Where(a => a.Class.Equals(search.q) && a.Type.Equals("High")).SingleOrDefault();
search.Product1 = Product1;
return PartialView(search);
}
public ActionResult _Partial2(ProductSearch search)
{
Product Product2 = ProductsToSearch().Where(a => a.Class.Equals(search.q) && a.Type.Equals("Low")).SingleOrDefault();
search.Product2 = Product2;
return PartialView(search);
}
[HttpPost]
public ActionResult ActualView(ProductSearch search)
{
return View(search);
}
public ActionResult ActualView()
{
ProductSearch search = new ProductSearch();
return View(search);
}
Now if you enter 'A' for SearchText and hit Submit Query you will get two different results (basically common search text is used and based on the search query in each partial view it has generated different results)