I'm new to NancyFX and trying to simply bind a posted form to my model.
In the module when trying to access the posted values I run following statement:
string email = this.Context.Request.Form["Email"];
Debug.WriteLine(email);
Result is:
"Nancy.DynamicDictionaryValue" instead of posted value
Can anybody tell me what newbie mistake I'm doing:
The form looks like:
<form method="post" action="account">
<input type="text" id="Email" />
<input type="password" id="Password" />
<input type="submit" value="Create" />
</form>
the routing in Module contructor:
Post["/"] = parameters => CreateAccount(parameters);
The dynamic dictionary returns a dynamic value, if you cast it to a string (implicitly or explicitly) you'll get what you want, or just use the build in model binder https://github.com/NancyFx/Nancy/wiki/Model-binding
Just adding to the correct answer above in the hope it is useful to nancy-newbies like me.
Because the Nancy Form and Query are of type dynamic you can access the values using the name of the form or query-string param (see terms and max in the example code). I use a simple base class just to make the syntax terser throughout the rest of my modules.
Note: The ExpandoObject Model in the base class is there so I can just throw values at my view-model and not have to worry about cluttering things up with strongly typed data-transfer classes (this also helps prevent exposing any secret domain instance data).
public class SearchModule : _BaseModule
{
public SearchModule(ISearchService searchService)
{
Get["/search"] = _ =>
{
if (!Query.terms.HasValue) return HttpStatusCode.BadRequest;
var terms = (string) Query.terms;
var max = (Query.max.HasValue) ? (int) Query.max : 3;
Model.SearchResults = searchService.GetResults(max, terms);
...
};
}
}
public class _BaseModule : NancyModule
{
protected dynamic Model = new ExpandoObject();
public dynamic Query { get { return Request.Query; } }
public dynamic Form { get { return Request.Form; } }
}
Related
I try to save the values from dynamically created checkboxes:
<div>
<ul>
<li th:each="item, stat : *{users}">
<input type="checkbox" th:field="*{users[__${stat.index}__]}" th:value="${item}" />
<label th:text="${item}"></label>
</li>
</ul>
</div>
The controller provides the String items as follwing:
public List<String> getUsers() {
return users;
}
And the setter for the Strings is:
public void setUsers(final String[] users) {
for (final String string : users) {
System.out.println(string);
}
}
The values are correct shown in the html page. But when i click save button, and the setter is called, the values are empty. What can i do, where is the problem?
Any help would appreciate.
Please check out section about handlin multi-value checkboxes in Tutorial: Thymeleaf + Spring.
You should provide some model attribute (of type List<String>) containing all users possible to select. Let's call it selectableUsers.
Then it can collaborate with your form-backing bean (that one containing users) in a following manner:
<div>
<ul>
<li th:each="item : ${selectableUsers}">
<input type="checkbox" th:field="*{users}" th:value="${item}" />
<label th:for="${#ids.prev('users')}" th:text="${item}"></label>
</li>
</ul>
</div>
Note I think that getter and setter for a field should handle the same type, but they don't (getter returns List<String> however setter consumes String[])
What you are trying to do looks logical, but it does not work that way.
If you did not get it resolved you can do this instead:
In relevant method of your controller you can add list of titles for your checkboxes:
List<String> allUsers = Arrays.asList("abc","xyz"); // OR generate list dynamically
model.addAttribute("selectableUsers", allUsers);
Or add it to ModelAndView if that is what you are using.
Change your html to what was suggested by #Jakub Ch.
Change your getter and setter methods as follows:
private String users;
...
public String getUsers() {
return this.users;
}
public void setUsers(String users) {
this.users = users;
}
Then 'users' field will contain comma separated String values or their id numbers ( depending on how you set it up) indicating selected checkboxes. Then you can convert String values to array using code like below or if id numbers are stored get String values from your ArrayList.
public List<String> getStrings() {
return Arrays.asList(strings.split(","));
}
Hope it helps.
this should be straight forward, just cant figure out why it's not working.
I receive data in this format from Web API 2 (captured from Chrome's debugger):
AngularJS code to render the results
(vm.reportParameters contains that structure on the screenshot with 2 nodes):
<form>
<div ng-repeat="param in vm.reportParameters" class="form-group">
<label>Name</label>
<input type="text" class="form-control" value="{{param.Name}}" />
</div>
</form>
The output (missing value of Name property, should display "Country"):
Any idea what I am missing here? Why the value is not shown?
// GET api/reports/5
// This action retrieves parameters of selected report by reportId
[ResponseType(typeof(ParametersModel))]
public IHttpActionResult Get(string reportId)
{
try
{
var manager = new ReportsManager();
var model = manager.GetReportParameters(reportId);
if (model == null || model.Parameters == null || model.Parameters.Count == 0)
{
return NotFound();
}
return Ok<ParametersModel>(model);
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
Thanks.
UPDATE
This garbage-alike data has this weird format with all these k__XXXXX
things because I had various attributes applied to the model for XML
Deserialization (in C# code). After I removed all these Serialization
attributes, the model became normal and clean as expected. Go guess :)
Use without expression, ng-model
<input type="text" class="form-control" ng-model="param.Name" />
I wanted to create a page like following image in Composite-C1 with File upload functionality. But I am stucked here unable to find any thing on google. What will be the best approach to do this. How to create a simple razor functions to do this. What will be in form actions.
I would go ahead and use the free and very flexible Contrib FormBuilder which you can get from here https://bitbucket.org/burningice/compositec1contrib.formbuilder. Under 'Downloads' is the zip-files, which you would go ahead and download and install as Local Packages from within the C1 Console.
For this example you would need CompositeC1Contrib.FormBuilder.0.8.7 and CompositeC1Contrib.FormBuilder.POCO, and if you don't already use the Contrib.Core package, you need grab that one from here https://bitbucket.org/burningice/compositec1contrib/downloads. Install them in this order
Core
FormBuilder
FormBuilder.POCO
When that is all done, you need to create two thing, the form definition and a razor file to present the form
The form defintion is a .cs class file, kinda like a Model in NVC, with a number of properties and a submit handler
Form.cs
using System;
using CompositeC1Contrib.FormBuilder;
using CompositeC1Contrib.FormBuilder.Attributes;
using CompositeC1Contrib.FormBuilder.Validation;
using CompositeC1Contrib.FormBuilder.Web.UI;
namespace StackExchange
{
[FormName("Forms", "Feedback")]
public class Form : IPOCOForm
{
[FieldLabel("Feedback type")]
[RequiredField("Required")]
[DropdownInputElement]
public string FeedbackType { get; set; }
[FieldLabel("Version")]
[RequiredField("Required")]
[DropdownInputElement]
public string Version { get; set; }
[FieldLabel("Attachment")]
[RequiredField("Required")]
public FormField File { get; set; }
[FieldLabel("Version")]
[RequiredField("Required")]
[TextAreaInputElement]
public string Description { get; set; }
public void Submit()
{
throw new NotImplementedException();
}
}
}
The razor file is what controls the layout of the form. Out of the box it has helper methods to just dump all your fields downwards, you're free to add any html, markup and styling in it as you want.
Go ahead and create a .cshtml file named Feedback.cshtml and put in in your ~/App_Data/Razor/Forms folder
#inherits POCOBasedFormsPage<Feedback>
#if (IsSuccess && (SuccessResponse != null && !SuccessResponse.IsEmpty))
{
#EvaluateMarkup(SuccessResponse.Root)
}
else
{
#EvaluateMarkup(IntroText.Root)
using (BeginForm(new { #class = "form-horizontal" }))
{
#WriteErrors()
#WriteAllFields()
#WriteCaptcha()
<div class="control-group submit-buttons">
<div class="controls">
<input type="submit" name="SubmitForm" class="btn btn-primary" value="Send Enquiry" />
</div>
</div>
}
}
Now you're ready to insert the form on a page on in a template as you'd like. It will turn up as a function named Forms.Feedback and is used like any other C1 Function as you're used to.
I'm using Valdr on my project and I need to validate that a date input "startDate" is before another date input "endDate".
<input id="startDate" name="startDate" type="text" ng-model="project.startDate"/>
<input id="endDate" name="endDate" type="text" ng-model="project.endDate"/>
I know that, without Valdr, this problem can be solved using a custom directive, as shown here: Directive for comparing two dates
I found a little unclear how to create a custom validator on Valdr that uses the values of other fields.
The answer is short but dissatisfactory: valdr does currently not support this. There is an open feature request on GitHub, though.
Until the feature gets implemented in valdr, you can use your own validator directive and kind of make it talk to valdr. The directive can require a 'form' and can get the names of the date models you want to compare. Then you do your logic to compare the two values and set the validity of the appropriate 'ngModelController'. Since you need to provide an error when setting the validity to that model, the error name will be your connection with valdr.
After that, you just only need to map the error in the 'valdrMessage' service:
.run(function (valdrMessage) {
valdrMessage.angularMessagesEnabled = true;
valdrMessage.addMessages({
'date': 'Invalid date!'
});
});
Valdr will show the message bellow the invalid field as usual.
Actually you can solve this through a custom validator, which can get another field and compare the values with each other. The code below is using the valdr-bean-validation for serverside generation of valodation.json.
If you want to use it without this, just look into the JS code and add the validator in your validation.json manually.
Java Annotation (serverside declaration of the valdr validator):
package validation;
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR,
ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
public #interface DateFormat {
String message();
Class[] groups() default { };
String beforeFieldName();
}
Java Bean (usage of the annotation, this class has to be used in the generation of validation.json):
package pojo;
import validation.DateFormat;
public class RegistrationPojo implements BasePojo {
#NotNull(message = "message.date1.required")
private Date date1;
#NotNull(message = "message.date2.required")
#DateFormat(message = "message.date2.date", beforeFieldName = "date1")
private Date date2;
}
JS (implementation of the custom validator and registering it in valdr):
module.factory('validation.DateFormat', [
function () {
return {
name: 'validation.DateFormat',
validate: function (value, constraint) {
var minOk = true;
var maxOk = true;
var format = false; // constraint.pattern is mandatory
//do not validate for required here, if date is null, date will return true (valid)
console.log("my date validator called");
console.log(" beforeFieldName: " + constraint.beforeFieldName);
var field = document.querySelector('[name="' + constraint.beforeFieldName + '"]');
console.log("field value: " + (field ? field.value : "null"));
return (!field || value > field.value);
}
};
}]);
module.config([
"valdrProvider",
function(valdrProvider) {
valdrProvider.addValidator('validation.DateFormat');
}]);
You could go with this solution:
Have a bool value calculated upon changes in any of the date fields - a value indicating if the validation rule is met
Create a simple custom validator to check if that value is true or not
Register your validator and add a constraint for that calculated value
Place a hidden input for that calculated value anywhere you like your validation message to appear
I have a form that contains a lot of fields and I want to post all the form fields to a service using a post method. But I would like to send the whole form object and not to write one property by one. If I try to post the object that contains all my fields $scope.formData it also contains all the angular stuff inside like errors. What I need is a collection of field names and values. How can I achieve this with minimum coding?
Edit:
I ended up writing my own function:
function getAngularFormFields(form) {
var dictionary = { form: {} };
for (var key in form) {
if (form.hasOwnProperty(key) && !key.indexOf('$') == 0) {
dictionary.form[key] = form[key].$modelValue;
}
}
return dictionary;
}
Normally if you need to post a form you could just use the default method provided by your browser. This will send the form data, via POST, to your URL.
<form action="yourUrlHere" method="POST">
First name: <input type="text" name="fname">
Last name: <input type="text" name="lname">
<input type="submit" value="Submit">
</form>