calling function on razor view - razor

I'm trying to do something very simple here. Apologies new to MVC and Razor
This is on the Razor View Page (.cshtml).
Why is the ShowMessage function is not firing on the button click
Also would the code for the functions ideally be placed here or in the cshtml.cs file?
I managed to call a method in the cshtml.cs file but I couldnt display an alert box to inform the end user about a success or a failure so now I'm trying this way with ViewBag.
#page
#model IndexModel
#{
ViewData["Title"] = "Air Menu page";
#functions
{
public void ShowMessage()
{
string output = "Hello";
ViewBag.Message(output);
}
}
}
<form asp-page-handler="showmessage" method="post">
<button class="btn btn-default">Show Message</button>
</form>

The answer was to use ViewData or possibly ViewBag
public void OnGetStartKodi()
{
int exitcode = 0;
if (exitcode == 1)
{
ViewData["output"] = exitcode;
return Page();
}
else ViewData["output"] = "failed try again exitcode:" + exitcode;
return Page();
}
AND
fetch from Razor page using
#if (ViewData.ContainsKey("output"))
{
<script>alert("#ViewData["output"]")</script>
}

Related

How to handle form post from View Component (Razor Core 2)

This weekend a lot of struggle with a View Component.
I try to add a dropdownlist that does an auto postback onchange. This dropdownlist is on a view component.
I have 2 problems:
I don't get the asp-page-handler after the post, does it work like I implemented it on the form-tag?
Post calls method public void OnPost on razor page containing view
component. I would think it would be better to have a method on the
View Component like OnChangeProject?
The code of my View (View Component):
<form asp-page-handler="ChangeProject" method="post">
#Html.AntiForgeryToken()
#Html.DropDownList("id", new SelectList(Model, "Id", "Id"), new { onchange = "this.form.submit()" })
</form>
Thanks in advance!!
I exprienced the same problem and the way i fixed it is already answered in your question.
The form call is made at the page where you got your View Component embedded. I don't think it would be even possible to call a handler in your View Component with asp-page-handler as this is Razor Pages tag helper.
The way i got it work is simply putting the page-handler method on the PageModel that is embedding the View Component. In your case you can simply implement this handler on your Razor Page:
public IActionResult OnPostChangeProject()
{
// ... do Something
}
I don't know though how it would work to trigger a controller method in your View Component. Possibly create a new Controller class and route to it with asp-controller and asp-action in your form tag.
You should remember that the Page handlers could be viewed as convenience methods.
All the ASP.Net Core framework does is looks at the Query string parameters and Form data and translates it into Page handler calls.
And even though the Handlers are not available in View Components or Partial Views you still can get access to all the required ingredients by injecting IHttpContextAccessor into the View.
It will provide you with HttpContext.Request which contains both the Query and the Form properties.
You can then create your own Handler mapper. Here is one, for example:
public class HandlerMapping
{
public string Name { get; set; }
public System.Delegate RunDelegate { get; set; }
public HandlerMapping(string name, Delegate runDelegate)
{
RunDelegate = runDelegate;
Name = name;
}
}
public class PartialHandlerMapper
{
IHttpContextAccessor _contextAccessor;
public PartialHandlerMapper(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
public void RouteHandler(List<HandlerMapping> handlerMappings, string PartialDescriminatorString = null)
{
var handlerName = _contextAccessor.HttpContext.Request.Query["handler"];
var handlerMapping = handlerMappings.FirstOrDefault(x => x.Name == handlerName);
if (handlerMapping != null)
{
IFormCollection form;
try
{
form = _contextAccessor.HttpContext.Request.Form;
}
catch
{
return;
}
if (!string.IsNullOrWhiteSpace(PartialDescriminatorString) && form[nameof(PartialDescriminatorString)] != PartialDescriminatorString)
return;
List<Object> handlerArgs = new List<object>();
var prmtrs = handlerMapping.RunDelegate.Method.GetParameters();
foreach (var p in prmtrs)
{
object nv = null;
var formValue = form[p.Name];
if (!StringValues.IsNullOrEmpty(formValue))
{
try
{
nv = TypeDescriptor.GetConverter(p.ParameterType).ConvertFromString(formValue);
}
catch (FormatException)
{
//throw new FormatException($"Could not cast form value '{formValue}' to parameter {p.Name} (type {p.ParameterType}) of handler {handlerName}. Make sure you use correct type parameter. ");
nv = Activator.CreateInstance(p.ParameterType);
}
catch (ArgumentException)
{
nv = Activator.CreateInstance(p.ParameterType);
}
}
else
nv = Activator.CreateInstance(p.ParameterType);
handlerArgs.Add(nv);
}
handlerMapping.RunDelegate.DynamicInvoke(handlerArgs.ToArray());
}
}
}
And inject it into the service container:
services.AddScoped<PartialHandlerMapper>();
And here is a shopping cart partial view code section example:
#inject ShoppingManager shoppingManager
#inject PartialHandlerMapper partialHandlerMappping
#{
string ToggleCartItemTrialUseHandler = nameof(ToggleCartItemTrialUseHandler);
string DeleteCartItemHandler = nameof(DeleteCartItemHandler);
List<HandlerMapping> handlerMappings = new List<HandlerMapping> {
new HandlerMapping (ToggleCartItemTrialUseHandler, (Guid? PicID, bool? CurrentValue) => {
if (PicID == null || CurrentValue == null)
return;
shoppingManager.UpdateTrial((Guid)PicID, !(bool)CurrentValue);
}),
new HandlerMapping (DeleteCartItemHandler, (Guid? PicID) => {
if (PicID == null)
return;
shoppingManager.RemoveProductFromCart((Guid)PicID);
})
};
partialHandlerMappping.RouteHandler(handlerMappings);
var cart = shoppingManager.GetSessionCart();
}
Form element example from the same view:
<td align="center" valign="middle">
<form asp-page-handler="#DeleteCartItemHandler">
<input name=PicID type="hidden" value="#i.PicID" />
<button>
Delete
</button>
</form>
</td>
Where #i is an Item in the shopping cart
It's possible to create a combo (Controller/ViewComponent) by decorating the controller with a ViewComponent(Name="myviewcomponent").
Then create the invokeasync as usual, but because the controller doesn't inherit from a ViewComponent, the return result would be one of the ViewComponent result (ViewViewComponentResult, et).
The form in the viewcomponent can then have a button with asp-controller/action tag helpers targetting the controller/action.

