Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Saving Form Data using Web API and AngularJS (select list)

I have a form that displays personnel data and offers the ability to update the personnel information and save the changes to my database. See form below:

Figure 1. Personnel Information Form

The fields that are bound to the textboxes I have no issues updating. Though, when it comes to the HTML select list (dropdown), I'm unable to save the newly selected value.

NOTE I can make a change in any number of textboxes and then make a change to the selection in the select list and the rest of the form saves properly, just without the changes to the select list, so it doesn't appear to be failing anywhere.

I'm not receiving any errors; however, I am seeing an additional OPTIONS request before the PUT, which I'm unsure about (so any additional tips on why that is would also be appreciated).

Here's my form:

<form name="personForm" novalidate ng-controller="PersonnelEditCtrl as vm">
<fieldset class="col-md-4">
    <legend>Basic Personnel Information</legend>
    <div class="form-group row" ng-class="{'has-error':personForm.inputLastName.$invalid && personForm.inputLastName.$dirty}">
        <label class="col-md-3 control-label" for="inputLastName">Last Name</label>
        <div class="col-md-4">
            <input class="form-control" id="inputLastName" name="inputLastName" 
                   type="text" placeholder="Last Name (required)" 
                   ng-model="vm.person.lastName" required ng-minlength="1" ng-maxlength="30" />
        </div>
        <span class="help-block" has-error">
            <span ng-show="personForm.inputLastName.$error.required">
                Last name is required.
            </span>
            <span ng-show="person.form.inputLastName.$error.minlength">
                Last name must be at least 1 character in length.
            </span>
            <span ng-show="person.form.inputLastName.$error.maxlength">
                Last name cannot exceed 30 characters in length.
            </span>
        </span>
    </div>
    <div class="form-group row" ng-class="{'has-error':personForm.inputFirstName.$invalid && personForm.inputFirstName.$dirty}">
        <label class="col-md-3 control-label" for="inputFirstName">First Name</label>
        <div class="col-md-4">
            <input class="form-control" id="inputFirstName" name="inputFirstName" 
                   type="text" placeholder="First Name (required)" 
                   ng-model="vm.person.firstName" required ng-minlength="1" ng-maxlength="30" />
        </div>
        <span class="help-block" has-error">
            <span ng-show="personForm.inputFirstName.$error.required">
                First name is required.
            </span>
            <span ng-show="person.form.inputFirstName.$error.minlength">
                First name must be at least 1 character in length.
            </span>
            <span ng-show="person.form.inputFirstName.$error.maxlength">
                First name cannot exceed 30 characters in length.
            </span>
        </span>
    </div>
    <div class="form-group row" ng-class="{'has-error':personForm.inputMiddleInitial.$invalid && personForm.inputMiddleInitial.$dirty}">
        <label class="col-md-3 control-label" for="inputMiddleInitial">Middle Initial</label>
        <div class="col-md-4">
            <input class="form-control" id="inputMiddleInitial" name="inputMiddleInitial" 
                   type="text" placeholder="Middle Initial (required)" 
                   ng-model="vm.person.middleInitial" required ng-minlength="1" ng-maxlength="1" />
        </div>
        <span class="help-block" has-error">
            <span ng-show="personForm.inputMiddleInitial.$error.required">
                Middle initial is required.
            </span>
            <span ng-show="person.form.inputMiddleInitial.$error.minlength">
                Middle initial must be at least 1 character in length.
            </span>
            <span ng-show="person.form.inputMiddleInitial.$error.maxlength">
                Middle initial cannot exceed 1 characters in length.
            </span>
        </span>
    </div>
    <div class="form-group row">
        <label class="col-md-3 control-label" for="inputDateOfBirth">Date of Birth</label>
        <div class="col-md-4">
            <input class="form-control" ng-model="vm.person.dob" type="date" />
        </div>
    </div>
    <div class="form-group row">
        <label class="col-md-3 control-label" for="selectPayband">Payband</label>
        <div class="col-md-4">

            <select id="selectPayband" name="selectPayband" 
                    ng-model="vm.person.payband" ng-options="payband.name for payband in vm.paybands track by payband.id">
            </select>
        </div>
    </div>
    <div class="form-group row">
        <div class="col-md-4">
            <span>
                <button class="btn btn-primary" style="width:80px; margin-right:10px" 
                        ng-click="vm.submit()" ng-disabled="personForm.$invalid">Save</button>
            </span>
            <span>
                <button class="btn btn-default" style="width:70px" 
                        ng-click="vm.cancel(personForm)">Cancel</button>
            </span>
        </div>
    </div>
    <div class="form-group row" ng-show="vm.message">
        <div class="col-md-6">
            <pre style="font: inherit">{{ vm.message }}</pre>
        </div>
    </div>
