Recent NancyFX has support for list binding https://github.com/NancyFx/Nancy/wiki/Model-binding but it didn't work for me.
For collection model binding test I created a stackoverflow-like Question model:
public class Question
{
public int Id { get; set; }
public string Name { get; set; }
public List<Answer> Answers { get; set; }
public List<Comment> Comments { get; set; }
}
public class Answer
{
public int Id { get; set; }
public string Name { get; set; }
public List<Comment> Comments { get; set; }
}
public class Comment
{
public int Id { get; set; }
public string Name { get; set; }
}
I bind it like:
Post["/question"] = parameters =>
{
Question question = this.Bind();
return View["question.html", question];
};
In HTML form I tried different things:
<form action="/question" method="post">
<ul>
<li>
<input type="text" name="Name" value="Question Name" />
<input type="text" name="Id" value="1" />
</li>
<li>
<ul>
<li>
<input type="text" name="Answers[0].Name" value="Answer 1 Name" />
<input type="text" name="Answers[0].Id" value="1" />
</li>
<li>
<input type="text" name="Answers[1].Name" value="Answer 2 Name" />
<input type="text" name="Answers[1].Id" value="2" />
</li>
<li>
<input type="text" name="Answers[2].Name" value="Answer 3 Name" />
<input type="text" name="Answers[2].Id" value="3" />
</li>
</ul>
</li>
</ul>
<input type="submit" />
</form>
And also:
<form action="/question" method="post">
<ul>
<li>
<input type="text" name="Name" value="Question Name" />
<input type="text" name="Id" value="1" />
</li>
<li>
<ul>
<li>
<input type="text" name="Question[Answers][0].Name" value="Answer 1 Name" />
<input type="text" name="Question[Answers][0].Id" value="1" />
</li>
<li>
<input type="text" name="Question[Answers][1].Name" value="Answer 2 Name" />
<input type="text" name="Question[Answers][1].Id" value="2" />
</li>
<li>
<input type="text" name="Question[Answers][2].Name" value="Answer 3 Name" />
<input type="text" name="Question[Answers][2].Id" value="3" />
</li>
</ul>
</li>
</ul>
<input type="submit" />
</form>
But it doesn't bind collection of Answers to the Question object while question's Name and Id are binded correctly.
What is wrong with it and how to make this nested collection binded correctly?
Later I'll try binding a collection of comments to both Question and Answer objects.
There does appear to be a bug somewhere.
It doesn't seem to bind the List & List as they are null.
Also if you do this:
<form action="/" method="post">
<ul>
<li>
<input type="text" name="Name[0]" value="Question Name" />
<input type="text" name="Id[0]" value="1" />
<input type="text" name="Name[1]" value="Question Name2" />
<input type="text" name="Id[1]" value="2" />
</li>
<ul>
</form>
Then bind with:
Post["/"] = parameters =>
{
var model = this.Bind<List<Question>>();
return 200;
};
It will give you 2 items with the correct values.
Once you add the Answers inputs back in it fails to bind the Question.
Please log an issue at the repository - https://github.com/NancyFx/Nancy/issues?milestone=&page=1&state=open
If you feel like investigating and sending a pull request even better :)
Related
I have a react component with a redux form as below
<div className="col-sm-12">
<div className="form-group row">
<div className="col-sm-4">
<label>A. Name</label>
</div>
<div className="col-sm-8">
<ul className="sub-qn-ans-list d-flex list-unstyled">
<li>
<Field
name="first_name"
component={renderField}
type="text"
placeholder="First name"
className="form-control" />
</li>
<li>
<Field
name="last_name"
component={renderField}
placeholder="Last name"
type="text"
className="form-control" />
</li>
</ul>
</div>
</div>
<div className="form-group row">
<div className="col-sm-4">
<label>B. Residential Address</label>
</div>
<div className="col-sm-8">
<ul className="sub-qn-ans-list d-flex list-unstyled">
<li>
<Field
name="residential.street"
component={renderField}
placeholder="Street"
type="text"
className="form-control" />
</li>
<li>
<Field
name="residential.area"
component={renderField}
placeholder="Area"
type="text"
className="form-control" />
</li>
<li>
<Field
name="residential.city"
component={renderField}
placeholder="City"
type="text"
className="form-control" />
</li>
<li>
<Field
name="residential.district"
component={renderField}
placeholder="District"
type="text"
className="form-control" />
</li>
<li>
<Field
name="residential.landline"
component={renderField}
placeholder="Landline"
type="text"
className="form-control" />
</li>
<li>
<Field
name="residential.mobile"
component={renderField}
placeholder="Mobile"
type="text"
className="form-control" />
</li>
</ul>
</div>
</div>
</div>
I'm trying to apply validations for the input fields. The validation for the fields first_name and last_name works fine. But I get an error when I'm trying to apply validation for residential.street.
Below is my code for the validation.
const LocatorValidation = (values, form) => {
const errors = {};
if (!values.first_name) {
errors.first_name = 'Required';
} else if (!/[A-Za-z.]+/.test(values.first_name)) {
errors.first_name = 'First name must have only alphabets';
}
if (!values.last_name) {
errors.last_name = 'Required';
} else if (!/^[A-Za-z.]+$/.test(values.last_name)) {
errors.last_name = 'First name must have only alphabets';
}
if (!values.residential) {
errors.residential.street = 'Required';
}
return errors;
}
export default LocatorValidation;
Here is the image which shows the error.
As the error states in errors.residential.street the residential property of errors is undefined. So you need to set it as empty object first like
errors.residential = {}
errors.residential.street = "Required"
I would also improve the check
!values.residential || !values.residential.street
i am developing angular app with webapi 2, have input form it contains input values. i encapsulate input values into an object "ALBUM" pass to angular post method
enter code here
<form>
<div class="form-group row">
<label for="inputAName" class="col-sm-3 col-form-label">Album Name</label>
<div class="col-sm-8">
<input type="text" class="form-control" id="inputEmail3" placeholder="AlbumName" ng-model="album.Albm_Name">
</div>
</div>
<div class="form-group row">
<label for="inputPassword3" class="col-sm-3 col-form-label">Music Artist</label>
<div class="col-sm-8">
<select ng-model="album.Aritist_id">
<option ng-repeat="Artist in Artists" value="{{Artist.Artist_id}}">{{Artist.Artist_Name}}</option>
</select>
</div>
</div>
<div class="form-group row">
<label for="inputRdate" class="col-sm-3 col-form-label">Released Date</label>
<div class="col-sm-8">
<input type="date" class="form-control" id="inputPassword3" placeholder="date" ng-model="album.RelaeseDate">
</div>
</div>
<div class="form-group row">
<label for="inputImg" class="col-sm-3 col-form-label">Image of Album</label>
<div class="col-sm-8">
<input type="file" class="form-control-file" id="exampleInputFile" aria-describedby="fileHelp" name="file"
ng-model="album.picture" base-sixty-four-input required onload="onLoad" maxsize="500" accept="image/*">
</div>
</div>for
<div class="form-group row">
<div class="col-sm-offset-9 col-sm-2">
<button type="submit" class="btn btn-primary" ng-click="insertAlbum(album)">Save</button>
</div>
</div>
</form>
above code , ng-model='album.picture' return an object which contains more values "attached image file"
i need to filter album.picture only contain base64 string value, how do i do
without breaking the album object from view
$scope.insertAlbum = function (album) {
var urlAlbum = 'http://localhost:8090/api/album';
dataService.insertObject(urlAlbum, album).then(function (responce) {
alert("Success");
}, function (eror) {
alert(eror.message);
});
}
public partial class tblAlbum
{
public tblAlbum()
{
this.tblTracks = new HashSet<tblTrack>();
}
public int Albm_id { get; set; }
public Nullable<int> Aritist_id { get; set; }
public string Albm_Name { get; set; }
public Nullable<System.DateTime> RelaeseDate { get; set; }
public byte[] picture { get; set; }
public virtual tblArtist tblArtist { get; set; }
public virtual ICollection<tblTrack> tblTracks { get; set; }
}
just i found a solution
here it is :
$scope.insertAlbum = function (album) {
var urlAlbum = 'http://localhost:8090/api/album';
album.picture = $scope.file.base64;
dataService.insertObject(urlAlbum, album).then(function (responce) {
alert("Success");
}, function (eror) {
alert(eror.message);
});
}
manually i created an picture property and asigned views input value using $scope
I am using Angularjs with Asp.net MVC. As I am new in Angularjs I am facing some issues using it with MVC. Presently I want to validate a form. I am using --
1.For client side: Angularjs
2.For Server side :
Asp.net MVC default validation technic using data annotation.
will check the ModelState IsValid after form submit.
ModelState.IsValid is working only when the name tag for each html element is exactly the same as the property name of my model.
Is there any other way for server side validation while using MVC with angularjs as when we have a big form full of controls then we have to take care for each element so that the name tag remains same with the respective property name of my model.
My Html:
#model AngularMVC.Models.StudentModel
#{
ViewBag.Title = "student";
}
#using (Html.BeginForm("Submit", "Test", FormMethod.Post, new { name = "addstudent" }))
{
<div class="row">
<section>
<div class="form-group">
<div class="col-md-2">Name:</div>
<div class="col-md-10">
<div class="col-md-4">
<input class="form-control" name="Name" id="Name" type="text" placeholder="Enter Name" ng-model="Name" ng-required="true" />
</div>
<div class="col-md-4">
<span style="color:red" ng-show="addstudent.Name.$invalid">
<span ng-show="addstudent.Name.$dirty && addstudent.Name.$invalid">Name is required.</span>
</span>
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-2">Mobile Number:</div>
<div class="col-md-10">
<div class="col-md-4">
<input class="form-control" type="text" placeholder="Enter Mobile Number" name="MobileNumber" ng-model="MobileNumber" ng-pattern="/^\d{0,9}(\.\d{1,9})?$/" />
</div>
<div class="col-md-4">
<span style="color:red" ng-show="addstudent.MobileNumber.$invalid">
<span ng-show="addstudent.MobileNumber.$dirty && addstudent.MobileNumber.$invalid">Mobile number is required.</span>
<span ng-show="addstudent.MobileNumber.$dirty && addstudent.MobileNumber.$error.pattern">Not a valid mobile number!</span>
</span>
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-2">Email:</div>
<div class="col-md-10">
<div class="col-md-4">
<input class="form-control" type="email" name="EmailAddress" placeholder="Enter email" ng-model="email" />
</div>
<div class="col-md-4">
<span style="color:red" ng-show="addstudent.EmailAddress.$invalid">
<span ng-show="addstudent.EmailAddress.$dirty && addstudent.EmailAddress.$invalid">email is required.</span>
<span ng-show="addstudent.EmailAddress.$dirty && addstudent.EmailAddress.$error.pattern">Not a valid email address!</span>
</span>
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-2">Address:</div>
<div class="col-md-10">
<div class="col-md-4">
<input class="form-control" type="text" placeholder="Enter Addess" name="Address" ng-model="address" ng-required="true" />
</div>
<div class="col-md-4">
<span style="color:red" ng-show="addstudent.Address.$invalid">
<span ng-show="addstudent.Address.$dirty && addstudent.Address.$invalid">Address is required.</span>
</span>
</div>
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Add Student" class="btn btn-default" />
</div>
</div>
</div>
</section>
</div>
}
<script src="~/Scripts/ng-module.js"></script>
Student Model:
public class StudentModel
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[RegularExpression(#"/^\d{0,9}(\.\d{1,9})?$/")]
public long MobileNumber { get; set; }
[RegularExpression("^[a-zA-Z0-9_\\.-]+#([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$")]
public string EmailAddress { get; set; }
[Required]
public string Address { get; set; }
}
Instead of
input class="form-control" name="Name"
you can use
#Html.TextBoxFor(r=>r.Name)
In that way , you will not care about names. IntelliSense will do all work for you.
How can I make the ng-model of an input dynamic?
Static ng-model:
<input type="text" ng-model="myModel.firstName" />
Dynamic ng-model:
$scope.myInputs = [{ key: "firstName"}, { key: "lastName" }];
<div ng-repeat="input in myInputs">
<input type="text" ng-model="myModel[input.key]" />
</div>
The myModel[input.key] does not seem to be calculating correctly.
In myInputs[input.key], input.key is not the index. So you cannot access expected value.
You can either
<div ng-repeat="input in myInputs">
<input type="text" ng-model="input.key" />
</div>
Or
<div ng-repeat="input in myInputs track by $index">
<input type="text" ng-model="myInputs[$index].key" />
</div>
We have a somewhat complicated model on an AngularJS controller:
function controller($scope) {
$scope.model = {
childmodel1: {
childmodel2: {
property1: 'abc',
property2: 3,
property3: 0.5,
property4: 'abc'
}
}
}
}
In the HTML markup, we don't want to repeat the whole access chain everytime we access childmodel2:
<div ng-controller="ctrl">
<div>
<input type="text" ng-model="model.childmodel1.childmodel2.property1" />
<input type="text" ng-model="model.childmodel1.childmodel2.property2" />
<input type="text" ng-model="model.childmodel1.childmodel2.property3" />
<input type="text" ng-model="model.childmodel1.childmodel2.property4" />
</div>
</div>
Is there an AngularJS directive that creates a sub-scope like this:
<div ng-controller="ctrl">
<div ng-unknowndirective="model.childmodel1.childmodel2">
<input type="text" ng-model="property1" />
<input type="text" ng-model="property2" />
<input type="text" ng-model="property3" />
<input type="text" ng-model="property4" />
</div>
</div>
It's the same thing that's done on ng-repeat, but without the repetition :)
We tried ng-scope, ng-controller, ng-model, none of them works this way. Googling didn't yield any results, we don't know the terminology to search for.
Thank you #Ufuk, here's my solution:
mt.directive('subscope', function () {
return {
scope: {
subscope: '='
}
};
});
and
<div ng-controller="ctrl">
<div subscope="model.childmodel1.childmodel2">
<input type="text" ng-model="subscope.property1" />
<input type="text" ng-model="subscope.property2" />
<input type="text" ng-model="subscope.property3" />
<input type="text" ng-model="subscope.property4" />
</div>
</div>
You can use ng-init to suppress the access chain
<div ng-init="childmodel2 = model.childmodel1.childmodel2">
<input type="text" ng-model="childmodel2.property1" />
<input type="text" ng-model="childmodel2.property2" />
<input type="text" ng-model="childmodel2.property3" />
<input type="text" ng-model="childmodel2.property4" />
</div>
It creates alias model. More appropriate you create the alias in your controller like
$scope.childmodel2 = $scope.model.childmodel1.childmodel2 // and remove ng-init from HTML
Demo