MVC5 Post data from partial view being used in main view

I have already tried many solutions available on web as per my understanding in context to this question but being new in MVC I am still unable to find a solution. Kindly help.
I have a view which is the home page of the website named as "Index.cshtml" and is situated under the following path:
WebsiteName/Areas/Website/Views/CypressHome/Index.cshtml
Secondly, I have a created a user trial form as partial view named as "_PartialHomeFormFreeTrial.cshtml" which is situated under the following path:
WebsiteName/Areas/Website/Shared/_PartialHomeFormFreeTrial.cshtml.
This form I have used inside my "Index.cshtml" as below:
<!--freetrialform-->
#Html.Partial("_PartialHomeFormFreeTrial")
<!--//freetrialform-->
Now, my partial page is posting data comprising of following elements:
#using (Html.BeginForm("Create", "Customer", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div>
#Html.EditorFor(model => model.CustomerName, new { htmlAttributes = new { #class = "input__field input__field--kuro" } })
#Html.ValidationMessageFor(model => model.CustomerName, "", new { #class = "text-danger" })
............
other fields such as email, phone, date, etc..
<input type="submit" id="" value="SEND REQUEST" />
</div>
}
Now, I have created a controller named "CustomerController" which has the following code to save the data of the form as used as partial view in the main view "Index.cshtml":
public class CustomerController : Controller
{
private WebsiteContext db = new WebsiteContext();
// GET: Website/Customer
public ActionResult Index()
{
return View();
}
// GET: Website/Customer/Create
public ActionResult Create()
{
ViewBag.StatusPlanID = new SelectList(db.StatusPlans, "StatusPlanID", "Status");
return View("Index");
}
// POST: Website/Customer/Create
[HttpPost]
public ActionResult Create([Bind(Include = "CustomerID,CustomerName,CustomerEmail,CustomerPhone,DateOfRegistration,StatusPlanID")] Customer customer)
{
if (ModelState.IsValid)
{
db.Customers.Add(customer);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.StatusPlanID = new SelectList(db.StatusPlans, "StatusPlanID", "Status", customer.StatusPlanID);
return View(customer);
}
}
I have tried many changes in my controller, return in the views and
many other things but I am getting the same error always. Neither the
validations are working nor the validated data is getting saved.
The error is as below:
Server Error in '/' Application.
The view 'Create' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Areas/Website/Views/Customer/Create.aspx
~/Areas/Website/Views/Customer/Create.ascx
~/Areas/Website/Views/Shared/Create.aspx
~/Areas/Website/Views/Shared/Create.ascx
~/Views/Customer/Create.aspx
~/Views/Customer/Create.ascx
~/Views/Shared/Create.aspx
~/Views/Shared/Create.ascx
~/Areas/Website/Views/Customer/Create.cshtml
~/Areas/Website/Views/Customer/Create.vbhtml
~/Areas/Website/Views/Shared/Create.cshtml
~/Areas/Website/Views/Shared/Create.vbhtml
~/Views/Customer/Create.cshtml
~/Views/Customer/Create.vbhtml
~/Views/Shared/Create.cshtml
~/Views/Shared/Create.vbhtml
And the url is changing as below:
1. On running the system initially: http://localhost:53872/
2. On clicking on submit: http://localhost:53872/Areas/Website/Customer/Create along with the
error as stated above.
For more information my WebsiteAreaRegistration.cs file contains the below code:
public class WebsiteAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Website";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Website_home",
"",
new { controller = "CypressHome", action = "Index", id = UrlParameter.Optional }
);
context.MapRoute(
"Website_default",
"Areas/Website/{controller}/{action}/{id}",
new { controller = "CypressHome", action = "Index", id = UrlParameter.Optional }
);
}
}
Though I have understood the problem but unable to figure out. Kindly help.
In your code last return statement is return View(customer). That's means after POST data it return a View (using HTTP GET method) as same name as Action that's Create. But your description you have a Create Action but you have no view page.
So please create a Create.cshtml with a Model corresponding customer Object.
Or change the return statement.
Based on your comment you can follow this Change.
1.Remove
public ActionResult Create()
{
ViewBag.StatusPlanID = new SelectList(db.StatusPlans, "StatusPlanID", "Status");
return View("Index");
}
2.then change
public ActionResult Index()
{
ViewBag.StatusPlanID = new SelectList(db.StatusPlans, "StatusPlanID", "Status");
return View(new Customer());
}
3.in Index.cshtml
#Model Customer
#Html.Partial("_PartialHomeFormFreeTrial",Model)
4.Then
return View("Index",customer);

