-1

I have a model having a field as Dictionary type need to post to back end controller action. I am wondering how should I define the model object in JQuery?

I keep getting primitive error from ajax call with the following code:

var templateBuilder = {
  TemplateTitle: "Title",
  ParagraphTitle: "",
  ParagraphTitleList: "",
  ParagraphText: "",
  ParagraphId: "",
  ParagraphOrder: "",
  ParagraphDictionary: ""
};

templateBuilder.ParagraphDictionary = dict; //dict is a Dictionary<int, int> type variable I populated earlier.

$.ajax({
  url: "/TemplateBuilder/Build",
  type: "POST",
  data: templateBuilder,
  contentType: "application/json; charset=utf-8",
  dataType: "json",
  success: function(response) {
    alert("successful");
  },
  error: function(response) {
    alert("fail");
    alert(response.responseText);
  }
});

At the backend action method I have:

public ActionResult Build([FromUri]TemplateBuilderViewModel templateBuilder)

Could anyone point me where I did wrong, please?

Thank you.

UPDATE: Here is the View Model

public class TemplateBuilderViewModel
{

    //public int Id { get; set; }

    [DisplayName("Template Title")]
    public string TemplateTitle { get; set; }

    [DisplayName("Paragraph List")]
    public string ParagraphTitle { get; set; }
    public SelectList ParagraphTitleList { get; set; }

    [DisplayName("Paragraph Body")]
    [DataType(DataType.MultilineText)]
    public string ParagraphText { get; set; }

    public int ParagraphId { get; set; }

    public int ParagraphOrder { get; set; }

    public Dictionary<int, int> ParagraphDictionary { get; set; }
}

And where dict is populated: var dict = [];

for (i = 1; i < counter; i++) 
{
    var paragraphId = Id;

    var order = order;

    dict.push({
        ParagraphId: paragraphId,
        ParagraphOrder: order
    });
}

UPDATED JQuery part: After I made changes to below, the problem is I can see goes to controller action, but from the action method parameter templateBuilder, the count of property ParagraphDictionary is 0, which means the Dictionary is not parsed and pass to backend. Other properties like TemplateTitle has value there. Any idea?

var dict = {};

for (i = 1; i < counter; i++) {

    var paragraphId = Id;

    var order = order;


    dict[order] = paragraphId;
}


var templateBuilder = {
    TemplateTitle: templateTitle,
    ParagraphTitle: "",
    ParagraphTitleList: undefined,
    ParagraphText: "",
    ParagraphId: "",
    ParagraphOrder: "",
    ParagraphDictionary: dict
};

$.ajax(
    {
        url: "/TemplateBuilder/Build",
        type: "POST",
        data: JSON.stringify(templateBuilder),
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function (response) {
            alert("successful");
        },
        error: function (xhr, status, error) {
            alert("fail");
            alert(error);
        }
    });
Xiao Han
  • 1,004
  • 7
  • 16
  • 33
  • If you're sending post data you should be using `[FromBody]`, not `[FromUri]` - although it's not really needed in this case. That said, what does your `TemplateBuilderViewModel` class and the JS `dict` variable look like? – Rory McCrossan Oct 04 '17 at 19:52
  • Thanks, I added more details, but I don't see anything wrong in the ViewModel or the dict variable. – Xiao Han Oct 04 '17 at 20:02
  • If I change the ajax call in JQuery to data: JSON.stringify(templateBuilder), it would give me "No parameterless constructor defined for this object." error – Xiao Han Oct 04 '17 at 20:06

3 Answers3

0

Ok, first of all you did not show what the "primitive error" is, so I will just put some tips that worked for me in similar situations

Set the HttpPost decorator to your method and remove the FromUri

[HttpPost]
public ActionResult Build(TemplateBuilderViewModel templateBuilder)

Second, if you have problems with your model and that is the cause of your error, you can try with a generic model and parse in your method, this way your model will be always accepted

[HttpPost]
public ActionResult Build(object templateBuilder)
{
TemplateBuilderViewModel tb = JsonConvert.DeserializeObject<TemplateBuilderViewModel>(((Newtonsoft.Json.Linq.JObject)templateBuilder)["templateBuilder"].ToString());
}

Finally, maybe your problem is authorization, so add the AllowAnonymous decorator to your method just to test

Victor Hugo Terceros
  • 2,969
  • 3
  • 18
  • 31
  • Thanks for your reply. from the ajax error function error response, it shows message like: "Invalid JSON primitive: TemplateTitle." similar to this post: https://stackoverflow.com/questions/2445874/invalid-json-primitive-in-ajax-processing – Xiao Han Oct 05 '17 at 13:16
0

In your javascript, your dict object is an array of objects which cannot be mapped into a dictionary. Instead, define dict as an object using {} and then address the property using the javascript bracket notation. Admittedly, the line between an object and an array gets a little blurry in js. Try this:

dict = {};
for (i = 1; i < counter; i++) 
{
    var paragraphId = Id;
    var order = order;

    dict[paragraphId] = order;
}

I'm not positive that you wanted the ints in that order. In my simple mock up I used the following where my view model's Dictionary<int, int> mapped well. Just replace the index and value as needed.

var model = {
    Name: 'your face',
    Value: 8,
    Dictionary: {}                
};
for (var i = 0; i < 5; i++)
    model.Dictionary[i] = i;

For testing purposed I put extra properties on my view model, totally meaningless.

nurdyguy
  • 2,876
  • 3
  • 25
  • 32
  • Thank you. I did make the change you suggested. It seems working, but for some reason, the property ParagraphDictionary did not get pass to Controller action, it shows 0 count in debugging mode. – Xiao Han Oct 05 '17 at 13:48
  • To start with, check the networks tab in Chrome and see if the model is populated there. – nurdyguy Oct 09 '17 at 14:33
  • In browser's developer tool, network tab, there is nothing. But I end up using array in JavaScript and map to a List and it works – Xiao Han Oct 10 '17 at 13:52
0

This is my working solution. In ViewModel class I have:

public class TemplateBuilderViewModel
{

    [DisplayName("Template Title")]
    public string TemplateTitle { get; set; }

    [DisplayName("Paragraph List")]
    public string ParagraphTitle { get; set; }

    public SelectList ParagraphTitleList { get; set; }

    [DisplayName("Paragraph Body")]
    [DataType(DataType.MultilineText)]
    public string ParagraphText { get; set; }

    public List<ParagraphOrderModel> ParagraphOrder { get; set; }
}



public class ParagraphOrderModel
{
    public int ParagraphId { get; set; }
    public int ParagraphOrder{ get; set; }
}

In the view, I have this in JQuery:

var dict = [];

for (i = 1; i < counter; i++) {
    var paragraphId = Id;

    var order = order;

    dict.push({
        "ParagraphId": paragraphId,
        "ParagraphOrder": order
    });
}

var templateBuilder = {
    TemplateTitle: templateTitle,
    ParagraphTitle: "",
    ParagraphTitleList: undefined,
    ParagraphText: "",
    ParagraphOrder: dict
};

$.ajax(
    {
        url: "/TemplateBuilder/Build",
        type: "POST",
        data: JSON.stringify({ templateBuilder: templateBuilder }),
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function (response) {
            alert("successful");
        },
        error: function (response) {
            alert("fail");
            alert(response.responseText);
        }
    });

And in the Controller action method I have:

[HttpPost]
public JsonResult Build(TemplateBuilderViewModel templateBuilder)
Xiao Han
  • 1,004
  • 7
  • 16
  • 33