Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Heather Floyd 609 posts 1030 karma points MVP 6x c-trib
    Apr 15, 2015 @ 18:53
    Heather Floyd
    0

    Mixing strongly-typed ASP.Net MVC Forms with Angular Bits - Is it possible?

    We are building several front-end forms for a website project.

    For simpler forms I have been able to utilize strongly-typed MVC forms (akin to this example: http://umbraco.com/follow-us/blog-archive/2013/7/14/moving-from-webforms-to-mvc)

    Via C# I have a form data model defined with all the fields needed...

    (example snippets):

    public class MyForm
    {
        [Required]
        public string Name { get; set; }
    
        public string Email { get; set; }
    }
    

    a .cshtml partial view which utilizes that model and HTML Helpers to render the fields...

    (example snippets):

    using (Html.BeginUmbracoForm<MyFormController>("HandleSubmit", FormMethod.Post)) 
    {
        <label>
            <span>Full Name</span>
            @Html.EditorFor(x => Model.Name, new { @type = "text" })
        </label>
        <label>
            <span>Email</span>
            @Html.EditorFor(x => Model.Email, new { @type = "email" })
        </label>
    }
    

    and a controller which processes the form data...

    (example snippets):

    public class MyFormController : SurfaceRenderMvcController
    {
        ...
    
        [HttpPost]
        [ActionName("HandleSubmit")]
        public ActionResult HandleSubmit(MyForm model)
        {
            if (ModelState.IsValid)
            {
                //Do stuff with model data...
                var name = model.Name;
                var email = model.Email;
                //etc. ...
            }
        }
    }
    

    This all works beautifully.

    However for some forms, we want to add a bit of front-end UI-fanciness, for instance, a postal code textbox which looks up the city and state and auto-populates those fields in the form.

    The question is...

    How can we use a little bit of Angular on the front-end, without "breaking" the ASP.Net MVC model-binding, which otherwise works very nicely?

    Is it possible to somehow "decorate" the HtmlHelpers with proper angular attributes for interaction? Or is there some better way for the front-end and back-end to "play nice" - without necessarily converting the entire view to angular with a webapi post at the end?

    Thanks in advance for any thoughts!

    ~Heather

  • Jeroen Breuer 4908 posts 12265 karma points MVP 5x admin c-trib
    Apr 16, 2015 @ 10:31
    Jeroen Breuer
    1

    Hello,

    It should be possible to just keep everything as it is and add a little Angular that only does a few things. For example the form could look like this:

    @using (Html.BeginUmbracoForm("SendOrderCoinsAndBills", "OrderCoinsAndBills", null, new { @id = "orderForm", ng_controller = "OrderController" }))

    Then you can do some nice Angular stuff:

    <tr ng-repeat="coin in coins" ng-show="coin.allowed" class="coinscount">
        <td ng-model="coin.image"><img ng-src="/images/{{coin.image}}" /></td>
    </tr>

    You can also use Angular to change some input fields. As long as the name attribute is the same as the model property you can still just post it back to your SurfaceController.

    Jeroen

  • Heather Floyd 609 posts 1030 karma points MVP 6x c-trib
    Apr 16, 2015 @ 15:45
    Heather Floyd
    0

    Thanks, Jeroen,

    As soon as our Angular dev gets back from vacation, I'll have him try it and let you know how it goes :-)

    ~Heather

  • Heather Floyd 609 posts 1030 karma points MVP 6x c-trib
    Apr 28, 2015 @ 06:53
    Heather Floyd
    0

    Hi Jeroen,

    So, I was doing some experimenting with this...

    The angular fancy-stuff we have is a tool which, if you enter a zip-code, will lookup the city/state and pre-populate that data.

    My Angular guy has it setup like this:

    using (Html.BeginUmbracoForm("HandleSubmit", "AddEditLocationForm", null, new { @class = "row" }))
    {
        ...some other fields...
        <div data-ng-controller="CustomCode.PostalCodeController">
            <div class="row">
                <label class="col-s-3 col-xs-4 col-xxs-8">
                    <span>Postal Code</span>
                    @*@Html.EditorFor(x => Model.PostalCode, new { @type = "text" })*@
                    <input type="text" name="PostalCode" data-ng-model="postalCode" />
                </label>
    
                <span class="col-xs-3 col-xxs-4">
                    <button type="button" class="next" data-ng-click="getCitiesByPostalCode()" style="margin-top: 2em;">Search</button>
                </span>
                <span class="help-alt city-state col-s-6 col-xs-5 col-xxs-12" data-ng-show="!hasCitiesList()">Enter POSTAL CODE for City & State</span>
                <label class="col-s-6 col-xs-8 col-xxs-12 city-state" data-ng-show="hasCitiesList()">
                    <select data-ng-model="selectedCity" data-ng-options="city for city in cities" data-ng-change="checkIfManuallyNeedToEnterCity()"></select>
                </label>
            </div>
    
            <div class="row" data-ng-show="needsManualCityEntry">
                <label class="col-s-8 col-xs-6 col-xxs-12">
                    <span>City</span>
                    @*@Html.EditorFor(x => Model.Locality, new { @type = "text" })*@
                    <input type="text" name="Locality" data-ng-model="locality" />
                </label>
                <label class="col-s-4 col-xs-6 col-xxs-12">
                    <span>State</span>
                    @*@Html.EditorFor(x => Model.Region, new { @type = "text" })*@
                    <select name="Region" regiondata-ng-model="selectedState" data-ng-options="state.name for state in states"></select>
                </label>
            </div>
        </div> 
        ...some more fields & a submit button...
    }
    

    (You can see that I have commented-out the standard Html.EditorFor() controls that I would use if we didn't have the angular.)

    I added name="{My model property}" data to the standard input controls. I was hoping that would magically make it work, but unfortunately, the data I have made available in the model (via an initial RenderForm action on the controller) doesn't populate for those few address fields. It doesn't recognize the connection between the model.PostalCode and the input name="PostalCode".

    I also realized that I couldn't handle it the other way around:

    @Html.EditorFor(x => Model.PostalCode, new { @type = "text", data-ng-model="postalCode" })
    

    causes a compile error at data-ng-model= due to the dashes. Similarly, this also doesn't work:

    @Html.EditorFor(x => Model.PostalCode, new { @type = "text", @data-ng-model="postalCode" })
    

    Could you explain a bit more specifically what we'd need to do to make this work both ways?

    Thanks so much!

    ~Heather

  • MK 429 posts 906 karma points
    Apr 28, 2015 @ 07:01
    MK
    0

    Try the following: @data_ng_model

  • Heather Floyd 609 posts 1030 karma points MVP 6x c-trib
    Apr 28, 2015 @ 21:58
    Heather Floyd
    0

    Thanks, Moshe.

    I changed it to :

    @Html.EditorFor(x => Model.PostalCode, new { @type = "text", @data_ng_model="postalCode" })
    

    Which doesn't cause compiling issues... and when the page renders, I see the value in the postal code field... but the Angular attribute isn't present in the rendered markup which is this:

    <input class="text-box single-line" data-val="true" data-val-required="LocationPostalCodeRequired" id="PostalCode" name="PostalCode" type="text" value="97013">
    

    So... still not quite there.

    ~Heather

    Ps. Similar result if I leave the "@" off:

    @Html.EditorFor(x => Model.PostalCode, new { @type = "text", data_ng_model="postalCode" })
    
  • Heather Floyd 609 posts 1030 karma points MVP 6x c-trib
    Apr 30, 2015 @ 03:13
    Heather Floyd
    102

    So, after some help from @ClausJnsn who provided this snippet ...

    And some reading of the TemplateHelper source code ...

    I've now got this rendering properly using a custom editor template:

    \Views\Shared\EditorTemplates\ng.cshtml

    @using System.Text
    
    @model object
    
    @{
        ModelMetadata modelMetadata = ViewData.ModelMetadata;
        StringBuilder builder = new StringBuilder();
    
        //Check type
        var inputType = "text";
        if (ViewData["type"] != null)
        {
            inputType = ViewData["type"].ToString();
        }
        else
        {
            if (Model is string)
            {
                inputType = "text";
            }
            else if (Model is int)
            {
                inputType = "number";
            }
        }
    
        //ng data
        var ngModel = ViewData["ng_model"].ToString();
    
        //Name & ID attributes
        var modelFieldName = modelMetadata.PropertyName;
        var idAttrib = modelFieldName;
        if (ViewData["id"] != null)
        {
            idAttrib = ViewData["id"].ToString();
        }
    
        builder.AppendFormat("<input type=\"{0}\"  name=\"{3}\" ng-model=\"{1}\" ng-init=\"{1}='{2}'\" value=\"{2}\" id=\"{4}\" ", inputType, ngModel, Model, modelFieldName, idAttrib);
    
        if (ViewData["class"] != null)
        {
            builder.AppendFormat("class=\"{0}\"", ViewData["class"].ToString());
        }
    
        builder.AppendFormat(" />");
    }
    
    @Html.Raw(builder.ToString())
    

    Then I have updated my form view to this:

     @Html.EditorFor(x => Model.PostalCode, "ng", new { @type = "hidden", @ng_model = "address.postalCode" })
    

    Which renders this:

    <input type="hidden" name="PostalCode" ng-model="address.postalCode" ng-init="address.postalCode='97013'" value="97013" id="PostalCode"/>
    

    (They're hidden fields because my Angular guy is going to bind it to his own visible controls so the data gets updated and the value is passed back via the MVC model. (At least that's the plan ;-) )

    I hope this code will be useful to someone else.

    Thanks everyone for your help!

    ~Heather

Please Sign in or register to post replies

Write your reply to:

Draft