Here I have a partial view in MVC 5
#modelType HOA5.MainMenuModel
<div class="navbar navbar-inverse navbar-fixed-top" id="mainNavbar">
<div class="container">
#Html.Raw(ViewBag.htmlstr)
</div>
</div>
...and the controller returns some dynamic content to ViewBag
Function Index() As ActionResult
Dim vModal As New MainMenuModel
vModal.MenuHTML = SB.ToString
Dim MenuHTML As String = ReturnMenu()
ViewBag.htmlstr = MenuHTML
Return View("MainMenuView")
End Function
... and then run just that view
http://localhost:52735/mainMenu/
It works perfectly and inserts the dynamic content. However, when I add the partial view to _Layout
#Html.Partial("~/Views/MainMenu/MainMenuView.vbhtml")
and run it
http://localhost:52735
Then the static content appears, but not the dynamic content.. Have also tried variants of #RenderAction, #RenderPartial but either that is not correct, or I have messed up the syntax!
Any pointers would be appreciated as head has been introduced to wall too many times today :-)
Thank you
As is so often we make things more complicated :-)
The easy fix was to make the function that returns the HTML string an ActionResult
Function ReturnMainMenu() As ActionResult
Try
strSQL = "SELECT * FROM Customers"
Using DS As DataSet = ReturnDataSet_Data(strSQL, 2, False)
For Each row As DataRow In DS.Tables(0).Rows
Next
End Using
Dim vModal As New MainMenuModel
Dim SB As New StringBuilder
SB.Append("<div class=""navbar navbar-inverse navbar-fixed-top"" id=""mainNavbar"">")
SB.Append("<div class=""container"">")
SB.Append("<div class=""navbar-header"">")
SB.Append("<button type=""button"" class=""navbar-toggle"" data-toggle=""collapse"" data-target="".navbar-collapse"">")
SB.Append("<span class=""icon-bar""></span>")
SB.Append("<span class=""icon-bar""></span>")
SB.Append("<span class=""icon-bar""></span>")
SB.Append("</button>")
SB.Append("<a class=""navbar-brand"" href=""/"">Add Variable Here xxx</a>") ' Add the variable here
SB.Append("</div>") ' End of navbar-header
SB.Append("<div class=""navbar-collapse collapse"">")
SB.Append("<ul class=""nav navbar-nav"">")
SB.Append("<li>Home</li></li>")
SB.Append(" <li>Upload eInvoice</li>")
SB.Append("<li>Contact</li>")
SB.Append("</ul>")
SB.Append("</div>") 'end of navbar-collapse
SB.Append("</div>")
SB.Append("</div>")
Dim checkstring As String = SB.ToString
vModal.MenuHTML = SB.ToString
Return Content(checkstring)
Catch ex As Exception
Return Nothing
End Try
End Function
Then (now I understand the syntax) add it to _Layout
#Html.Action("ReturnMainMenu", "MainMenu")
...and everything works....
Related
I have a View that has an Http.Action to get a partial View.
My View has the following:
#Code
ViewData("Title") = "View Claims"
Dim actionUrl As String = "FetchClaimsFor?pn=" & ViewData("PersonnelNo") & "&cm=" & ViewData("ClaimMonth") & "&cy=" & ViewData("ClaimYear")
End Code
<div class="col-sm-12 col-md-6 col-lg-6">
#Html.Action(actionUrl, "ClaimMonth")
</div>
My ClaimMonthController has the following:
<HttpGet>
<ChildActionOnly>
Function FetchClaimsFor() As PartialViewResult
Dim pn As String = Request.QueryString("pn")
Dim cm As Integer = Request.QueryString("cm")
Dim cy As Integer = Request.QueryString("cy")
Dim ClaimsMade = New OvertimeClaims
ClaimsMade.GetClaimsFor(pn, cm, cy)
Select Case tsbClaimsMade.CoreDays
Case 0
Return PartialView("_TSB0", OvertimeClaims)
Case 5
Return PartialView("_TSB5", tsbClaimsMade)
Case 6
Return PartialView("_TSB6", tsbClaimsMade)
Case 8
Return PartialView("_TSB8", tsbClaimsMade)
Case Else
Return PartialView("_TSB0", tsbClaimsMade)
End Select
End Function
I did this approach as the partialview can be one of 4 types based on a value returned in the ClaimsMade object.
Unfortunately I'm getting the error...
A public action method 'FetchClaimsFor?pn=11111&cm=12&cy=2016' was not
found on controller 'MyAPP.Controllers.ClaimMonthController'.
Looking at other similar question in SO I have tried with and without <HttpGet> and with and without <ChildActionOnly> - each time I get the same error.
UPDATE
forgot to mention I also tried adding Public to the function
SOLUTION
View code should be:
#Html.Action("FetchClaimsFor", "ClaimMonth", New With { .pn = ViewData("PersonnelNo"), .cm = ViewData("ClaimMonth"), .cy = ViewData("ClaimYear") })
Controller code should be:
Function FetchClaimsFor(pn As String, cm As Integer, cy As Integer) As PartialViewResult
REMOVE the next three lines.
Dim pn As String = Request.QueryString("pn")
Dim cm As Integer = Request.QueryString("cm")
Dim cy As Integer = Request.QueryString("cy")
The first param to Html.Action is not a URL, but rather an action name. You're correctly being told that there's no action that matches FetchClaimsFor?pn=11111&cm=12&cy=2016. Instead, you should be doing:
#Html.Action("FetchClaimsFor", "ClaimMonth", New With { .pn = ViewData("PersonnelNo"), .cm = ViewData("ClaimMonth"), .cy = ViewData("ClaimYear") })
Note: I'm not fluent in VB, so the syntax may be off
I have a VB.NET mvc view form that has 3 parameters and 2 buttons and I not sure how to design my page.
Button 1 is essentially a GET - suppose to run a sql query and return a dataset (multiple tables).
Button 2 is essentially a PUT - suppose to run a sql update statement (multiple updates).
CustomModel includes my 3 parameters as Properties and I have included the Required Attribute on all.
How do I design so that the Required attributes are respected.
I.e. my ViewInfo method is being reached even if the parameter inputs are blank.
Do I declare ViewInfo with the HttpGet and my UpdateInfo with HttpPut attribute. Do I need the parameter declarations in my ViewInfo method?
Is my #Html.BeginForm(...) declaration correct?
VB.NET Model class:
Public Class CustomModel
<Required(ErrorMessage:="Parameter 1 is required"),
Display(Name:="Parameter 1:")>
Public Property Parameter1 As String
<Required(ErrorMessage:="Parameter 2 is required"),
Display(Name:="Parameter 2:")>
Public Property Parameter2 As String
<Required(ErrorMessage:="Parameter 3 is required"),
Display(Name:="Parameter 3:")>
Public Property Parameter3 As Integer
Public Sub New(Parameter1 As String, Parameter2 As String, Parameter3 As Integer)
Me.Parameter1 = Parameter1
Me.Parameter2 = Parameter2
Me.Parameter3 = Parameter3
End Sub
Public Property Info As DataSet = New DataSet
End Class
VB.NET Controller:
Function Index() As ActionResult
ViewBag.ShowView = False
Return View()
End Function
Function ViewInfo(Parameter1 As String, Parameter2 As String, Parameter3 As Integer)
Dim result As New CustomModel(Parameter1, Parameter2, Parameter3)
If ModelState.IsValid Then
result.Info = GetInfo(Parameter1, Parameter2, Parameter3)
End If
ViewBag.ShowView = True
Return View("Index", result)
End Function
Function UpdateInfo(Parameter1 As String, Parameter2 As String, Parameter3 As Integer)
Dim result As New CustomModel(Parameter1, Parameter2, Parameter3)
If ModelState.IsValid Then
result.Info = UpdateInfo(Parameter1, Parameter2, Parameter3)
End If
ViewBag.ShowView = True
Return View("Index", result)
End Function
HTML View
#ModelType CustomModel
...
#Using (Html.BeginForm("ViewInfo", "Home", Nothing, FormMethod.Post))
...
#Html.LabelFor(...)
#Html.TextBoxFor(...)
...
<button type=button>View</button>
<button type=submit>Update</button>
End Using
Output:
#If ViewBag.Show Then
// Multiple WebGrid calls ???
End If
I found a solution to get me on my way finally.
I did not realise the default layout page behind the scenes does not include the validation javascript file so I had to add to my specific view file:
#Section scripts
#Scripts.Render("~/bundles/jqueryval")
End Section
Is there something you have to do so that this file is automatically brought in or is what I have done is correct you have to manually include it in either the layout or the specific view where you need it?
I'm using custom validation requiredconditional that inherits from RequiredAttribute to conditionally require certain fields. This works beautifully everywhere except in once case and I cannot figure out what is happening. It is calling the IsValid method twice for one property in the model (the client side validation works perfectly) The model has 2 properties using this attribute but only one has the issue. At first I thought it was because one of my conditions was a checkbox and it was posting back both the checked value and the hidden value but I tried using radio buttons instead and even a hidden value but had the same results in all cases. Here is my view (simplified for testing):
#ModelType List(Of eLADNETBusiness.AdditionalInterest)
#Code
ViewData("Title") = "Bind Coverage Entry"
End Code
<h2>Bind Coverage Entry</h2>
#Using Html.BeginForm()
#Html.AntiForgeryToken()
#Html.Hidden("hullValue", ViewBag.HullValue)
Dim currentCount As Integer = 0
#For count As Integer = 0 To Model.Count - 1
currentCount = count
#<div class="editor-label">
#Html.LabelFor(Function(model) model(currentCount).LienholderAmount)
</div>
#<div class="editor-field">
#Html.EditorFor(Function(model) model(currentCount).LienholderAmount)
#Html.ValidationMessageFor(Function(model) model(currentCount).LienholderAmount)
</div>
#<div>
#Html.EditorFor(Function(model) model(currentCount).Lienholder90Percent)
</div>
Next
#<p>
<input type="submit" value="Continue" />
</p>
End Using
And here is my model (simplified for testing):
<DataContract()> _
Public Class AdditionalInterest
<DataMember()> _
Public Property ID As Integer = 0
<RequiredConditional("Lienholder90Percent", False, ErrorMessage:="Enter Breach of Warranty lienamount or select 90 percent of insured value")> _
<Display(Name:="Lienholder Amount")> _
<DataMember()> _
Public Property LienholderAmount As Nullable(Of Integer)
<DataMember()> _
Public Property Lienholder90Percent As Boolean
End Class
And my requiredconditional attribute:
Imports System.Collections.Generic
Imports System.Linq
Imports System.ComponentModel.DataAnnotations
Imports System.Web.Mvc
Imports System.Collections
Imports System.Text
Public Class RequiredConditional
Inherits RequiredAttribute
Implements IClientValidatable
Private Property PropertyNames() As String()
Private Property DesiredValues() As Object()
Public Sub New(comparePropertyNames As String(), comparePropertyDesiredValues As Object())
PropertyNames = comparePropertyNames
DesiredValues = comparePropertyDesiredValues
End Sub
Public Sub New(comparePropertyNames As String, comparePropertyDesiredValues As Object)
PropertyNames = New String() {comparePropertyNames}
DesiredValues = New String() {comparePropertyDesiredValues}
End Sub
Protected Overrides Function IsValid(value As Object, context As ValidationContext) As ValidationResult
Dim instance As Object = context.ObjectInstance
Dim type As Type = instance.GetType()
Dim propertyvalue As Object
Dim trueConditions As Integer = 0
For count As Integer = 0 To PropertyNames.Count - 1
propertyvalue = type.GetProperty(PropertyNames(count)).GetValue(instance, Nothing)
If Not propertyvalue Is Nothing Then
If DesiredValues.Count >= count + 1 Then
If propertyvalue.ToString() = DesiredValues(count).ToString() Then
trueConditions += 1
End If
End If
End If
Next
'if all conditions are met, validate value
If trueConditions = PropertyNames.Count And trueConditions = DesiredValues.Count Then
Dim result As ValidationResult = MyBase.IsValid(value, context)
Return result
End If
Return ValidationResult.Success
End Function
Public Function GetClientValidationRules(metadata As System.Web.Mvc.ModelMetadata, context As System.Web.Mvc.ControllerContext) As System.Collections.Generic.IEnumerable(Of System.Web.Mvc.ModelClientValidationRule) _
Implements System.Web.Mvc.IClientValidatable.GetClientValidationRules
Dim results As New List(Of ModelClientValidationRule)
Dim rule = New ModelClientValidationRule With {.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()), .ValidationType = "requiredif"}
Dim depProp As String = String.Empty
Dim sbProp As New StringBuilder()
Dim sbTarget As New StringBuilder()
Dim helper As New ValidationHelper
For count As Integer = 0 To PropertyNames.Count - 1
Dim prop As String = PropertyNames(count)
depProp = helper.BuildDependentPropertyName(metadata, TryCast(context, ViewContext), prop)
sbProp.AppendFormat("|{0}", depProp)
Dim targetValue As String = String.Empty
If DesiredValues.Count >= count + 1 Then
targetValue = (If(DesiredValues(count), "")).ToString()
End If
If DesiredValues(count).GetType() = GetType(Boolean) Then
targetValue = DesiredValues(count).ToString.ToLower
End If
sbTarget.AppendFormat("|{0}", targetValue)
Next
rule.ValidationParameters.Add("dependentproperty", sbProp.ToString().TrimStart("|"))
rule.ValidationParameters.Add("targetvalue", sbTarget.ToString().TrimStart("|"))
results.Add(rule)
Return results
End Function
End Class
So when I click submit and debug into the requiredconditional attribute, the lienholderamount property hits IsValid twice. The first time it hits, the value of Lienholder90Percent is False even though in the model it is true and was true on the form (and passed client side validation) so fails validation at that point. Then hits again and Lienholder90Percent is True (which is correct) and passes validation. But since it failed on the first one, it still fails and show the error message. You'll notice that the model is a list but for testing purposes, I'm only sending one and still getting the same results. Can't figure out why it is only happening with this one property. Hope it is something easy I just can't see. I've spent all day trying to figure this out. Like I said, I use this attribute quite a bit and works great. Can't find a difference for this case.
Inherit from ValidationAttribute instead of RequiredAttribute.
RequiredAttribute's IsValid is called right after the property is filled, whereas the ValidationAttribute's IsValid is called after the whole model is filled (which would make sense for more complex validation).
I have converted my MVC3 application to MVC5, I had to change all views to razor. Having a challenge with a select list:
In ASPX view that works I am using the following:
<select id="Profession" name="Profession" style="width: 235px; background-color: #FFFFCC;">
<% List<string> allProfessions = ViewBag.AllProfessions;
string selectedProfession;
if (Model != null && !String.IsNullOrEmpty(Model.Profession))
selectedProfession = Model.Profession;
else
selectedProfession = allProfessions[0];
foreach (var aProfession in allProfessions)
{
string selectedTextMark = aProfession == selectedProfession ? " selected=\"selected\"" : String.Empty;
Response.Write(string.Format("<option value=\"{0}\" {1}>{2}</option>", aProfession, selectedTextMark, aProfession));
}%>
</select>
In Razor I am using:
<select id="Profession" name="Profession" style="width: 235px; background-color: #FFFFCC;">
#{List<string> allProfessions = ViewBag.AllProfessions;
string selectedProfession;}
#{if (Model != null && !String.IsNullOrEmpty(Model.Profession))
{selectedProfession = Model.Profession;}
else {selectedProfession = allProfessions[0];}
}
#foreach (var aProfession in allProfessions)
{
string selectedTextMark = aProfession == selectedProfession ?
"selected=\"selected\"" : String.Empty;
Response.Write(string.Format("<option value=\"{0}\" {1}>{2}</option>",
aProfession, selectedTextMark, aProfession));
}
</select>
The list shows up at the top of the page, I can't figure out where is the problem. Would appreciate your assistance.
Don't create your dropdown manually like that. Just use:
#Html.DropDownListFor(m => m.Profession, ViewBag.AllProfessions, new { style = "..." })
UPDATE
I tried your solution but got this error: Extension method cannot by dynamically dispatched
And, that's why I despise ViewBag. I apologize, as my answer was a little generic. Html.DropDownList requires the list of options parameter to be an IEnumerable<SelectListItem>. Since ViewBag is a dynamic, the types of its members cannot be ascertained, so you must cast explicitly:
(IEnumerable<SelectListItem>)ViewBag.AllProfessions
However, your AllProfessions is a simple array, so that cast won't work when the value gets inserted at run-time, but that can be easily fixed by casting it to a List<string> and then converting the items with a Select:
((List<string>)ViewBag.AllProfessions).Select(m => new SelectListItem { Value = m, Text = m })
There again, you see why dynamics are not that great, as that syntax is rather awful. The way you should be handling this type of stuff is to use your model or, preferably, view model to do what it should do: hold domain logic. Add a property to hold your list of profession choices:
public IEnumerable<SelectListItem> ProfessionChoices { get; set; }
And then, in your controller action, populate this list before rendering the view:
var model = new YourViewModel();
...
model.ProfessionChoices = repository.GetAllProfessions().Select(m => new SelectListItem { Value = m.Name, Text = m.Name });
return View(model);
repository.GetAllProfessions() is shorthand for whatever you're using as the source of your list of professions, and the Name property is shorthand for how you get at the text value of the profession: you'll need to change that appropriately to match your scenario.
Then in your view, you just need to do:
#Html.DropDownListFor(m => m.Profession, Model.ProfessionChoices)
Given that you don't have this infrastructure already set up, it may seem like a lot to do just for a drop down list, and that's a reasonable thing to think. However, working in this way will keep your view lean, make maintenance tons easier, and best of all, keep everything strongly-typed so that if there's an issue, you find out at compile-time instead of run-time.
I believe it's happening because of the Response.Write. Try this:
#Html.Raw(string.Format("<option value=\"{0}\" {1}>{2}</option>", aProfession,
selectedTextMark, aProfession))
I have a Razor view that starts like:
#using My.Models
#model MySpecificModel
#{
ViewBag.Title = "My Title";
// NullReferenceException here:
string dateUtc =
(Model == null ? DateTime.UtcNow.ToShortDateString() :
Model.Date.ToShortDateString());
I see no reason for the NullReferenceException in the last line (note: the " = ? :" stuff is on one line in my source code. It is formatted to fit here.)
I then remove the declaration of/assignment to dateUtc and the NullReferenceException moves up to the ViewBag.Title line:
#using My.Models
#model MySpecificModel
#{
ViewBag.Title = "My Title"; // NullReferenceException here:
How can this possibly happen? ViewBag is of course not null.
NOTE 1: This only happens if Model is null.
NOTE 2: MySpecificModel.Date is of type DateTime, so can never be null.
A NullReferenceException on ViewBag.Title can indicate the error is really on a nearby line. In this example the error was thrown on line 1 but the real error was the null Model.Invoice in line 3.
<h2>#ViewBag.Title</h2>
<div class="pull-right">
<button onclick="addInvoice('#Model.Invoice.InvoiceId');">Add Invoice</button>
</div>
Also ASP.NET MVC Razor does not handle nulls in ternary statements like C# does.
//Ternaries can error in Razor if Model is null
string date = (Model == null ? DateTime.Now : Model.Date);
//Change to
string date = null;
if (Model == null)
date = DateTime.Now;
else
date = Model.Date;
You provide an empty default model which does nothing. It will be just so the Model isn't null. It will help to have an IsEmpty property . Better yet, if it can be applied in your case, a model with default values. The important thing is the Model not being null ever
You will get a null exception if you do not pass any model to the view and still have the view bound to a model e.g.:
#model MySpecificModel
When you did not pass such a model to the view from your controller.