Pass model into template from Surface Controller - c#

Is there a way I can pass in a populated object or list into a template / view from a surface controller in Umbraco 7?

Sounds like a good spot to use ViewData. There isn't much info in the question so I am kind of winging it here, but something like this...
// your surface controller
public ActionResult SubmitForm(YourFormModel model)
{
if (ModelState.IsValid) {
var submitResults = ResultsOfYourCode();
ViewData["submitResults"] = submitResults;
return RedirectToCurrentUmbracoPage();
}
}
// your view
#{
var submitResults = (submitResultsType)#ViewData["submitResults"];
}
<p>#submitResults.Property1</p>
This is totally untested and is just off the top of my head to give you an idea.

Related

Update multiple entities and return a ViewModel?

I’m confused on how to work with updating two entities while using a ViewModel. I’ve done some searching but I’m not coming up with anything that seems to help me understand this.
I have two entities Person and Address. I use a ViewModel to combine the two together in order to display them in my Edit.cshtml. At the top of my View I declare the ViewModel to be used.
Now, I get to the actual Edit portion. I’m assuming I would have to make an update to each entity and then create a new ViewModel to return to the View since the Veiw is expecting a ViewModel?
I’m also curious if there is a way to let the View know that the update was successful so I can display a message at the top of the View or if it would be better to just redirect to the Index View.
Is this a correct way of coding this or is there a more streamlined way to accomplish the same thing?
Top of my .cshtml page
#model Project.Models.MemberViewModel
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult MyAccount(MemberViewModel model)
{
if (model.PersonId == 0)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
MemberViewModel updatedVM = new MemberViewModel();
using (var db = new DataContext())
{
Person currentPerson = db.Person.Find(model.PersonId);
if (currentPerson == null)
{
return HttpNotFound();
}
db.Entry(currentPerson).State = System.Data.Entity.EntityState.Modified;
db.SaveChanges();
Address currentAddress = db.Address.Find(model.PersonId);
if (currentAddress == null)
{
return HttpNotFound();
}
db.Entry(currentAddress).State = System.Data.Entity.EntityState.Modified;
db.SaveChanges();
////update and return a ViewModel
//updatedVM.FirstName = currentPerson.FirstName;
//updatedVM.LastName = currentPerson.LastName;
//updatedVM.Address = currentAddress.Address1;
//updatedVM.City = currentAddress.City;
//updatedVM.State = currentAddress.State;
//updatedVM.Zip = currentAddress.Zip;
}
return View(updatedVM);
}
In my opinion, the best practice would be to redirect to Index.cshtml after successfully saving the data (I think this is the whole idea of keeping MVC clean):
return RedirectToAction("Index");
I would only show an error message if there is trouble with the data.This can be done similar to this:
You can add these to the ViewModel:
bool hasErrors;
string errorMessage;
and in the view you would implement at the top of the page:
#if(Model.hasErrors)
{
<div>Model.errorMessage</div>
}
The idea is to validate the data inside the ViewModel constructor, or inside the controller, and if there is something wrong just set hasErrors = true and a custom errorMessage and show it in the view.cshtml.
Hope it helps.

Redirect from view to different view in asp.net mvc

I have a problem transfering data from one view to another via the controler actions.
I the first view is a grid.mvc-Grid displayed. By select on row of the grid I get the ID for that object.
by transfering this to an action in the controler I try to filter the data. That works fine.
Here is the filter:
[HttpGet]
public ActionResult PersonenById(int id)
{
var personen = new ObservableCollection<Person>();
//Getting the data here :-)
foreach (DataRow r in access.Rows)
{
Person p = new Person();
//do some stuff
personen.Add(p);
}
//return PartialView("Personen", personen); //does not work
TempData["personen"] = personen;
return RedirectToAction("Personen"); // redirect to another view
}
In method II the view is filled:
public ActionResult Personen()
{
var persons = new ObservableCollection<Person>();
if (TempData["Persons"] == null)
{
}
return View(persons); //Works perfect
}
else
{
persons = (ObservableCollection<Person>) TempData["Persons"];
return View(persons);//does not redirect to that View
}
}
(Sorry for the strange formating. :-))
Is there any different way to send data from a view to another?
I tried:
return partial;
return View("Persons",persons);
and a lot other stuff.
You can redirect in a .cshtml view.
Eg:
Context.Response.StatusCode = 403;
Context.Response.Redirect(
$"{Context.Request.PathBase}/Error/403", false);
Should work like this:
return RedirectToAction("Personen", model);
Also, the "Personen" action should have the model as an argument, like this:
public ActionResult Personen(Person model) ...
LE: I have also noticed you have tried to send the data through the TempData object. Make sure the indexed object's name is the same (e.g. TempData["person"] everywhere)
Hope it answers your question.

