Adding this feature requires a few steps:

    • Adding JavaScript that will send the data to the server
    • Creating a new action on the controller to handle this request
    • Adding code to the service layer to update the database

    The view already includes an HTML form that has a textbox and a button for adding a new item. You’ll use jQuery to send a POST request to the server when the Add button is clicked.

    Open the wwwroot/js/site.js file and add this code:

    Then, write the addItem function at the bottom of the file:

    1. function addItem() {
    2. $('#add-item-error').hide();
    3. var newTitle = $('#add-item-title').val();
    4. $.post('/Todo/AddItem', { title: newTitle }, function() {
    5. window.location = '/Todo';
    6. })
    7. .fail(function(data) {
    8. if (data && data.responseJSON) {
    9. var firstError = data.responseJSON[Object.keys(data.responseJSON)[0]];
    10. $('#add-item-error').text(firstError);
    11. $('#add-item-error').show();
    12. }
    13. });
    14. }

    This function will send a POST request to http://localhost:5000/Todo/AddItem with the name the user typed. The third parameter passed to the $.post method (the function) is a success handler that will run if the server responds with 200 OK. The success handler function uses window.location to refresh the page (by setting the location to /Todo, the same page the browser is currently on). If the server responds with 400 Bad Request, the fail handler attached to the $.post method will try to pull out an error message and display it in a the <div> with id add-item-error.

    The above JavaScript code won’t work yet, because there isn’t any action that can handle the /Todo/AddItem route. If you try it now, ASP.NET Core will return a 404 Not Found error.

    You’ll need to create a new action called AddItem on the TodoController:

    1. {
    2. {
    3. return BadRequest(ModelState);
    4. }
    5. var successful = await _todoItemService.AddItemAsync(newItem);
    6. if (!successful)
    7. {
    8. return BadRequest(new { error = "Could not add item" });
    9. }
    10. return Ok();
    11. }

    Models/NewTodoItem.cs

    This model definition (one property called Title) matches the data you’re sending to the action with jQuery:

    1. $.post('/Todo/AddItem', { title: newTitle } // ...
    2. // A JSON object with one property:
    3. // {
    4. // title: (whatever the user typed)
    5. // }

    ASP.NET Core uses a process called model binding to match up the parameters submitted in the POST request to the model definition you created. If the parameter names match (ignoring things like case), the request data will be placed into the model.

    After binding the request data to the model, ASP.NET Core also performs model validation. The [Required] attribute on the Title property informs the validator that the Title property should not be missing (blank). The validator won’t throw an error if the model fails validation, but the validation status will be saved so you can check it in the controller.

    Back to the AddItem action method on the TodoController: the first block checks whether the model passed the model validation process. It’s customary to do this right at the beginning of the method:

    1. if (!ModelState.IsValid)
    2. {
    3. }

    If the is invalid (because the required property is empty), the action will return 400 Bad Request along with the model state, which is automatically converted into an error message that tells the user what is wrong.

    Next, the controller calls into the service layer to do the actual database operation:

    Finally, if everything completed without errors, the action returns 200 OK.

    If you’re using a code editor that understands C#, you’ll see red squiggely lines under AddItemAsync because the method doesn’t exist yet.

    As a last step, you need to add a method to the service layer. First, add it to the interface definition in ITodoItemService:

    1. public interface ITodoItemService
    2. {
    3. Task<IEnumerable<TodoItem>> GetIncompleteItemsAsync();
    4. Task<bool> AddItemAsync(NewTodoItem newItem);
    5. }

    Then, the actual implementation in TodoItemService:

    1. public async Task<bool> AddItemAsync(NewTodoItem newItem)
    2. {
    3. var entity = new TodoItem
    4. {
    5. Id = Guid.NewGuid(),
    6. IsDone = false,
    7. Title = newItem.Title,
    8. DueAt = DateTimeOffset.Now.AddDays(3)
    9. };
    10. _context.Items.Add(entity);
    11. var saveResult = await _context.SaveChangesAsync();
    12. return saveResult == 1;
    13. }

    This method creates a new TodoItem (the model that represents the database entity) and copies the Title from the NewTodoItem model. Then, it adds it to the context and uses to persist the entity in the database.

    Run the application and add some items to your to-do list with the form. Since the items are being stored in the database, they’ll still be there even after you stop and start the application again.