ASP.NET MVC / If modelstate is invalid -> return a bootstrap alert

my question is: "How can I show a alert if the modelstate is invalid?"
I have a HomeController where I check if the ModelState.IsValid. I'm using a modal to create new items.
// GET: Project/
public ActionResult Index()
{
using (var service = new Service1Client())
{
ProjectDto newProject = new ProjectDto();
newProject.StartTime = DateTime.Now;
var details = service.GetAllRequirementDetails();
var list = new SelectList(details, "Id", "Title");
var projects = service.GetAllProjects().ToList();
var vm = new ProjectIndexViewModel(list, newProject);
vm.Projects = projects;
return View(vm);
}
}
// POST: Project/Create/
[HttpPost]
public ActionResult Index(CreateProjectViewModel vm)
{
if (ModelState.IsValid)
{
using (var service = new Service1Client())
{
service.CreateProject(vm.NewProject);
return RedirectToAction(RedirectString);
}
}
return RedirectToAction(RedirectString);
}
I want to add
<div class="alert alert-danger" role="alert">
Something went wrong!
</div>
Use ViewBag if returned from same method or if model state returned redirect to next method then use TempData.
Here is example code.
// POST: Project/Create/
[HttpPost]
public ActionResult Index(CreateProjectViewModel vm)
{
if (ModelState.IsValid)
{
using (var service = new Service1Client())
{
service.CreateProject(vm.NewProject);
return RedirectToAction(RedirectString);
}
}
ViewBag.Message = "Something went wrong";//if it is redirecting to some other action then use TempData
return View(vm);
}
In view part
#if(!string.isNotNullorEmpty(ViewBag.Message){
<div class="alert alert-danger" role="alert">
<a href="#" class="alert-link">
#ViewBag.Message</a>
</div>
}
If you are not posting your form via ajax, what you can do is to set a flag to indicate that the validation failed and in your page, check the value of that and execute your js code to show the bootstrap alert.
If ModelState.IsValid is false, you should not be redirecting, you should be returning the posted model to the same view.
[HttpPost]
public ActionResult Index(CreateProjectViewModel vm)
{
if (ModelState.IsValid)
{
using (var service = new Service1Client())
{
service.CreateProject(vm.NewProject);
return RedirectToAction(RedirectString);
}
}
ViewBag.DidValidationFail="Yes";
return View(vm);
}
And in your Index view, render the div based on the value of this flag
#if (ViewBag.DidValidationFail == "Yes")
{
<div class="alert alert-danger" id="errorMsg" role="alert">
Something went wrong!
</div>
}
If you do not prefer to use the dynamic ViewBag, you may add a property to your viewmodel and set that property value and use that in your razor (Same as we did with ViewBag item)
You can use put your message in the else part, and go back to your view (with the posted model) instead of redirecting. I've included a line that is useful to see what went wrong while debugging. That way you can customize your message according to the errors.
[HttpPost]
public ActionResult Index(CreateProjectViewModel vm)
{
if (ModelState.IsValid)
{
using (var service = new Service1Client())
{
service.CreateProject(vm.NewProject);
return RedirectToAction(RedirectString);
}
}
else
{
ViewBag.error = "Something went wrong";
var errors = ModelState.Values.SelectMany(x => x.Errors);
return View(vm);
}
}
And in the view :
<div class="alert alert-danger" role="alert">
#ViewBag.error
</div>

