Pedre
Pedre

Reputation: 21

Nancy Binding to Models with same Properties

I have a Nancy Service that render a View. The View contains two Models, Employee and Person. Both these models have a Property called Name. When these values get Posted back it seems Nancy takes the first Bind<> and applies it to both the Properties with the same name. Is there a way to bind the Input to a specific model other than what I have done below?

This is the Module.

public class IndexModule : NancyModule
{
    public IndexModule()
        : base("/")
    {
        Get["/"] = parameters =>
        {
            var returnModel = new ReturnModel();

            return View["index.cshtml", returnModel];
        };

        Post["/"] = parameters =>
        {
            var person = this.Bind<Person>();      //Name="Name"
            var employee = this.Bind<Employee>();  //Name="Name", should be "empName"
            return 200;
        };
    }
}

public class ReturnModel
{
    public Person PersonModel;
    public Employee EmployeeModel;

    public ReturnModel()
    {
        PersonModel = new Person();
        EmployeeModel = new Employee();
        PersonModel.Name = "Name";
        EmployeeModel.Name = "empName";
    }
}

HTML View

@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<NancyTestSite.Modules.ReturnModel>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
</head>
<body>
<form method="POST">
    <input name="@(Model.EmployeeModel.Name)" type="text" value="@(Model.EmployeeModel.Name)">
    <input name="@(Model.PersonModel.Name)" type="text" value="@(Model.PersonModel.Name)">
    <button type="submit">Submit</button>
</form>

Upvotes: 1

Views: 784

Answers (1)

Nehmick
Nehmick

Reputation: 21

Nancy's ModelBinding works by binding the 'name'-attribute of the html-input to the name of a property in the given model.

When you are doing this...

<input name="@(Model.PersonModel.Name)" type="text" value="@(Model.PersonModel.Name)">

... the viewengine translates it to this:

<input name="Name" type="text" value="Name">

It does so because you set PersonModel.Name to "Name" in your ReturnModel constructor and @(Model.PersonModel.Name) simply gets the value of Model.PersonModel.Name. Since you set EmployeeModel.Name = "empName" the other input would look like this:

<input name="empName" type="text" value="empName">

So when you are posting your form-data, there are two inputs that should look somewhat like this:

empName = "empName"
Name = "Name"

When you are then calling this.Bind<Person>() and this.Bind<Employee>, Nancy notices you have a property called "Name" in those classes and therefore tries to find an input called "Name" in the form-data (it does so with every public property). Since there is indeed a "Name" field in your form-data, Nancy sets the Name property to the given value, leading to your person and your employee both having their Name properties set to "Name".

As far as i know it is not possible to bind an input to a specific model (by default). But you could simply do something like this:

<input name="EmployeeName" type="text" value="@(Model.EmployeeModel.Name)">
<input name="PersonName" type="text" value="@(Model.PersonModel.Name)">

So the form data that is sent to the server contains something like this:

EmployeeName = "empName"
PersonName = "Name"

Finally, in your controller, you do this:

Post["/"] = parameters =>
    {
        var person = new Person();
        person.Name = this.Request.Form["PersonName"];

        var employee = new Employee();
        employee.Name = this.Request.Form["EmployeeName"];

        return 200;
    };

I hope this helps to understand how model-binding and viewengine work.

Upvotes: 2

Related Questions