</fieldset>

personnelEditCtrl.js

angular
.module("personnelService")
.controller("PersonnelEditCtrl",
             PersonnelEditCtrl);

function PersonnelEditCtrl(personnelResource, paybandResource, $filter) {
var vm = this;
vm.person = {};
vm.message = '';
vm.paybands = [];

paybandResource.query(function (data) {
    vm.paybands = $filter('orderBy')(data, 'Name');
});

personnelResource.get({ id: 2 },
    function (data) {
        vm.person = data;
        vm.person.dob = new Date(vm.person.dob);
        vm.originalPerson = angular.copy(data);
    });

if (vm.person && vm.person.personId) {
    vm.title = "Edit: " + vm.person.firstName + " " + vm.person.lastName;
}
else {
    vm.title = "New Person";
}

vm.submit = function () {
    vm.message = '';
    if (vm.person.personId) {
        vm.person.$update({ id: vm.person.personId },
            function (data) {
                vm.message = '... Save Complete';
            })
    }
    else {
        vm.person.$save(
            function (data) {
                vm.originalPerson = angular.copy(data);
                vm.message = '... Save Complete';
            })
    }
};

vm.cancel = function (editForm) {
    editForm.$setPristine();
    vm.person = angular.copy(vm.originalPerson);
    vm.message = "";
};
}

personnelResource.js

(function () {
"use strict";

angular
    .module("common.services")
    .factory("personnelResource",
            ["$resource",
             "appSettings",
                personnelResource])

function personnelResource($resource, appSettings) {
    return $resource(appSettings.serverPath + "/api/people/:id", null,
        {
            'update':{method:'PUT'}
        });
}
}());

paybandResource.js

(function () {
"use strict";

angular
    .module("common.services")
    .factory("paybandResource",
            ["$resource",
             "appSettings",
                paybandResource])

function paybandResource($resource, appSettings) {
    return $resource(appSettings.serverPath + "/api/paybands/:id");
}
}());

Database Structure:

dbo.People

PersonId : int (PK)
FirstName : string
MiddleInitial: string
LastName : string
DateOfBirth: datetime
PaybandId : int (FK)

dbo.Paybands

Id : int (PK)
Name : string

It probably has nothing to do with it, but I'll post my Web API Controller code for "People" as well:

using System.Linq;
using System.Web.Http;
using CPS.WebAPI.Models;
using System.Web.Http.Cors;
using System.Data.Entity;

namespace CPS.WebAPI.Controllers
{
[EnableCorsAttribute("http://localhost:53265", "*", "*")]
public class PeopleController : ApiController
{
    private CPS_Context db = new CPS_Context();

    public IQueryable<Person> GetPeople()
    {
        return db.Person;
    }

    public Person Get(int id)
    {
        Person person;

        if (id > 0)
        {
            var people = db.Person;
            person = people.FirstOrDefault(p => p.PersonId == id);
        }
        else
        {
            person = db.Person.Create();
        }

        return person;
    }

    public void Post([FromBody]Person person)
    {
        CPS_Context db = new CPS_Context();
        var newPerson = db.Person.Add(person);
        db.SaveChanges();
    }

    public void Put(int id, [FromBody]Person person)
    {
        CPS_Context db = new CPS_Context();
        db.Entry(person).State = EntityState.Modified;
        var updatedPerson = db.SaveChanges();
    }

    public void Delete(int id)
    {

    }
}
}

Don't mind the bare minimum code for the Web API, I'm just doing everything from scratch and doing everything as minimal as possible to get it working in the mean time.

Thank you very much for any help you can provide in helping me to save the selected option in my select list. Please let me know if you have any further questions or need any more information - I tried to be as thorough as possible. Thanks again in advance for any help anyone can provide!

like image 380
Element808 Avatar asked Feb 13 '17 07:02

Element808


1 Answers

My issue turned out to be that JSON data included Navigation Properties. The update failed because there was a referential integrity conflict.

The solution to my problem was to update my Entity Framework data model to exclude navigation properties from JSON. I did this by adding [JsonIgnore] attribute like this:

[ForeignKey("Payband")]
public virtual int PaybandId { get; set; }

[JsonIgnore]
public virtual Payband Payband { get; set; }
like image 67
Element808 Avatar answered Oct 18 '22 02:10

Element808