Angular model binding with MVC Html.TextBoxFOr

2019-03-30 14:15发布

问题:

I have controls that are model tied to ASP.net MVC5

 @Html.TextBoxFor(model => model.OriginLocation.City, new { @class = "form-control", data_ng_model = "address1.City", test_change = "" })

So when the page loads the value of text box input is bound and should display value coming from service with Razor bound controls, latter i can manipulate that value which changes angular model for this control. What i have is textbox loads empty. I can see the value when I view source but its not displayed.

<input class="form-control ng-pristine ng-valid" data-ng-model="address1.City" data-val="true" data-val-length="The field City must be a string with a maximum length of 50." data-val-length-max="50" id="OriginLocation_City" name="OriginLocation.City" test-change="" type="text" value="Manheim">

js fragment

app.controller('LocationCtrl', ["$scope",
function ($scope) {
  $scope.address1 = { Label: 'address1' };

回答1:

ngModel has precedence over the value that is originally set (it's setting the value to "" because the model doesn't exist). Take a look here...

http://jsfiddle.net/yApeP/

But you can specify a value using ngInit...

http://jsfiddle.net/D7vh7/

Which means you can use ngInit when generating the textbox...

@Html.TextBoxFor(model => model.OriginLocation.City,
    new { @class = "form-control", 
         data_ng_model = "address1.City", 
         test_change = "",
         data_ng_init = string.Format("address1.City = '{0}'", Model.OriginLocation.City.Replace("'", @"\'"))  })


回答2:

Angular will replace the contents of the text box with the value from its own model, which means you need to populate the Angular model. This can be achieved quickly by serializing your MVC model (or part of it) into your Angular model:

app.controller("TestController", function($scope) {
    $scope.person = @Html.Raw(JsonConvert.SerializeObject(Model.Person));
});

You can then render your MVC controls in the view like this:

@Html.TextBoxFor(x => x.Person.Name, new { ng_model = "person.Name" })


回答3:

Considering the answer by @Alan, I found a way that is "kind of" clean. Well that is the solution that I am using when creating ASP MVC Projects with Angular.js.

Instead of serializing the model inside the Angular controller (which would require to declare the controller inside of the Razor template), I serialize it in a global variable in each of my razor template like :

<script>
    //Add the namespace you want to not poluate the global namespace
    window.Model = @Html.Raw(JsonConvert.SerializeObject(Model))
</script>

I also use the property attribute [ScriptIgnore] for the properties I don't want to serialize (that could cause circular references).

public class Classroom {
    public string Title {get;set;}

    [ScriptIgnore]
    //Students could be associated to classroom, and that classroom has  
    //  students etc. If you want those properties, create a new object in a 
    //  PageView withtout circular references
    public List<Students> Students {get;set;}
}

After that, inside of my controllers, I simply do something like

app.controller("TestController", function($scope) {
    $scope.model = window.Model;
    //My other stuff...
});

Also, that way your angular Model always have the SAME properties has your ASP MVC Model.



回答4:

One clean way to solve this life-cycle issue is by writing a directive for the "value" attribute for the input.

At first you can simply write the Razor's model value in yout input element by using the basic Razor syntax.

 <input type="text" class="form-control" id="name" name="name" value="@Model.Name" data-ng-model="myModel.name">

That will write the value of the @Model.Name in the input when the page is rendered, but this value will be replaced by empty values when Angular starts the bindings.

So you can write a directive, and on the "link" method you can play with the ngModel in order to keep the value that was present in the input before Angular cleans it. (The property 'prioriy: 0' indicates that the directive should be compiled as soon as possible)

function inputDirective(){
    return {
        restrict:'A',
        priority: 0,
        require: 'ngModel',
        link: function(scope, elem, attrs, ngModelCtrl){                      
                  ngModelCtrl.$setViewValue($(elem).val());
              }
    };
}

That should restore the value written by Razor in your Angular's model