MVC presistent Model - c#

In a MVC 3 application i need to be able to part creation view in 2 different parts, because of some folder creation logic.On first view i have a textbox with Project Name, on click of next button i need to create folder for that project, and i need a way to get that project name folder on my next view in some way. Right now i press next and i loose project Name, and i cant get it again.
On internet there are plenty of places where they implement it the same way, but i cant figure out how to do that.
Any solutions on how to presist model?

You could use a hidden field in the second view that will contain the project name that was entered in the first view. That way when you submit this second form you will get the project name and the project folder.
Another possibility is to store the value that was entered in the first view somewhere on the server (database, session, ...)
UPDATE:
As request in the comments section here's an example using a hidden field.
First view:
#model FirstStepViewModel
#using (Html.BeginForm())
{
#Html.EditorFor(x => x.ProjectName)
<button type="submit">OK</button>
}
then a controller action to which this first step will be submitted:
[HttpPost]
public ActionResult Foo(FirstStepViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
return RedirectToAction("Bar", new { projectname = model.ProjectName });
}
and then you will have a second controller action that will serve the second view:
public ActionResult Bar(FirstStepViewModel firstStep)
{
var model = new SecondStepViewModel
{
ProjectName = firstStep.ProjectName
};
return View(model);
}
you will then have a corresponding view:
#model SecondStepViewModel
#using (Html.BeginForm())
{
#Html.HiddenFor(x => x.ProjectName)
#Html.EditorFor(x => x.ProjectFolder)
<button type="submit">OK</button>
}
which will post to the final action:
[HttpPost]
public ActionResult Bar(SecondStepViewModel model)
{
// here you will get both model.ProjectName and model.ProjectFolder
...
}

You could also load all parts of the form at once, and then use a tabbed layout (e.g., jQuery UI Tabs) and submit the entire form at the last step.

Related

Asp.net MVC & AJAX

I have an Ajax button that whenever I click it, it shows a single record from the database (in my Controller I used .Take(1) )
public PartialViewResult BtnNext()
{
List<Queue> model = db.Queues.OrderBy(x => x.QueueNumber).Take(1).ToList();
return PartialView("_queuenumber", model);
}
What I would like to do here is - whenever I click the next button it will display the first record from the database, then when I click it again it will show the second record and so on..
I wonder if that is even possible and what kind of stuff should I use to do that?
Yes. Its possible.
Just set Application["counter"] = 0 in Application_Start function then make value increments by 1 in result view and use it to get next record.
public PartialViewResult BtnNext()
{
List<Queue> model = db.Queues.OrderBy(x => x.QueueNumber).Skip(Application["counter"]).Take(1).ToList();
Application["counter"] = Application["counter"] + 1;
return PartialView("_queuenumber", model);
}
Reference
Use FormCollection try following code.
public PartialViewResult BtnNext(FormCollection Form)
{
Int32? Count = Convert.ToInt32(Form["Count"]??0);
List<Queue> model = db.Queues.OrderBy(x => x.QueueNumber).ToList();
model.ElementAt(count); // [NotMapped] public Int32? count { get; set; } add in model class
model.count=count+1;
return PartialView("_queuenumber", model);
}
on view
<input type="submit" class="btn btn-primary" value="Save" id="BtnNext">
<input type="hidden" id="Count" name="Count" value="#Model.Count" />
A good practice when you realize your Views need to handle and manipulate your data, is to create a ViewModel class that wraps all the objects that you need to send to that view. In your case, you can start with a simple
public class QueueViewModel
{
public Queue Queue { get; set ; }
public int CurrentRecord { get; set ; }
}
Now, all you have to do is changing the action method the controller so that you initialize and pass the ViewModel to the View. It will also be better to have an optional argument acting as the default record, and then using the linq instruction Skip to go to and take a specific record:
Public PartialViewResult NextRecord(int current = 0)
{
QueueViewModel model = new QueueViewModel();
model.CurrentRecord = current;
model.Queue = db.OrderBy(x => yourClause).Skip(current).Take(1);
return PartialView(“yourview”, model);
}
I changed the List<Queue> within your model as I think you don’t need a list if you’re only showing one record at a time, but you can easily go back to the generics if you feel you really need to.
As for the view part where you handle the index on the model, there are many ways to achieve the same result. What I personally like to do is using the model to fill a data attribute of a DOM element and use that in the Ajax call. Since you now have
#model yourModelNamespace.QueueViewModel
it is possible for you to set an element (let’s say a button) to host the current value:
<button data-current-record=“#Model.CurrentRecord”>...</button>
You can now very easily retrieve that value within your Ajax call to the action method:
var currentRecord = parseInt($(‘button’).data()[currentRecord]);
$.ajax({
url: yourPathToTheAction,
type: ‘GET’,
data: {
current: currentRecord + 1
}
});
This way you can go further and add other functions calling the same controller to move to previous record or jump to the last or the first and so on...