ASP.Net MVC Viewmodels How to map values from a viewmodel to the database tables

I'm trying to use ViewModels and AutoMapper, as I know these are best practice for avoiding lots of issues.
I'm ok using AutoMapper to populate a viewmodel, but I'm not as sure about how to update my database, from the ViewModel being posted back to my controller.
My GET is:
public ActionResult Edit(int id = 0)
{
Customer customer = db.Customers.Find(id);
var offers = db.Offers.Where(x => x.CustomerId == id).ToList();
var email = db.Emails.FirstOrDefault();
var vm = new CreateViewModel();
vm.CustomerId = customer.CustomerId;
vm.ArrivalDate = customer.ArrivalDate;
vm.CustomerName = customer.CustomerName;
vm.EmailAddress = customer.EmailAddress;
vm.NumNights = customer.NumNights;
vm.NumPeople = customer.NumPeople;
vm.EmailBody = email.EmailBody;
vm.From = email.From;
vm.Subject = email.Subject;
// Map list of Offers into ViewModel
vm.Offers = Mapper.Map<IList<Offer>, IList<OfferVM>>(offers);
return View(vm);
}
My POST is:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(EditViewModel vme)
{
if (ModelState.IsValid)
{
// Update the Customer properties
Customer customer = db.Customers.Find(vme.CustomerId);
customer.NumPeople = vme.NumPeople;
customer.NumNights = vme.NumNights;
customer.ArrivalDate = vme.ArrivalDate;
customer.CustomerName = vme.CustomerName;
customer.EmailAddress = vme.EmailAddress;
// Update Offers table
foreach (var o in vme.Offers)
{
// find the offer
Offer offer = db.Offers.Find(o.OfferId);
if (offer != null)
{
// update the properties of Offer
offer.RoomRate = o.RoomRate;
offer.IncludeInOffer = o.IncludeInOffer;
}
}
db.SaveChanges();
return RedirectToAction("Index");
}
return View(vme);
}
So my Post is manually updating two database tables (Offers and Customers).
The GET method is elegant, using AutoMapper, the POST is not. I'm wondering if there is a more straightforward way of updating the database via AutoMapper, without having to manually go through each property I am looking to update? Or is my POST controller as efficient as it can be?
Thank you for any pointers,
Mark
There is an overload of Map that allows to map properties to one preexisting instance:
Mapper.Map<IObjectA, IObjectB>(objectA, ObjectB);
Thus, you just have to map from the parameter vme (CreateViewModel vme) to the customer recovered from the DB. This also applies to the offers part.
Of course, you'll have to configure the mappings in the reverse direction: you've done it from db objects to view model, and now you need to map them from viewmodel to db objects. If you've followed Automapper conventions it will be pretty easy or even unnnecessary.
EDIT: added interesting comment by Henk Mollema
You can configure AutoMapper to ignore properties, if your ViewModel doesn't contain all the properties from your domain model (which is most likely the case), it won't overwrite them with nulls.
You first need to define your mapping like this:
Mapper.CreateMap<EditViewModel, Customer>();
Your updated action method could look like this. What happens here is your action method receives your view model and checks its validity. If it is valid then it does the mapping from your view model to your domain model. This domain model is then passed to your service or repository layer to update the cusotmer in the database. How you update the record depends entirely on you.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(EditViewModel viewModel)
{
if (!ModelState.IsValid)
{
return View(viewModel);
}
Customer customer = Mapper.Map<Customer>(viewModel);
customerService.Edit(customer);
return RedirectToAction("List");
}
I hope this can help guide you in the right direction. Your HttpGet action method will work in a similar way.

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