The app is designed to allow the user to enter a an IP address for a local machine and and it will then return the HDD information for that machine. It starts out with a default value already in the TextAreaFor box and performs the query for that value. This part works with no problem. But when a user tries to enter in their own value and hit the Refresh button, it keeps coming up with the error Object reference not set to an instance of an object.
I'm not sure why this is happening. It seems to me that clicking the button submits a POST action, which should kick off the second method in the controller. The current model is then passed to the controller with the values in the TextAreaFor attached and the mainCode() method is run on the new values.
Edit: According to What is a NullReferenceException, and how do I fix it? I am pretty sure that I am returning an empty model from my controller. I just don't see how. The form field should be sending the controller everything contained in TextAreaFor so the model should not be empty.
Edit2: I did some testing and the model is getting returned alright, but the values from TextAreaFor are not. When the mainCode() tries to do some logic to startDrives.startingDrives, it can't because that variable is empty for some reason.
Model:
namespace RelengAdmin.Models
{
public class DriveInfo
{
public class DriveHolder
{
public string startingDrives {get; set;}
}
public DriveHolder startDrives = new DriveHolder();
public void mainCode()
{
/****Code to return the HDD size omitted****/
}
}
}
View:
#using (Html.BeginForm())
{
<input type="submit" value="Refresh" />
#Html.TextAreaFor(model => model.startDrives.startingDrives, new {#class = "HDDTextBox"})
}
Controller:
namespace RelengAdmin.Controllers
{
public class HDDCheckerController : Controller
{
[HttpGet]
public ActionResult Index()
{
DriveInfo myDrive = new DriveInfo();
myDrive.startDrives.startingDrives = "148.136.148.53"
myDrive.mainCode();
return View(myDrive);
}
[HttpPost]
public ActionResult Index(DriveInfo model)
{
model.mainCode();
return View(model);
}
}
}
The issue is that your model's startDrives property is not actually declared as a property with getters and setters, so the model binder won't bind to it. I was able to duplicate the issue locally, and solve it by declaring the startDrives as a property and initializing it in the constructor.
public class DriveInfo
{
public class DriveHolder
{
public string startingDrives { get; set; }
}
public DriveHolder startDrives { get; set; }
public DriveInfo()
{
startDrives = new DriveHolder();
}
public void mainCode()
{
/****Code to return the HDD size omitted****/
}
}
Your question is a bit unclear of where the model is actually null.. but I would assume that when you hit your button, it goes to the correct action, but there is nothing in model because you haven't passed any specific values..
so try this:
CSHTML
#using (Html.BeginForm())
{
<input type="submit" value="Refresh" />
#Html.TextArea("startingDrive", "148.136.148.53", new {#class = "HDDTextBox"})
}
Controller
[HttpPost]
public ActionResult Index(string startingDrive)
{
DriveInfo searchThisDrive = new DriveInfo();
searchThisDrive.startDrives.startingDrives = startingDrive;
searchThisDrive.mainCode();
return View(searchThisDrive);
}
Let me know if this helps!
Related
Passing ViewModel to #Html.Partial
Have two ViewModels
public class RegisterVM
{
... some properties
public AddressVM AddressInformation { get; set; } //viewmodel
}
public class AddressVM {
public string Street1 { get; set; }
public string Street2 { get; set; }
public string PostalCode { get; set; }
}
When loading main view using VM:
#model ViewModels.RegisterVM
All field load. But When I add Partial View and pass viewmodel
#Html.Partial("_AddressDetails", Model.AddressInformation)
It fails
Error: Exception Details: System.NullReferenceException: Object reference not set to an instance of an object. Why does it fail?
The partial View _AddressDetails is expecting a
#model ViewModels.AddressVM
Update
Based on changes from Prashant,
When submitting the information The Address information is NULL.
In The controller:
[HttpPost]
public ActionResult Register(RegisterVM vm){
...
//when viewing vm.AddressInformation.Street1 is null. and there is a value
//Is there a different way of retrieving the values from partial view?
}
Thanks for reading.
The error is generated because property AddressInformation is null, and you need to initailize it in a parameterless constructor or in the controller before you pass it to the view, for example
public class RegisterVM
{
public RegisterVM() // default constructor
{
AddressInformation = new AddressVM();
}
public AddressVM AddressInformation { get; set; }
....
}
However you usage means that the controls generated will be
<input name="Street1" .../>
whereas they need to be
<input name="AddressInformation.Street1" .../>
in order to bind to your model. You can either make your partial an EditorTemplate (/Views/Shared/EditorTemplates/AddressVM.cshtml) and use in the main view as
#Html.EditorFor(m => m.AddressInformation)
or pass the prefix to the partial as additional ViewData
#Html.Partial("_AddressDetails", Model.AddressInformation, new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "AddressInformation" }})
This is working for me. You just need to instantiate your VM, attach it and send it to the view.
Page Action
public ActionResult Page(){
RegisterVM vm = new RegisterVM();
vm.AddressInformation = new AddressVM();
return View(vm);
}
Page.cshtml
#model Project.Web.Models.RegisterVM
<!-- loading partial view -->
#Html.Partial("_AddressDetails",Model.AddressInformation)
Partial View File
<input type="text" name="name" value=" " />
I am not having more info about code but as per mention details, can you try this
public ActionResult Register(){ return View(register); }
i know you may tried this but try to assigned explict value. as this is basic MVC implementation. if it not work out then you need to provide more code details.
hope this help.
in Register get Method must instatiate your viewModel because in view, call other partial with viewModel members(proprties);
public ActionResult Register(){
RegisterVM vm = new RegisterVM();
return View(vm);
}
I know there are a lot of similar question here but none seem quite the same as mine.
In my View:
#model LocalInformedMotionServer.Models.FeedData
#Html.DropDownList("Premise", Model.LoadUp("key"), new { #style = "width: 218px;height:35px;" })
In my controller:
public class CloudController : Controller
{
public IEnumerable<wsCommon.Premise> Premises { get; set; }
public ActionResult Feed(string key)
{
var feedData = new FeedData();
Premises= feedData.LoadUp(key);
return View(feedData);
}
}
In my Model:
public class FeedData
{
public IEnumerable<wsCommon.Premise> LoadUp(string saltKey)
{
Premises premises = new InformedBiz.Premises();
return premises.GetPremises(saltKey);
}
}
It errors because the variable:
"key"
in this call:
Model.LoadUp("key")
is being read in as'null' in my controller method.
Of course as this is all new to me I could be doing this all wrong..
ADDITIONAL:
In my CloudController Class I have this:
public class CloudController : Controller
{
public ActionResult Feed(string saltKey)
{
var feedData = new FeedData();
feedData.LoadUp(saltKey);
return View(feedData);
}
public ActionResult Index()
{
return View();
}
public ActionResult LogIn()
{
return View();
}
}
I'm not sure what your Premise class looks like, but I usually use an IEnumberable of SelectListItem for drop downs in my views. So you could do something like this:
public IEnumerable<SelectListItem> LoadUp(string saltKey)
{
Premises premises = new InformedBiz.Premises();
return premises.GetPremises(saltKey).Select(
p => new SelectListItem { Text = p.Name, Value = z.PremiseId.ToString() }
);
}
You'll also need to create a Post ActionResult method that accepts the model in your view (FeedData) as well as wrap your DropDownList control in a Html.BeginForm, to post results to the controller. Hope this makes a bit of sense.
You have not posted the properties of your FeedData model but assuming it contains a property which is typeof Premise and you want to be able to select a Premise from a collection, then using a view model that represents what you want to display/edit is the recommended approach (refer View Model Design And Use In Razor Views and What is ViewModel in MVC?)
You view model might look like
public class FeedDataVM
{
.....
[Display(Name = "Premise")]
[Required(ErrorMessage = "Please select a Premise")]
public int? SelectedPremise { get; set; }
....
public SelectList PremiseList { get; set; }
}
and in your controller (not sure what saltKey is for?)
public ActionResult Feed(string saltKey)
{
FeedDataVM model = new FeedDataVM();
IEnumerable<Premise> premises = // get the collection of premise objects from your repository
// assuming you want to display the name property of premise, but post back the key property
model.PremiseList = new SelectList(premises, "key", "name");
return View(model);
}
View
#model FeedDataVM
#using(Html.BeginForm())
{
....
#Html.LabelFor(m => m.SelectedPremise)
#Html.DropDownListFor(m => m.SelectedPremise, Model.PremiseList, "-Please select")
#Html.ValidationMessageFor(m => m.SelectedPremise)
....
<input type="submit" value="Save" />
}
and the POST method
public ActionResult Feed(FeedDataVM model)
{
// model.SelectedPremise contains the value of the selected option as defined by the key property of Premise
}
Side note: Your FeedData model contains a method to retrieve a collection of Premise which appears to be calling another service. You should avoid this type of design which makes it difficult to debug and unit test. Your controller is responsible for initializing/getting you data models and view models and for populating/mapping their properties.
I've got an action on my controller that looks like this:
[HttpPost]
public ActionResult Edit(EditMyObjectViewModel editMyObjectViewModel)
{
}
EditMyActionViewModel contains a MyObject
This is passed in to the Edit view (the GET version of the above controller action)
When it is posted back in, the ID isn't set....
If I change the controller to be:
[HttpPost]
public ActionResult Edit(Guid id, EditMyObjectViewModel editMyObjectViewModel)
{
editMyObjectViewModel.ID = id;
}
That works, but it seems a little wrong?
I guess I could also bind a hidden field on the view to Model.ID?
What's the convention here?
EDIT
Model / ViewModels are as follows:
public class EditMyObjectViewModel
{
public IEnumerable<SomeItem> SomeUnrelatedStuff { get; set; }
public MyObject MyObject { get; set; }
}
public class MyObject
{
public guid ID { get; set; }
public string Name { get; set; }
}
View is as follows:
#model MyApp.Models.EditMyObjectViewModel
#using (Html.BeginForm("Edit", "License", FormMethod.Post, new { #class = "form form-horizontal" }))
{
#Html.TextboxFor(x=>x.MyObject.Name);
<input type="submit" class="btn btn-success" value="Create Modification" />
}
You are right, you either need to create a hidden field to hold the id or you need to add the ID as a parameter to the action method.
The issue is simply where the id comes from and how it gets populated. The default model binder populates the model from form fields; it wont' use a query parameter. And the query parameter won't go into the model but it will get populated in an argument on the action method.
The one other thing you could do is to create a custom model binder to populate the query parameter into the model. But that feels like overkill in this situation. Plus, you'd have to do this for each page/model.
It it was me, I'd add the id to the action method.
its me... yet again!
Ive got these class,
public class PrankTargetArgumentViewModel
{
public PrankTarget Target { get; set; }
public PrankDefinition Prank { get; set; }
public List<PrankArgument> Arguments { get; set; }
}
public class PrankArgument
{
public string Name { get; set; }
public string Value { get; set; }
}
and what I'm doing is - if this current ParkDefinition needs arguments them im doing an ActionRedirect on the save to another Action which should handle the gathering of the Arguments
My Action result is like this..
public ActionResult PrankArguments()
{
PrankInstance currentInstance = SessionContext.CurrentPrankInstance;
if (currentInstance == null)
throw new ArgumentNullException("currentInstance");
PrankTargetArgumentViewModel model = new PrankTargetArgumentViewModel();
model.Prank = currentInstance.Prank;
model.Target = currentInstance.Target;
string[] args = model.Prank.Arguments.Split('|');
model.Arguments = new List<PrankArgument>();
foreach (string s in args)
{
model.Arguments.Add(new PrankArgument { Name = s, Value = s });
}
return View(model);
}
my http post method is just an empty method with the parameter of PrankTargetArgumentViewModel
[HttpPost]
public ActionResult PrankArguments(PrankTargetArgumentViewModel model)
{
return View();
}
My HTML is like this..
#using (Html.BeginForm())
{
#Html.EditorFor(x => Model)
<p>
<input type="submit" value="Create" />
</p>
}
So my problem is this, on the PrankArguments(PrankTargetArgumentViewModel model) post back action, the model param is always null.. I've filled the object with values on the load so I guessed they would be there on the post back with the new arguments that I added.
so the flow goes like this.
Create Prank
If prank needs arguments then load ActionResult PrankArguments()
Add extra arguments to an already poplulated object.
save, Call ActionResult PrankArguments(PrankTargetArgumentViewModel model)
-- this is where the problem is, the model parameter is passed back as null.
Ive had this problem quite a few times and always just given up but im not going to let that happen this time!
any help would be great! cheers, Ste!
Ps. If you need anymore of my code just let me know.
EDIT - Removed view bag debug properties!
I think if I understand you correctly if your view is strongly typed to PrankTargetArgumentViewModel then all you have to do to retrieve the values is:
[HttpPost]
public ActionResult PrankArguments()
{
var pta = new PrankTargetArgumentViewModel();
TryUpdateModel(pta);
}
After reviewing my own code - I noticed that I didn't need the entire PrankTargetArgumentViewModel and a simple List of Arguments would have been fine.
I alterd my PrankArguments view to take an IEnumerable and used;
#using (Html.BeginForm())
{
#Html.EditorForModel()
<p>
<input type="submit" value="Finish" />
</p>
}
then had my post back method signature like this
[HttpPost]
public ActionResult PrankArguments(IEnumerable<PrankArgument> arguments)
which worked exactly how I wanted.
Thanks for all the suggestions guys.
This is what my data model classes look like:
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Position Position { get; set; }
}
public class Position
{
public string Title { get; set; }
}
I have a Create view where I want to have two text boxes for first name and last name, and then a dropdown box that has the position title. I tried doing it this way:
View (only the relevant part):
<p>
<label for="Position">Position:</label>
<%= Html.DropDownList("Positions") %>
</p>
Controller:
//
// GET: /Employees/Create
public ActionResult Create()
{
ViewData["Positions"] = new SelectList(from p in _positions.GetAllPositions() select p.Title);
return View();
}
//
// POST: /Employees/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Employee employeeToAdd)
{
try
{
employeeToAdd.Position = new Position {Title = (string)ViewData["Positions"]};
_employees.AddEmployee(employeeToAdd);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
However, when I click submit, I get this exception:
System.InvalidOperationException was unhandled by user code
Message="There is no ViewData item of type 'IEnumerable<SelectListItem>' that has the key 'Positions'."
I'm pretty sure I'm doing this wrong. What is the correct way of populating the dropdown box?
You can store:
(string)ViewData["Positions"]};
in a hiddn tag on the page then call it like this
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Employee employeeToAdd, string Positions)
{
In the Create() (WITH POST ATTRIBUTE) employee since the ViewData["Positions"] is not set you are getting this error. This value should form part of your post request and on rebinding after post should fetch it from store or get it from session/cache if you need to rebind this..
Remember ViewData is only available for the current request, so for post request ViewData["Positions"] is not yet created and hence this exception.
You can do one quick test... override the OnActionExecuting method of the controller and put the logic to fetch positions there so that its always availlable. This should be done for data that is required for each action... This is only for test purpose in this case...
// add the required namespace for the model...
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// add your logic to populate positions here...
ViewData["Positions"] = new SelectList(from p in _positions.GetAllPositions() select p.Title);
}
There may be other clean solutions to this as well probably using a custom model binder...
I believe that ViewData is for passing information to your View, but it doesn't work in reverse. That is, ViewData won't be set from Request.Form. I think you might want to change your code as follows:
// change following
employeeToAdd.Position = new Position {Title = (string)ViewData["Positions"]};
// to this?
employeeToAdd.Position = new Position {Title = (string)Request.Form["Positions"]};
Good luck!