How to create server method, that populates collection and returns partial view, in ASP.NET MVC?

I am trying to call IEnumerable method in my _Layout.cshtml file. At the final I was adviced to "use html.action - to call server method that populates collection and returns partial view".
Currently I have created partial file _Dodatki.cshtml, that contains call of IEnumerable method (Aktualnosci.cs is model file):
#model IEnumerable<DluzynaSzkola.Models.Aktualnosci>
In my _Layout.cshtml I called method from my constructor with:
#Html.Action("_Dodatki", "AktualnosciController ", new {area="" })
And at the final I want to create method in my AktualnosciConstructor.cs file. Currenly I have method:
[ChildActionOnly]
[ActionName("_Dodatki")]
public ActionResult Dodatki()
{
IList<Aktualnosci> lista = new IList<Aktualnosci>();
return PartialView("_Dodatki", lista);
}
Unfortunately, when using syntax as above, it gives me message in compiler:
"cannot create an instance of the abstract class or interface
'IList'".
When replacing 'IList' with 'List', it gives me exception:
"System.Web.HttpException: The controller for path '/' was not found
or does not implement IController."
I have no idea how in other way I can populate collection in the method.
edit: As per request, below AktualnosciController.cs definition, with no other methods:
namespace DluzynaSzkola.Controllers
{
public class AktualnosciController : Controller
{
//here are other methods
[ChildActionOnly]
[ActionName("_Dodatki")]
public ActionResult Dodatki()
{
IList<Aktualnosci> lista = new IList<Aktualnosci>();
return PartialView("_Dodatki", lista);
}
}
}
as noticed by GTown-Coder your controller name seems wrong. Updated my answer accordingly.
I think that your problem might be the same as answered by this SO post.
try specifying the Area name and, if this controller is not in an area simply add an empty area name.
#Html.Action("_Dodatki", "AktualnosciController ", new {area="" })
Even if this does not solve your problem it is good practice because if this view is latter used within an area it will try to find the controller in the area and not in the root space.
Allright, I have implemented changes to my project, that works fine.
My in _Layout.cshtml call is changed a bit. AktualnosciController supposed to be called just Aktualnosci !!!
<div class="kontenerDodatkiLayout hidden-xs hidden-sm">
<div class="archiwum">Archiwum</div>
#Html.Action("_Dodatki", "Aktualnosci", new { area = "" })
</div>
My partial view _Dodatki.cshtml model call is changed a bit:
#model IEnumerable<DateTime>
<div class="wpisDodatki">
#foreach (var item in Model)
{
<div> #Html.DisplayFor(modelItem => item)</div>
}
<p>test<br />test<br />test</p>
</div>
And method in my controller AktualnosciController.cs looks like that:
//[ChildActionOnly]
[ActionName("_Dodatki")]
public ActionResult Dodatki()
{
using (var context = new DluzynaContext())
{
var lista = context.Indeks.Select(it => it.Dzien).ToList();
return PartialView("_Dodatki", lista);
}
}
in here lista is passed to my partial view _Dodatki, and it is populated with context property Indeks and model property Dzien.
Thanks guys for your help #Wndrr , #GTown-Coder.

ASP.Net MVC Refresh Page without destroying ViewModel

I want to create a multilingual webpage. To switch between languages I've got a dropdown on my page. If the change event of the dropdown gets fired the Method called "ChangeLanguage" in my Controller is called.
public ViewModels.HomeViewModel HVM { get; private set; }
// GET: Home
public ActionResult Index()
{
this.HVM = new ViewModels.HomeViewModel();
return View(this.HVM);
}
public JsonResult ChangeLanguage(int id) {
return Json(new {Success = true});
}
Now I'd like to to change my "SelectedLanguage" Property in my ViewModel (HVM) - but the Reference is null. May anyone explain why HVM is null in my ChangeLanguage Method?
After my SelectedLanguage Property is changed I'd like to reload my whole page to display it's texts in another language
e.g.
#model ViewModels.HomeViewModel
<html>
<div class="HeaderText">
Text = #{
#Model.TextToDisplay.Where(o =>
o.Language.Equals(Model.SelectedLanguage)).First()
}
</div>
Here's what I want to do in PseudoCode:
PseudoCode:
public JsonResult ChangeLanguage(int id) {
this.HVM.SelectedLanguage =
this.HVM.AvailableLanguages.Where(o =>
o.ID.Equals(id)).First();
Page.Reload();
return Json(new {Success = true});
}
May anyone explain why HVM is null in my ChangeLanguage Method?
Adhering to stateless nature of HTTP protocol, all (unless explicitly added into request header) requests (MVC method calls) loose state data associated with it. Web server treats every request a new request and creates new instances of classes right from controller itself.
In your case since it is a new request, controller has a HVM property defined but in ChangeLanguage it is not instantiated (it gets instantiated only into Index method which is not called when you invoke ChangeLanguage) hence it is null.
After my SelectedLanguage Property is changed I'd like to reload my
whole page to display it's texts in another language.
Option 1: Refresh page
Simple option to implement. Pass the language selection to server, server will return a new view with specific data. Drawback, whole page will refresh.
Option 2: Update view selectively
If option 1 is really not acceptable, then consider this option. There are multiple ways you can achieve it. Basically it involves either (a) breaking you view into partial view and update only the portion that is affect by selection or (b) bind data element with a JS object.
(a) - Not much need to be said for this.
(b) - Data binding can easily be done if you employ a JS library like KnockoutJS.
Change your methods to these methods , This trick will work for you =>pass your model to Change language from view. Also update JsonResult to ActionResult.
public ActionResult ChangeLanguage(ViewModels.HomeViewModel model,int id)
{
this.HVM.SelectedLanguage =
this.HVM.AvailableLanguages.Where(o =>
o.ID.Equals(id)).First();
return RedirectToAction("Index",model);
}
public ActionResult Index(ViewModels.HomeViewModel model)
{
if(model == null)
{
this.HVM = new ViewModels.HomeViewModel();
}
return View(this.HVM);
}