ASP.NET Cross session list

I'm attempting to make a simple page that will compare multiple form submissions.
I have a html page with a form, and a for-loop that generates a div for each item in a list of form submissions. The list is passed from the controller. I am trying to maintain the list in the controller rather than rely on a database.
When I try to resubmit the form, which should add another object to the list, the list re initializes.
In debugging, I see that the list is empty when the form gets submitted. I'm unsure as to the correct terminology, but it seems that the list is emptied whenever the view is rendered. Is there a way to maintain list contents?
I know there are better ways to do this, and welcome any advice. I'm still learning, so pleas go easy.
Thanks!
This is the simplified controller.
namespace MvcApplication2.Controllers
{
public class HomeController : Controller
{
List<paymentPlan> plansList = new List<paymentPlan>();
public ActionResult Index()
{
return View(plansList);
}
[HttpPost]
public ActionResult Index(FormCollection collection)
{
paymentPlan Project = new paymentPlan();
Project.customerName = Convert.ToString(collection["customerName"]);
plansList.Add(Project);
return View(plansList);
}
}
}
This is my simplified view.
#model List<MvcApplication2.Models.paymentPlan>
#using (Html.BeginForm("index", "home", FormMethod.Post, new { Id = "signupForm" }))
{
<label for="customerName">Customer Name:</label>
<input type="text" name="customerName" class="form-control required" />
#Html.ValidationSummary(true)
<input type="submit" value="Calculate" class="btn btn-primary" />
}
#{
bool isEmpty = !Model.Any();
if (!isEmpty)
{
foreach (var i in Model)
{
<div>
Name: #i.customerName
</div>
}
}
}
This is my simplified model.
namespace MvcApplication2.Models
{
public class paymentPlan
{
public string customerName { get; set; }
}
}
I think that's a question of controller and asp.Net MVC lifecycle !
A controller lifetime is the same as the request, for each request a new controller is created and once the work is done it's disposed!
So try to remove this List<paymentPlan> plansList = new List<paymentPlan>(); and work with TempData[] or ViewData[] or Session[] like this :
Controller
public class HomeController : Controller
{
public ActionResult Index()
{
Session["plansList"] = ((List<paymentPlan>)Session["plansList"])!=null? (List<paymentPlan>)Session["plansList"] : new List<paymentPlan>();
return View((List<paymentPlan>)Session["plansList"]);
}
[HttpPost]
public ActionResult Index(FormCollection collection)
{
paymentPlan Project = new paymentPlan();
Project.customerName = Convert.ToString(collection["customerName"]);
((List<paymentPlan>)Session["plansList"]).Add(Project);
return View(plansList);
}
}
check this : http://www.asp.net/mvc/overview/getting-started/lifecycle-of-an-aspnet-mvc-5-application

Umbraco 5 surface controller and partial macro

I'm playing with Umbraco 5 (complete newbie) and currently trying to experiment with the surface controller and macro's.
I've created a basic surface controller:
public class TestSurfaceController : SurfaceController
{
//
// GET: /TestSurface/
[ChildActionOnly]
public ActionResult GetTest()
{
List<Test> test = new List<Test>();
test.Add(new Test { TestTitle = "Test" });
return View(test);
}
}
And a partial macro:
#inherits PartialViewMacroPage
#using Umbraco.Cms.Web
#using Umbraco.Cms.Web.Macros
#using Umbraco.Framework
#model IEnumerable<Umbraco.Models.Test>
<ul>
#foreach (var test in Model)
{
<li>#test.TestTitle</li>
}
</ul>
And on my home template, I call it:
#inherits RenderViewPage
#using System.Web.Mvc.Html;
#using Umbraco.Cms.Web;
#{
Layout = "_Layout.cshtml";
}
#section head
{
#Umbraco.RenderMacro("getTest")
}
How do I get it to just display test in the ul? I either get an error saying I can't use inherits if model is used, then if I take away inherits I get a message saying that the model supplied isn't as expected.
#inherits RenderViewPage
Remove this line from your partial page, if you want i can post an example of a working surface controller action and partial view.
hope that helps.
Working Example is below,
public class MDSSurfaceController : SurfaceController
{
public MDSSurfaceController(IRoutableRequestContext routableRequestContext)
: base(routableRequestContext)
{
}
[ChildActionOnly]
public PartialViewResult ApartmentListMacro(string apartmentType, string Name, string PropertyRfDicItem, string RatesperNightDict, string SleepsDict, string BedroomsDict, string BathroomsDict, string ViewDict)
{
ApartmentListModel apM = new ApartmentListModel();
//initialize model
return PartialView(apM);
}
Then my Partial View is
#using Umbraco.Cms.Packages.SystemInfo.Models
#model Umbraco.Cms.Packages.SystemInfo.Models.ApartmentListModel
#{
//Html Code
}