locked
Fail to set up a jQuery collection RRS feed

  • Question

  • User1493428334 posted

    Hi,

    I try to create a non-array collection in jQuery. Below is a simplified version of what I am trying to do:

            var Categories = ["Softdrink", "Cheese", "Fruit" ];       

            var Cats = $();
            var temp;
            for (var i = 0; i < Categories .length; i++) {
                temp = { Id: i, Category: Categories[i] };
                Cats = Cats.add(temp);
            }

    In the above, it ends up creating an array called "Cats". 

    My question is, how to create a non-array collection, ie, when it gets sent from the client side thru Ajax to the server, the server will receive IEnumerable or ICollection, but not an array.

    Appreciate your help,

    Friday, June 12, 2020 5:04 AM

Answers

  • User-474980206 posted

    Sorry, was editing your code. Should be just;

       Cats.push(temp);

    all arrays have a push method, it appends to the end. But it returns the new length, so the assign changed the variable. I would always use .map for this use case.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Saturday, June 13, 2020 3:30 PM

All replies

  • User-719153870 posted

    Hi GoldenMicrosoft,

    Is it ok for you to convert the Cats to json string and then pass to the server, serialize it and conver to other type, like a list?

    Best Regard,

    Yang Shen

    Friday, June 12, 2020 10:23 AM
  • User-474980206 posted

    In JavaScript arrays are collections, and objects are dictionaries. As json is based on JavaScript syntax, any .net IEnumable or ICollection will be mapped to a json array.

    in .net json deserialization ICollection and IEnumerable are interfaces, with no constructor. This mean if you are binding to one they must exist or you need to supply a custom serializer. In practice this means an action parameter can not be an interface. If the model has Interface property, the model constructor must initialize it.

    Note: jquery objects are arrays with extended methods via their prototype.

    Friday, June 12, 2020 2:43 PM
  • User1493428334 posted

    Hi Yang Shen,

     Thanks for your response.

    Actually my code will generate an array of JSON objects. If I modify the code to the one below, which is equivalent to the one from yesterday, it is clearer that "temp" is a JSON object:

            var Categories = ["Softdrink", "Cheese", "Fruit"];

            var Cats = $();
            var temp;
            for (var i = 0; i < Categories.length; i++) {
                temp = { Id: i };
                temp["Category"] = Categories[i];
                Cats = Cats.add(temp);
            }

    Here I am on the client side, in the jQuery, trying to send a collection of JSON objects thru AJAX, to the server, for a Web API Update operation. The server side will take care of the serialization/deserialization.

    Please correct me if my understanding is wrong.

    Friday, June 12, 2020 4:33 PM
  • User-474980206 posted

    you really should not be using $() (empty jquery array object)  as your base. 

     var Categories = ["Softdrink", "Cheese", "Fruit"];
    
     var Cats = [];
     var temp;
     for (var i = 0; i < Categories.length; i++) {
        temp = { 
           Id: i,
           Category: Categories[i]
        };
        Cats = Cats.push(temp);
     }
    
    
    // or better yet:
    
    var Categories = ["Softdrink", "Cheese", "Fruit"];
    var Cats = Categories.map(function(c,i) {
       return {
          Id: i,
          Category: c
       }
    );
    
    

    either of which produces the following JSON

    [
       {"Id": 0, "Category": "Softdrink"}, 
       {"Id": 1, "Category": "Cheese"}, 
       {"Id": 2, "Category": "Fruit"}
    ];

    which can be used in the following actions:

    public class Category
    {
       public int Id {get; set;}
       public string Category {get; set;}
    }
    
    
    // pass as collection
    public IHttpActionResult MyMethod (List<Category> cats)
    {
    }
    
    // pass as array
    public IHttpActionResult MyMethod2 (Category[] cats)
    {
    }
    
    // will not work without writing custom model binder as binder can not create instance of interface
    public IHttpActionResult MyMethod3 (IEnumerable<Category> cats)
    {
    }
    
    

    Friday, June 12, 2020 5:41 PM
  • User1493428334 posted

    Hi Bruce,

    Thanks for your response.

                in .net json deserialization ICollection and IEnumerable are interfaces, with no constructor.This mean if
                you are binding to one they must exist or you need to supply a custom serializer. In practice this means
               an action parameter can not be an interface. If the model has Interface property, the model constructor
               must initialize it.

    (1) When I am sending data from client side AJAX to the.net server, does it need to be serialized/deserialized?
    (2) What is an action parameter in jQuery?
    (3) I am confused by "in .net json deserialization ICollection and IEnumerable are interfaces, with no constructor.
         This mean if you are binding to one they must exist or you need to supply a custom serializer".
         Could you please elaborate and provide an example?

    Friday, June 12, 2020 5:53 PM
  • User1493428334 posted

            Hi Bruce,

            When I sent my previous response, I have not read this one from you.       

            You have partially answered my questions.Thanks.

            I still have questions:

            (1) Does your comment regarding IHttpActionResult equally applicable to HttpResponseMessage?

            (2) About "will not work without writing custom model binder as binder can not create instance of interface", could you give a rough sketch of an example of a custom binder?

    Thanks again,
    Golden

    Friday, June 12, 2020 6:28 PM
  • User-474980206 posted

    1) http request are a byte stream. to send data from the client to the server, it must be serialized (json, form posts, and xml are common formats). 

    2) by action parameter I meant, a parameter to a MVC action. the MVC binder deserializes the query string and post data t parameters

    3) you can not create an instance of an interface

        var list = new List<string>(); // valid
        var list2 = new IEnumerable<string>(); // error
        var list3 = (IEnumerable<string>) list; // extract interface from class instance

    4) there is a difference in MVC action binding and webapi binding as webapi binding is stricter parsing (and faster) in deserialization, but neither will support an interface for binding. if the parameter has am interface property the contractor must initial it.

    for json:
    
    {
       list: {"one","two"}
    }
    
    public class MyModel
    {
        public IEnumable List<string> {get; set;}
    }     
    public class MyModel2
    {
        public IEnumable List<string> {get; set;} = new List<string>();
    }     
    
    // fails binding
    public HttpResponseMessage Get(MyModel model)
    {
    }
    
    // works
    public HttpResponseMessage Get(MyModel2 model)
    {
    }
    

    5) see custom model binder:

    https://docs.microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api

     

    Friday, June 12, 2020 8:00 PM
  • User1493428334 posted

    Hi Bruse,

    Thanks so much for your comprehensive explanation.

    The IEnumerable<> (or ICollection<>) in my case is actually a property within a class, (it did not show because I tried to focus on handling that collection property) and I instantiate the property with HashSet(), which should be OK.

    However, there is a problem with the code you provided:

            var Cats = [];
            var temp;
            for (var i = 0; i < Categories.length; i++) {
                //temp = { Id: i, Category: Categories[i] };
                temp = { Id: i };
                temp["Category"] = Categories[i];
                Cats = Cats.push(temp);
            }

    In the above, "Cats = Cats.push(temp);" does not work. (the "push" does not even show up in intellesence).

    In my code I include only jquery-3.3.1.js. Do I miss anything?

    Golden

    Saturday, June 13, 2020 2:13 AM
  • User-474980206 posted

    Sorry, was editing your code. Should be just;

       Cats.push(temp);

    all arrays have a push method, it appends to the end. But it returns the new length, so the assign changed the variable. I would always use .map for this use case.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Saturday, June 13, 2020 3:30 PM
  • User1493428334 posted

    Hi Bruce,

    Your solution absolutely works! Thanks a great deal!

    Please also comment on the following:

    Since an Array is built on top of a basic Colection, an array "Cats" should work for both:

            for (var i = 0; i < Cats.length; i++) {
                ...
            }

    and 

            Cats.forEach(function (myCat) {
               ...;
            });

    Your solution works for both above.

    Thanks again,
    Golden,

    Sunday, June 14, 2020 6:39 PM