Dropdown filled with options provided by an enum (server side) with Breeze
Asked Answered
P

4

9

I develop an asp.net mvc solution with durandal/breeze.

I have a dropdown where list is populated from an Enum provided by Entity Framework Code First. Here is the model server side:

public enum EnumCategory
{
    Cat1,
    Cat2,
    Cat3,
    Cat4
}

Here is the table which use this enum:

public class Transport
{
    [Key]
    public int Id { get; set; }
    public EnumCategory Category { get; set; }
    ...
}

My question: how to retrieve these values of the enum server side to be able to fill my dropdown client side? Do I have to create a new array manually client side like this:

 var categories = [
    { id: '' , description: '' },
    { id: 'Cat1', description: 'Category 1' },
    { id: 'Cat2', description: 'Category 2' },
    { id: 'Cat3', description: 'Category 3' },
    { id: 'Cat4', description: 'Category 4' }];

My view display this dropdown like this:

<select data-bind="options: $root.categories,
                   optionsText: 'description',
                   optionsValue: 'id',
                   value: category,
                   validationOptions: { errorElementClass: 'input-validation-error' },
                   valueUpdate: 'afterkeydown'">
 </select>

It seems redundant to me to have to recreate a list of values client side because we already have this list of values server side.

Any idea?

Thanks.

Polygnotus answered 31/3, 2013 at 17:6 Comment(0)
S
1

As a workaround in the meantime, you could create global "enums" from your metadata like this:

manager.fetchMetadata()
    .then(function (data) {

        // extract all enums als global objects
        ko.utils.arrayForEach(data.schema.enumType, function (c) {
            window[c.name] = {};
            ko.utils.arrayForEach(c.member, function (m) {
                window[c.name][m.name] = m.value;
            });
        });

});

So if you had an enum called "Status", you would now have a global object that you can call:

var currentStatus = Status.Done; //returns the value as defined in the server side enum

These can then also be bound to dropdowns

Scoreboard answered 23/1, 2014 at 17:30 Comment(0)
T
2

You are right, it is redundant to have to repeat the enum definition on the client for an enum defined on the server. Ideally the breeze metadata should include the individual enum values that make up an Enum type.

Unfortunately, we haven't gotten there yet. But this is a very reasonable feature. Could you please add it to the Breeze User Voice. We take these suggestions very seriously in determining which features to work on next.

Terti answered 1/4, 2013 at 18:45 Comment(0)
C
1

Here is an option you can consider, although it does not use Breeze at all :-(, I haven't adopted breeze yet so not sure how it can aid us here.

This sample uses a standard WebAPI controller to populate a list of timezones into a dropdown list on a knockout V/VM.

The controller:

public class LookupController : ApiController
{
    public IEnumerable GetTimezones()
    {
        return TimeZoneInfo.GetSystemTimeZones().Select(tz => new {tz.Id, tz.DisplayName}).ToArray();
    } 
}

The output from the controller (sorry for the formatting, but it's basically Id, Name pairs, much like your categories list):

[{ Id: "Dateline Standard Time", DisplayName: "(UTC-12:00) International Date Line West" }, { Id: "UTC-11", DisplayName: "(UTC-11:00) Co-ordinated Universal Time-11" }, { Id: "Hawaiian Standard Time", DisplayName: "(UTC-10:00) Hawaii" }, { Id: "Alaskan Standard Time", DisplayName: "(UTC-09:00) Alaska" }, { Id: "Pacific Standard Time (Mexico)", DisplayName: "(UTC-08:00) Baja California" }, { Id: "Pacific Standard Time", DisplayName: "(UTC-08:00) Pacific Time (US & Canada)" }, { Id: "US Mountain Standard Time", DisplayName: "(UTC-07:00) Arizona" }, .... etc

Snippet from the view model:

$.ajax({
        url: '/api/lookup/timezones',
        context: this
    }).done(function(result) {
        // load timezones
        timezones(result); // timezones is a ko.observableArray
        // set the default time zone
        timezone('Eastern Standard Time'); // timezone is a ko.observable
    });

The view:

<select class="span6" data-bind="options: timezones, optionsText: 'DisplayName', optionsValue: 'Id', value: timezone"></select>

This gives me a dropdown on my form populated by objects from the server.

Checky answered 1/4, 2013 at 9:10 Comment(5)
The example you give me is specifically when the list of values comes from a database. My question is about retrieving list of values from an enum (server side). This is not the same approach. I don't want to store different values in my db. Thanks anyway.Polygnotus
You don't have to keep enum descriptions in the db. Add DescriptionAttribute to each enum value and then build an array of id and description pairs on the server. Use Enum.GetValues() and Attribute.GetCustomAttributes() to get id and description.Nebulous
Hi Bronzato, I am suggesting that you define your Enum once on the server and then access it on the client using an API controller. That's effectively what I am doing to populate a list of Timezones on the client. The list of timezones is powered by the .NET TimeZoneInfo class, which is kind of like your enum scenario, so far as it's generated from code and not a DB.Checky
@pawel: this is not possible to use Enum.GetValues() and Attribute.GetCustomAttributes() client size (javascript). Maybe I misunderstand what you try to explain me? Any code example?Polygnotus
@Alex: so if I understand you, you use the web API to pass data not provided by the database?Polygnotus
S
1

As a workaround in the meantime, you could create global "enums" from your metadata like this:

manager.fetchMetadata()
    .then(function (data) {

        // extract all enums als global objects
        ko.utils.arrayForEach(data.schema.enumType, function (c) {
            window[c.name] = {};
            ko.utils.arrayForEach(c.member, function (m) {
                window[c.name][m.name] = m.value;
            });
        });

});

So if you had an enum called "Status", you would now have a global object that you can call:

var currentStatus = Status.Done; //returns the value as defined in the server side enum

These can then also be bound to dropdowns

Scoreboard answered 23/1, 2014 at 17:30 Comment(0)
B
0

Using some of the concepts from @JohnPapa 's SPA courses, could you not expose an action on your Breeze Controller as follows:

[HttpGet]
    public object Categories()
    {
        var categories =  Enum.GetNames(typeof(EnumCategory)
        return categories;
    }

*EDIT: Realised I had used GetValues (which would return the int values) rather than GetNames

Then in your viewmodel (or a datacontext service):

var categories = EntityQuery.from('Categories')
           .using(manager).execute()
Belch answered 18/7, 2013 at 3:37 Comment(1)
DISCLAIMER: Please note that this is conceptual and totally untested! :DBelch

© 2022 - 2024 — McMap. All rights reserved.