Stas Svishov
Stas Svishov

Reputation: 531

Angular model binding with MVC Html.TextBoxFOr

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' };

Upvotes: 4

Views: 27136

Answers (4)

Gus Rodriguez
Gus Rodriguez

Reputation: 61

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

Upvotes: 0

RPDeshaies
RPDeshaies

Reputation: 1874

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.

Upvotes: 2

Alan
Alan

Reputation: 3002

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" })

Upvotes: 2

Anthony Chu
Anthony Chu

Reputation: 37520

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("'", @"\'"))  })

Upvotes: 19

Related Questions