NullReferenceException MVC4 C# Most likely beginner fail

I have been following
http://pluralsight.com/training/Courses/TableOfContents/mvc4-building
to learn some MVC C# for my Company, btw completely amazing Video.
I am populating a View with a SQL source.
In Debug I can definitely tell all my connections work, and I get to my foreach loop that should display all the data in that table
On my #Foreach( var item in Model ) it throws the NullRefException on my Model... here's the code I have
this is my complete view
#model IEnumerable<OilNGasWeb.ModelData.Clients>
#{
ViewBag.Title = "CLS-Group";
}
#foreach(var item in Model)
{
<div>
<h4>#item.Client</h4>
<div>#item.Address</div>
<div>#item.City</div>
<div>#item.State</div>
<div>#item.Zip</div>
<div>#item.ContactName</div>
<div>#item.ContactEmail</div>
<div>#item.County</div>
<div>#item.Authorized</div>
<hr />
</div>
}
So I'm thinking it is instantiated here
#model IEnumerable<OilNGasWeb.ModelData.Clients>
but just incase I was wrong maybe it's instantiated in the Home controller in the Index Action?
public ActionResult Index()
{
var Model = _db.Clients.ToList();
return View();
}
Please help me figure out why it's throwing this exception thanks. I wouldn't think you needed more code. but if you do let me know what M , V , C to post for you, as said above the data part works great.
public ActionResult Index()
{
var model = _db.Clients.ToList();
return View(model);
}
You need to pass the model to the view, otherwise it will be null.

.NET MVC 4 - Multiple "actions" on same Controller, how?

I'm pulling my hair out over this one and I'm looking for guidance before I start fudging together my own approach.
Here's what I've got:
View snippet
<td>#Html.ActionLink("More Details", "Index", new { id = product.ProductId })</td>
<td>#Html.ActionLink("Compare", "Compare", new { id = product.ProductId, compare = true })</td>
Controller snippet
public ActionResult Index(FormCollection values)
{
// Does stuff, works
}
public ActionResult Index(int productId)
{
// Does stuff, works
}
Now, here lies my problem. The Index functions are both taken now, from the POST to the form, and the "More Details" ActionLink being clicked. This works fine.
Now I want "Compare" to be functional, in which I want on the same page and will hold a list of compared products, which is fine. But how do I get that Compare functionality on the same View/Page?!
I've tried:
public ActionResult Compare(int productId)
{
}
But obviously that doesn't work as it requires a Compare.cshtml, which I don't want to happen. I want it to be modify my ViewModel and return it with newly Compared products, so I'd be able to do this from my original View:
#foreach(var products in Model.ComparedProducts)
The only way I can see me doing this is "fudging it" to have:
public ActionResult Index(int productId = 0, bool compare)
{
}
Which could become unruly with lots of functionality on the same page.
Surely there's something obvious I'm missing here?
Oh, and the AjaxLink options isn't right for me, as this is part of the site that has to work via postbacks (Progress Enhancement and all that jazz).
I think you return an ActionResult by calling return View(model), is that right? Without naming a view explicitly, the MVC resolving mechanism looks for views with the same name as the action, in your case "Compare.cshtml".
If you change your call to return View("Index", model) you will be using the Index.cshtml view regardless of the action name.
Is that what you were looking for?
An action in MVC does not require a corresponding View. It can return any view by supplying a name parameter to the View() function - see http://msdn.microsoft.com/en-us/library/system.web.mvc.controller.view%28v=vs.98%29.aspx for details.
You can in your compare function do all the logic required and redirect back to the index action or any other that display the page as needed using RedirectToAction().

Categories