EDIT: I have now changed Request to Context.Request and am receiving a different error to the title of this post now: Cannot apply indexing with [] to an expression of type 'HttpRequest'
So I am trying to introduce myself to ASP.Net and am following an online tutorial available here: https://www.w3schools.com/asp/webpages_forms.asp (the 'Displaying Images' part). I am attempting to implement this in an MVC style layout. The starting template for the structure is a modified version of the template that is produced when you run dotnet new -t web.
In my Pictures.cshtml file, I have the following:
#{
ViewData["Title"] = "Pictures";
var imagePath="";
if (Request["Choice"] != null)
{
imagePath="images/" + Request["Choice"];
}
}
<h2>Pictures</h2>
<form method="post" action="">
I want to see:
<select name="Choice">
<option value="Photo1.jpg">Photo 1</option>
<option value="Photo2.jpg">Photo 2</option>
<option value="Photo3.jpg">Photo 3</option>
</select>
<input type="submit" value="Submit" />
#if (imagePath != "")
{
<p>
<img src="#imagePath" alt="Sample" />
</p>
}
</form>
This is called from a MainController.cs as so:
using Microsoft.AspNetCore.Mvc;
namespace WebApp.Controllers
{
public class MainController : Controller
{
public IActionResult Pictures()
{
return View();
}
}
}
There is also a _ViewStart.cshtml which references a _Layout.cshtml.
When running this, I am redirected to my error page, and the terminal gives the error The name 'Request' does not exist in the current context
Can somebody help point me in the right direction as to what I am missing or what I have done wrong? Why does this tutorial example not work in the context of my project?
Cheers :)
I had the same error in Core 2.1. I've found if you put it in the ViewData or Styles section, it's hard to debug (or something just wonky about it). Otherwise, it should work like this for a specific parameter:
#Context.Request.Query["Choice"]
For your code, I would just request the value once and assign it to a variable, then see if it's empty or not. Then, create the path from your variable:
var choice = #Context.Request.Query["Choice"];
var imagePath="";
if (!String.IsNullOrEmpty(choice)){
imagePath="images/" + choice;
}
As a bonus, you can also get the entire query string with all the names and values (in case you want to parse it or whatever) by going:
#Context.Request.QueryString
I use this in ASP Core 5.0 :
ViewContext.HttpContext.Request.Query["Choice"]
example in JS:
if (getParameterByName("error") != null) {
window.location.href = "#Url.Action("Login", "Home" , new { error = ViewContext.HttpContext.Request.Query["Choice"] })";
}
Related
I've got the following in an apps script client side html template:
<body>
<form id="myForm" onsubmit="handleFormSubmit(this)">
<div>
<label for="optionList">Click me</label>
<select id="optionList" name="email" onchange="optionChange()">
<option>Loading...</option>
</select>
</div>
I want to capture the change using :
function optionChange() {
mySelect = document.getElementById("optionList");
console.log(mySelect)
};
But I do not see any elements in the console.
Following some other boilerplate code I got from some examples I tried:
mySelect = $(document).getElementById("optionList").value;
Now in the console I get:
TypeError: $(...).getElementById is not a function
At least I see something in the console. What is '$' and How can I get this working?
I've made the suggested change but still just see spreadsheet related post and get statements in the console.
You should specify the value property of the element, so
//based on your coding style
function optionChange() {
mySelect = document.getElementById("optionList");
mySelectValue = mySelect.value;
console.log(mySelectValue)
};
or
//based on your coding style
function optionChange() {
mySelect = document.getElementById("optionList").value;
console.log(mySelect)
};
Suppose I am having three dropdownlist controls named dd1, dd2 and dd3. The value of each dropdownlist comes from database. dd3's value depends upon value of dd2 and dd2's value depends on value of dd1. Can anyone tell me how do I call servlet for this problem?
There are basically three ways to achieve this:
Submit form to a servlet during the onchange event of the 1st dropdown (you can use Javascript for this), let the servlet get the selected item of the 1st dropdown as request parameter, let it obtain the associated values of the 2nd dropdown from the database as a Map<String, String>, let it store them in the request scope. Finally let JSP/JSTL display the values in the 2nd dropdown. You can use JSTL (just drop jstl-1.2.jar in /WEB-INF/lib) c:forEach tag for this. You can prepopulate the 1st list in the doGet() method of the Servlet associated with the JSP page.
<select name="dd1" onchange="submit()">
<c:forEach items="${dd1options}" var="option">
<option value="${option.key}" ${param.dd1 == option.key ? 'selected' : ''}>${option.value}</option>
</c:forEach>
</select>
<select name="dd2" onchange="submit()">
<c:if test="${empty dd2options}">
<option>Please select parent</option>
</c:if>
<c:forEach items="${dd2options}" var="option">
<option value="${option.key}" ${param.dd2 == option.key ? 'selected' : ''}>${option.value}</option>
</c:forEach>
</select>
<select name="dd3">
<c:if test="${empty dd3options}">
<option>Please select parent</option>
</c:if>
<c:forEach items="${dd3options}" var="option">
<option value="${option.key}" ${param.dd3 == option.key ? 'selected' : ''}>${option.value}</option>
</c:forEach>
</select>
Once caveat is however that this will submit the entire form and cause a "flash of content" which may be bad for User Experience. You'll also need to retain the other fields in the same form based on the request parameters. You'll also need to determine in the servlet whether the request is to update a dropdown (child dropdown value is null) or to submit the actual form.
Print all possible values of the 2nd and 3rd dropdown out as a Javascript object and make use of a Javascript function to fill the 2nd dropdown based on the selected item of the 1st dropdown during the onchange event of the 1st dropdown. No form submit and no server cycle is needed here.
<script>
var dd2options = ${dd2optionsAsJSObject};
var dd3options = ${dd3optionsAsJSObject};
function dd1change(dd1) {
// Fill dd2 options based on selected dd1 value.
var selected = dd1.options[dd1.selectedIndex].value;
...
}
function dd2change(dd2) {
// Fill dd3 options based on selected dd2 value.
var selected = dd2.options[dd2.selectedIndex].value;
...
}
</script>
<select name="dd1" onchange="dd1change(this)">
<c:forEach items="${dd1options}" var="option">
<option value="${option.key}" ${param.dd1 == option.key ? 'selected' : ''}>${option.value}</option>
</c:forEach>
</select>
<select name="dd2" onchange="dd2change(this)">
<option>Please select parent</option>
</select>
<select name="dd3">
<option>Please select parent</option>
</select>
One caveat is however that this may become unnecessarily lengthy and expensive when you have a lot of items. Imagine that you have 3 steps of each 100 possible items, that would mean 100 * 100 * 100 = 1,000,000 items in JS objects. The HTML page would grow over 1MB in length.
Make use of XMLHttpRequest in Javascript to fire an asynchronous request to a servlet during the onchange event of the 1st dropdown, let the servlet get the selected item of the 1st dropdown as request parameter, let it obtain the associated values of the 2nd dropdown from the database, return it back as XML or JSON string. Finally let Javascript display the values in the 2nd dropdown through the HTML DOM tree (the Ajax way, as suggested before). The best way for this would be using jQuery.
<%# page pageEncoding="UTF-8" %>
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html lang="en">
<head>
<title>SO question 2263996</title>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>
$(document).ready(function() {
$('#dd1').change(function() { fillOptions('dd2', this); });
$('#dd2').change(function() { fillOptions('dd3', this); });
});
function fillOptions(ddId, callingElement) {
var dd = $('#' + ddId);
$.getJSON('json/options?dd=' + ddId + '&val=' + $(callingElement).val(), function(opts) {
$('>option', dd).remove(); // Clean old options first.
if (opts) {
$.each(opts, function(key, value) {
dd.append($('<option/>').val(key).text(value));
});
} else {
dd.append($('<option/>').text("Please select parent"));
}
});
}
</script>
</head>
<body>
<form>
<select id="dd1" name="dd1">
<c:forEach items="${dd1}" var="option">
<option value="${option.key}" ${param.dd1 == option.key ? 'selected' : ''}>${option.value}</option>
</c:forEach>
</select>
<select id="dd2" name="dd2">
<option>Please select parent</option>
</select>
<select id="dd3" name="dd3">
<option>Please select parent</option>
</select>
</form>
</body>
</html>
..where the Servlet behind /json/options can look like this:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String dd = request.getParameter("dd"); // ID of child DD to fill options for.
String val = request.getParameter("val"); // Value of parent DD to find associated child DD options for.
Map<String, String> options = optionDAO.find(dd, val);
String json = new Gson().toJson(options);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(json);
}
Here, Gson is Google Gson which eases converting fullworthy Java objects to JSON and vice versa. See also How to use Servlets and Ajax?
Judging by your question, you're really not using a web framework but using servlets to render html.
I'll be nice and say that you're about a decade behind the times :), people use JSPs (and a web framework like struts) for this sort of thing. However, having said that, here goes:
Create a hidden field in your form and set the value to '1', '2' or '3' depending on which drop down is to be populated;
In your servlet, capture this value (request.getParamter()) and use it a 'case'/if/else statement to return the appropriate dropdown values.
I'll say it again, just use a web-framework, or atleast plain old jsp to do this.
You may need multiple servlets for this.
Servlet 1: Load the values for the first drop down list from the database. On the JSP page construct the drop down list. On the user selecting a value submit to servlet two.
Servlet 2: retrieve the value from the first list and perform your database search for the values of the second list. Construct the second list. When the user selects the second value submit it to servlet 3.
Servlet 3: retrieve the value selected in the second drop down and perform the database search to get values for the last drop down.
You may want to consider AJAX to make the populating of the lists appear seamless to the users. jQuery has some very nice plugins for making this quite easy if you are willing to do that.
<form action="servlet2.do">
<select name="dd1" onchange="Your JavaScript Here">
<option>....
</select>
</form>
You can write JavaScript that submits the form in the onchange event. Again, If you use an existing library like jQuery it will be 10 times simpler.
That was an awesome simple solution. I like how small the JQuery code is and really appreciate the link to the GSON API. All the examples made this an easy implementation.
Had one issue on building the JSON server URL with the reference to the parent SELECT ( e.g. $(this).val() ) [needed to specify the :selected attribute]. I've modified the script a little to include the suggested updates. Thanks for the initial code.
<script>
$(document).ready(function()
{
$('#dd1').change(function() { fillOptions('dd1', 'dd2'); });
$('#dd2').change(function() { fillOptions('dd2', 'dd3'); });
});
function fillOptions(parentId, ddId)
{
var dd = $('#' + ddId);
var jsonURL = 'json/options?dd=' + ddId + '&val=' + $('#' + parentId + ' :selected').val();
$.getJSON(jsonURL, function(opts)
{
$('>option', dd).remove(); // Clean old options first.
if (opts)
{
$.each(opts, function(key, value)
{
dd.append($('<option/>').val(key).text(value));
});
}
else
{
dd.append($('<option/>').text("Please select parent"));
}
});
}
</script>
I load values for select2 like the following way.
Declare the Type
var AdjustmentType = Backbone.Model.extend({
url : Hexgen.getContextPath("/referencedata/adjustmenttype")
});
create instance for the Type
var adjustmentTypes = new AdjustmentType();
load the values to select2 box
adjustmentTypes.fetch({
success : function() {
for(var count in adjustmentTypes.attributes) {
$("#adjustment-type").append("<option>" + adjustmentTypes.attributes[count] + "</option>");
}
}
});
$("#adjustment-type").select2({
placeholder: "Select Adjustment Type",
allowClear: true
});
My HTML Code
<div class="span4">
<div>ADJUSTMENT TYPE</div>
<select id="adjustment-type" tabindex="5" style="width:200px;">
<option value=""></option>
</select>
</div>
when i load this for the first it is not giving any exception but if i Refresh or navigate to different URL i get the following exception:
Uncaught query function not defined for Select2 adjustment-type
"Query" refers to the list by which to check your search terms against. You need to make sure that your data property is a proper array of objects (ie. your options elements).
I am building a WebPages site and have an issue when I try to pass ModelState data to a partial page.
Here is the code for Create.cshtml:
#{
Page.Title = "Create Fund";
var name = "";
if (IsPost) {
name = Request.Form["name"];
if (!name.IsValidStringLength(2, 128)) {
ModelState.AddError("name", "Name must be between 2 and 128 characters long.");
}
}
}
#RenderPage("_fundForm.cshtml", name)
Here is the code for _fundForm.cshtml:
#{
string name = PageData[0];
}
<form method="post" action="" id="subForm">
<fieldset>
#Html.ValidationSummary(true)
<legend>Fund</legend>
<p>
#Html.Label("Name:", "name")
#Html.TextBox("name", name)
#Html.ValidationMessage("name", new { #class = "validation-error" })
</p>
<p>
<input type="submit" name="submit" value="Save" />
</p>
</fieldset>
</form>
The issue I am having is when there is an error for "name", the validation error does not display. Is there a special way to pass ModelState between the two pages?
_fundForm is going to be shared between Create.cshtml and Edit.cshtml.
ModelState is a readonly property of System.Web.WebPages.WebPage class. Its backing field is a private ModelStateDictionary and is initialized at first access. I can't see any way to force ModelState across pages, apart from doing it via reflection as seen in SO question: Can I change a private readonly field in C# using reflection?
Otherwise, you can simply use a third parameter in the invocation, like this:
#RenderPage("_fundForm.cshtml", name, ModelState);
In effect, the first parameter after the page name will become the Model of the new page, so there is enough space (i.e. the next parameter) to pass the ModelState.
In your "_fundForm.cshtml" merge the ModelState received by the calling page with the local one, like this:
#{
//In _fundForm.cshtml
var ms = PageData[1];
ModelState.Merge((ModelStateDictionary)ms);
}
I have an editor template whose job is to take a SelectList as its model and build a select element in html using the Html.DropDownList() helper extension.
I'm trying to assign the name attribute for the select based on a ModelMetadata property. (Reason: on post-back, I need to bind to an object that has a different property name for this item than the ViewModel used to populate the form.)
The problem I'm running into is that DropDownList() is appending the name I'm providing instead of replacing it, so I end up with names like categories.category instead of category.
Here is some code for you to look at...
SelectList.ascx
<%# Control Language="C#"
Inherits="System.Web.Mvc.ViewUserControl<System.Web.Mvc.SelectList>" %>
<%= Html.DropDownList(
(string)ViewData.ModelMetadata.AdditionalValues["PropertyName"], Model) %>
Resulting HTML
<select id="SkillLevels_SkillLevel" name="SkillLevels.SkillLevel">
<option value="1">High</option>
<option value="2">Med</option>
<option selected="selected" value="3">Low</option>
</select>
Expected HTML
<select id="SkillLevels_SkillLevel" name="SkillLevel">
<option value="1">High</option>
<option value="2">Med</option>
<option selected="selected" value="3">Low</option>
</select>
Also tried
<%= Html.Encode((string)ViewData.ModelMetadata.AdditionalValues["PropertyName"])%>
...which resulted in "SkillLevel" (not "SkillLevels.SkillLevel"), proving that the data stored in metadata is correct.
and
<%= Html.DropDownList(
(string)ViewData.ModelMetadata.AdditionalValues["PropertyName"], Model,
new { name = (string)ViewData.ModelMetadata.AdditionalValues["PropertyName"] }) %>
...which still resulted in <select name=SkillLevels.Skilllevel>.
Questions
What's going on here? Why does it append the name instead of just using it? Can you suggest a good workaround?
Update:
I ended up writing a helper extension that literally does a find/replace on the html text:
public static MvcHtmlString BindableDropDownListForModel(this HtmlHelper helper)
{
var propertyName = (string)helper.ViewData.ModelMetadata.AdditionalValues["PropertyName"];
var compositeName = helper.ViewData.ModelMetadata.PropertyName + "." + propertyName;
var rawHtml = helper.DropDownList(propertyName, (SelectList)helper.ViewData.Model);
var bindableHtml = rawHtml.ToString().Replace(compositeName, propertyName);
return MvcHtmlString.Create(bindableHtml);
}
>>> I'd still like to understand why this workaround is necessary. Why does the select element's get assigned a composite name rather than the exact name I provide?
I have struggled with the MVC drop down - it just does not act right. So, I just roll my own html with a foreach on my model data. That may not be an acceptable workaround for you but MVC is flexible on purpose.
Disclaimer: I am not sure if this is totally safe and wont break anything.
But I faced the exact same situation. However, once i set
ViewData.TemplateInfo.HtmlFieldPrefix = String.Empty ,
the problem went away. This behavior seems to be by design inside of custom templates - take a look at ViewData.TemplateInfo.GetFullHtmlFieldName for example.