Tag Archives: Model

Building a list with Asp.Net Core

I’ve recently been working with Asp.Net Core to build some functionality, involving building a list of values. Typically, with Asp.Net Core using Razor, you have a form that may look something like this:

@using (Html.BeginForm("MyAction", "ControllerName", FormMethod.Post)
{
    @Html.AntiForgeryToken()
    <div class="form-group">
        @Html.LabelFor(model => model.MyValue)
        @Html.TextBoxFor(model => model.MyValue)
    </div>

    <div class="form-group">
        <button type="submit">Submit</button>
    </div>

This works really well in 90% of cases, where you want the user to enter a value and submit. This is your average CRUD application; however, what happens if, for some reason, you need to manipulate one of these values? Let’s say, for example, that you want to submit a list of values.

For the sake of simplicity, we’ll say that the controller accepts a csv, but we want to build this up before submission. You can’t simply call a controller method for two reasons: the first is that the controller will reload the page; and the second that you don’t have anywhere to put the value on the server. If this was, say, a method to create an entry in the DB, the DB entry, by definition, couldn’t exist until after the submission.

This all means that you would need to build this list on the client.

A solution

Let’s start with a very simple little feature of Html Helpers – the hidden field:

@Html.HiddenFor(model => model.MyList)

This means that we can store the value being submitted to the user, without showing it to the user.

We’ll now need to display the data being added. An easy way to do this is a very simple table (you can load existing values into the table for edit scenarios):

    <div>
        <table id="listTable">
            <tbody>
                @if ((Model?.ValueList ?? null) != null)
                {
                    @foreach (var v in Model.ValueList)
                    {
                        <tr>
                            <td>@v</td>
                        </tr>
                    }
                }
            </tbody>
        </table>
    </div>    

Pay particular attention to the Table Id and the fact that the conditional check is inside the tbody tag. Now let’s allow the user to add a new piece of data:

    <div class="form-group">
        @Html.LabelFor(model => model.NewValue)
        @Html.TextBoxFor(model => model.NewValue)
    </div>
    <div>
        <button type="button" id="add-value">Add Value</button>
    </div>

Okay, so now we have a button and a field to add the value; we also have a method of displaying those values. We’ll need a little bit of Javascript (JQuery in this case) to append to our list:

@section Scripts {
        $('#add-value').click(() => {

            const hiddenList = $('#MyList');
            const newValue = $('#NewValue');

            if (!hiddenList.val()) {
                hiddenList.val(newValue.val());
            } else {
                hiddenList.val(hiddenList.val() + ',' + newValue.val());
            }
            
            $('#listTable > tbody:last-child').append('<tr><td>' + newValue.val() + '</td></tr>');            
        });

On the button click, we get the hidden list and the new value, we then simply add the new value to the list. Finally, we manipulate the table in order to display the new value. If you F12 the page, you’ll notice that the Razor engine replaces the Html Helpers with controls that have Ids the same as the fields that they are displaying (note that if the field name contains a “.”, for example: MyClass.MyField, the Id would be MyClass_MyField).

When you now submit this, you’ll see that the hidden field contains the correct list of values.

References

https://stackoverflow.com/questions/16174465/how-do-i-update-a-model-value-in-javascript-in-a-razor-view/16174926

https://stackoverflow.com/questions/171027/add-table-row-in-jquery

https://stackoverflow.com/questions/36317362/how-to-add-an-item-to-a-list-in-a-viewmodel-using-razor-and-net-core

Asp.Net Core 2.0 – Passing data into a Model Using DI

Imagine you have an Asp.Net Core web page, and you would like to edit some data in a form, but you’d like to default that data to something (maybe initially specified in the Web.Config).

I’m sure there’s dozens of ways to achieve this; but this is one.

Let’s start with a bog standard MVC web app:

Step one is to define a model in which to hold your data; here’s mine:

public class CurrentAppData
{
    public string DataField1 { get; set; }
}

Let’s register that in the IoC container:

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        // This lambda determines whether user consent for non-essential cookies is needed for a given request.
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });
 
 
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
 
    services.AddTransient<CurrentAppData, CurrentAppData>(
        a => new CurrentAppData()
        {
            DataField1 = "test value"
        });

Next thing we’ll need is a View, and a corresponding view model to edit our data; here’s the view:

@model EditDataViewModel
@{
    ViewData["Title"] = "Edit Data";
}
 
<h2>@ViewData["Title"]</h2>
 
<div>
    <label>Change data here:</label>
    <input type="text" asp-for="EditData.DataField1" />
 
</div>

And now the view model (that is, the model that is bound to the view):

public class EditDataViewModel
{
    public EditDataViewModel(CurrentAppData editData)
    {
        EditData = editData;
    }
    public CurrentAppData EditData { get; set; }
}

The final step here is to adapt the controller so that the CurrentAppData object is passed through the controller:

public class EditDataController : Controller
{
    private readonly CurrentAppData _currentAppData;
 
    public EditDataController(CurrentAppData currentAppData)
    {
        _currentAppData = currentAppData;
    }
 
    public IActionResult EditData()
    {
        return View(new EditDataViewModel(_currentAppData));
 
 
    }
}

That works as far as it goes, and we now have the data displayed on the screen:

The next step is to post the edited data back to the controller; let’s change the HTML slightly:

<form asp-action="UpdateData" asp-controller="EditData" method="post" enctype="multipart/form-data">
    <label>Change data here:</label>
    <input type="text" asp-for="EditData.DataField1" />
    <br />
    <button type="submit" class="btn-default">Submit Changes</button>
</form>

We’ve added a submit button, which should cause the surrounding form element to execute whichever “method” it’s been told to (in this case, POST). It will look for an action on the controller called UpdateData, so we should create one:

public IActionResult UpdateData(EditDataViewModel editDataViewModel)
{
    System.Diagnostics.Debug.WriteLine(editDataViewModel.EditData.DataField1);
    return View("EditData", editDataViewModel);
}

Here, we’re accepting the EditDataViewModel from the view. However; when we run this, we get the following error:

Error:

InvalidOperationException: Could not create an instance of type ‘WebApplication14.Models.EditDataViewModel’. Model bound complex types must not be abstract or value types and must have a parameterless constructor. Alternatively, give the ‘editDataViewModel’ parameter a non-null default value.

Let’s first implement a fix for this, and then go into the whys and wherefores. The fix is actually quite straightforward; simply give the view model a parameterless constructor:

public class EditDataViewModel
{
    public EditDataViewModel()
    {
        
    }
 
    public EditDataViewModel(CurrentAppData editData)
    {
        EditData = editData;
    }
    public CurrentAppData EditData { get; set; }
}

The problem that we had here is that the `EditDataViewModel` that is returned to UpdateData is a new instance of the model. We can prove this by changing our code slightly:

Here, we’ve added a field called TestField1 to the model, and populated it just before we pass the model to the view; and on the post back, it’s gone. I’m not completely sure why the view model can’t be created by the middleware in the same way that the controller is; but that’s the subject of another post.

Finally, show the value back to the screen

To wrap up, we’ll just show the same value back to the screen; we’ll add an extra value to the model:

public class CurrentAppData
{
    public string DataField1 { get; set; }
 
    public string DisplayField1 { get; set; }
}

And we’ll just display it in the view:

<form asp-action="UpdateData" asp-controller="EditData" method="post" enctype="multipart/form-data">
    <label>Change data here:</label>
    <input type="text" asp-for="EditData.DataField1" />
    <br />
    <button type="submit" class="btn-default">Submit Changes</button>
    <br />
    <label>@Model.EditData.DisplayField1</label>
    <br />
</form>

Finally, we’ll copy that value inside the controller (obviously, this is simulating something meaningful happening), and then display the result:

public IActionResult UpdateData(EditDataViewModel editDataViewModel)
{
    editDataViewModel.EditData.DisplayField1 = editDataViewModel.EditData.DataField1;
    return View("EditData", editDataViewModel);
}

Let’s see what that looks like:

Updating a View Model from Inside a Collection

My last post suggested a concept of having a `Selectable` Model, which effectively wraps a model in a class with a generic item and an `IsSelected` property. This does, however, present a slight issue. What if you want to update the view model, based on the item being selected, or de-selected?

Say, for example, that from my previous post, you had a property on the Person class called Wages, and wanted to display the total wages of all the selected people. You would have defined `TotalCostSummary` in the ViewModel, and bound it in the View.

My solution is to pass a delegate into the Selectable item. Here’s how I did it (this doesn’t really make any sense without referring back to the first post).

SelectableItem Model

In the `SelectableItem`, define a new property like so:

        private Action _updateMethod = null;
        public Action IsSelectedChangedMethod
        {
            get { return _updateMethod; }
            set
            {
                _updateMethod = value;
            }
        }

And change `IsSelected` to looks like this:

        private bool _isSelected;
        public bool IsSelected
        {
            get { return _isSelected; }
            set
            {
                _isSelected = value;                
                RaisePropertyChanged();
                
                if (_updateMethod != null)
                    _updateMethod();
            }
        }

ViewModel

        private void UpdateTotalCost()
        {
            _summary = SelectablePeople.Where(a => a.IsSelected).Sum(s => s.Item.Wages);
            RaisePropertyChanged(() => TotalCostSummary);
        }

Next, change the code to define the list:

List<Person> population = GetPeople();
SelectablePeople = new ObservableCollection<SelectableItem<Person>>(population
	.Select(n => new SelectableItem<Person>() 
	{ 
		Item = n, IsSelected = false, IsSelectedChangedMethod = (() => UpdateTotalCost())  
	}).ToList());

Conclusion

The net effect is that the changes now made inside the model can bubble up to the ViewModel, but the separation of the two has been maintained.