passing model back to controller with checkboxes comes back empty - asp.net-mvc-4

trying to grasp how to pass back the following data as a model to the controller. I want to consume the model and add everything to a database afterwards. When I submit the form back to the controller, the model is empty. Is it because every thing else is null? do I have to pass everything else back as hidden fields? How do I sort that all out on the View before getting to the controller?
My controller basically deserializes an xml file that looks like this back to the view
<category>
<id>1</id>
<description>movies</description>
<genre>
<genres>
<id>1</id>
<name>comedy</name>
</genres>
<genres>
<id>2</id>
<name>action</name>
</genres>
<genres>
<id>3</id>
<name>adventure</name>
</genres>
<genres>
<id>4</id>
<name>drama</name>
</genres>
<genres>
<id>5</id>
<name>romance</name>
</genres>
</genres>
</category>
The view / form looks like this
<form>
<ul>
#for (int x = 0; x < Model.categories[i].genres.Count(); x++)
{
<li>
<label for="#Model.categories[i].genres[x].name">
<input type="checkbox" name="#Model.categories[i].genres[x].name" value="#Model.categories[i].genres[x].id" checked="#Model.categories[i].genres[x].selected" /> #Model.categories[i].genres[x].name
</label>
</li>
}
</ul>
</form>

Give all your checkboxes a fixed name, like GenreIds.
Then, in your action you should receive a string[] genreIds parameter.
When posting the form, the genreIds is posted as am array, so it must be received in an array parameter.
If this doesn't work because you have many categories and want to receive each group of GenreIds in its own category, then you can send a JSON representation of the form values and receive and deserialize it on the server side. To do so:
On the razor template:
Use the name of the genre to name all the checkboxes in each category
Handle the form sumit event, and:
Use jQuery serializeArray, to put all the form elements in an array
Then use JSON.stringify to convert this array to JSON format
Finally copy this string to a hidden field with a fixed name and post the form. I.e. in a hidden field with the name "serializedFormValues"
On the server side:
Add a parameter to your action with the name "serializedFormValues", and type = string
get the value of this parameter and deserialize the received JSON string, and use it on the server side
If you use JSON.NET you can convert the JSON to XML, or to an anonymous type object. There are another posibilities.
Remember that in any case, the genre Ids will always be string[] (or int, if ti's the case) and this arrays will only contain the checked values.
There is a last posibility which is processing the Request.Form "manually". But this is harder to do.

Try using CheckboxFor instead of the input tag.
As for your code, you don't have an ID associated with your checkbox. How are you supposed to be receiving it back in your controller?

Related

How to display the ID of an item in a List resource and a Datagrid instead of the object's IRI?

I noticed that every time an item is displayed in a List resource or a Datagrid component the element's ID property is presented as an IRI string.
I understand this is due to the relationship between HydraAdmin and JSON-LD content negotiation:
Sends proper HTTP requests to the API and decodes them using Hydra and JSON-LD formats
But my (simple) question is: how to display the ID and not the entire context? I only want the last part (the unique ID itself) to be displayed in the Datagrid or List resource column but by no means can I do this even though my API returning an entire ID property cannot display (example image below). Anyway, the value of "#id" (string) is always displayed due to Hydra and JSON-LD relationship.
Debugging the response headers and using useListContext I was able to find that there is an injected property called originId in the data object and that it is possible to display the original ID and not reference it to "#id" as the default for the property ID using as source "originId" and not "id".
in the DataGrid I used source="originId"
...
const {data, ids, basePath} = useListContext();
return (
<Datagrid fullWidth>
// here is the magic, use originId instead id
<TextField source={"originId"} label={"ID"}/>
<TextField source={"created_at"} label={"Data"}/>
<TextField source={"author"} label={"Autor"}/>
<TextField source={"content"} />
<TextField source={"is_public"} />
</Datagrid>
)

Programmatically return Partial View with correct index

I have a view that is rendering Person objects in a list called People in a table with dynamic rows. I render it like so in the HTML:
<partial name="_People" for="Model.People[i]" />
With i being the index in the for loop.
So it then goes and renders a partial view with the correct input names so I can bind back to it. Like so:
<input name="People[0].Name" ...
<input name="People[0].PhoneNumber" ...
I want to call back to my razor model to render a new row programmatically and have it return a row with the correct index. So say there are already 7 rows in the table, I hit a button and it returns a Partial View with my row inputs with the correct index of 8 in their names. I can then append this row to the table with javascript. So just wondering if there is a programmatic equivalent to
<partial name="_People" for="Model.People[8]" />
I figured it out. You can set the HTML Field Prefix in the view data template info.
Like so:
int index = 8;
ViewData.TemplateInfo.HtmlFieldPrefix = string.format("People[{0}], index)";

Using repeat.for with bound number

Consider sample below:
//edit.html
<input type="number" step="1" value.bind="number" />
<div repeat.for="num of number">${num}</div>
//edit.ts
export class Edit {
number: number = 2;
}
I expect to see 2 divs on first page load and number of divs should change when I change number in input. Instead I get error
Value for 'number' is non-repeatable
I figured it out. If you bind input field to variable, even when variable is number, it will be changed to string when changed by user. In my case, number became string once changed in input field. I used this gist to help me solve this problem:
https://gist.github.com/jdanyow/d9d8dd9df7be2dd2f59077bad3bfb399
It offers custom element and attribute for binding numbers to input fields.

Aurelia not outputting attribute with string interpolation in repeat

Is there any reason why a repeat.for binding would remove attributes from elements inside the repeater?
<div repeat.for="i of model.someArray.length">
<label>Some Array - Index ${i + 1}</label>
<input value.bind="model.someArray[i]" some-custom-attribute="someArray[${i}]"/>
</div>
and that some-custom-attribute is not being output within the repeat, but if I were to remove the string interpolation within there then it outputs fine.
== Edit ==
I have put it in a comment but just to make sure everyone is on the same page, ideally this is the output I expect:
<input value.bind="model.someArray[i]" some-custom-attribute="someArray[0]"/>
The some-custom-attribute is not an aurelia attribute, its pure HTML that a 3rd party JS library uses, so the goal here is to get the textual value of the index into the textual attribute value.
model.someArray.length is a number, not an array. You need to iterate over the array. If you do need the current index, the repeater provides the $index property for you to use.
Your code should look like this:
<div repeat.for="item of model.someArray">
<label>Some Array - Index ${$index + 1}</label>
<input value.bind="item" some-custom-attribute.bind="item"/>
</div>
To answer your original question, doing some-custom-attribute="model.someArray[${i}]" makes Aurelia think you are trying to pass a string value to the custom attribute. You can see that in the following gist: https://gist.run/?id=eed8ac8623ff4749aa5bb93c82a7b1fb I've created a custom element that just pushes whatever value it is given in to an element on the page. Note!!! Don't ever do what I'm doing here! I just did this this way so you wouldn't have to open the js console. To actually get a value passed in, you would need to use some-custom-attribute.bind="item" or (to do things how you are doing things, some-custom-attribute.bind="someArray[i]"

xml parsing in iPhone and getting other tags with same names

Let me try to explain as clear as possible what I mean exactly with this question.
The xml looks instead like this
<Books>
<Book id="1">
<title>Circumference</title>
<author>Nicholas Nicastro</author>
<summary>Eratosthenes and the Ancient Quest to Measure the Globe.</summary>
</Book>
<Book id="2">
<title>Copernicus Secret</title>
<author>Jack Repcheck</author>
<summary>How the scientific revolution began</summary>
</Book>
</Books>
It will look like this
<Books>
<Book id="1">
<title>Circumference</title>
<author>Nicholas Nicastro</author>
<summary id ='1'>Eratosthenes and the Ancient Quest to Measure the Globe.</summary>
<summary id ='2'>Eratosthenes more info in another tag.</summary>
<summary id ='3'>Eratosthenes and again another tag.</summary>
<summary id ='4'>Eratosthenes and the final tag another one here</summary>
</Book>
<Book id="2">
<title>Copernicus Secret</title>
<author>Jack Repcheck</author>
<summary id ='1'>How the scientific revolution began</summary>
<summary id ='2'>Eratosthenes more info in another tag.</summary>
<summary id ='3'>Eratosthenes and again another tag.</summary>
<summary id ='4'>Eratosthenes and the final tag another one here</summary>
</Book>
</Books>
Now if I follow the instruction on the site listed above , it doesn't explain how to handle summary 2,3,4( the xml i need to parse looks like that) and how I can show their output. All I will get is the last line. Does anyone have an idea about how I can get the other ones as well( meaning 2,3 in this case it seems to show only the last one since that's probably the last in the currentElementValue ).
I'm a bit confused would I have to address the attribute here as well or should I create a new search tag in my parser?
I think this is what you need to be looking at, you could grab the value of the id field from the attributes and using that value assign it to a variable which you can then use.
So I might have something this in my didStartElement (where attributes is a variable declared in the header):
if([elementName isEqualToString:#"Summary"]){
attributes = attributeDict;
}
Then something like this in my foundCharacters:
if([[attributes valueForKey:#"id"] intValue] == 1){
doSomething
}else if([[attributes valueForKey:#"id"] intValue] == 2){
doSomethingElse
}...
and so on until you've got all your data out.
N.B. This is 100% untested code but I'm confident it might work...
You will need to keep track of the summary elements you have parsed so far by keeping all the values in some container like an array: NSMutableArray. Thus, instead of an NSString to keep the summary, you'd have an NSMutableArray to hold the list of summaries you have parsed.
Whenever you encounter a summary in your parser, you don't set the NSString summary to the string you just read (which replaces the old value and explains why you only get the last summary tag). Instead, you store it as a new string and add that string to your NSMutableArray.
The problem with the design in the blog post you linked to is that it uses keyValueCoding to set the properties in the Book object and that doesn't facilitate adding items to an array item very well. Hence, you will need to include some special handling for the summary element in the parser and add methods to the Book class that allow you to add items to the summary array. See this post on how to also do that with KVC.