Passing a single object from a controller to a view - asp.net-core-mvc

I have a web-based game built using .netCore 2 and entity framework.
The game works, but I just added a new feature. In one of the views, the user can press a button that creates a new character via a controller.
Here is that controller method:
public async Task<IActionResult> AddCharacter(Guid playerId)
{
ICharManagement charMgr = new CharManagementClient();
Character newCharacter = await charMgr.AddCharacterAsync(
"Test Character",
new Guid("1e957dca-3fe1-4214-b251-a96e0106997a")
);
var newCharacterObject = newCharacter;
return View(newCharacterObject);
}
The Character object is from an API I use and it looks like this:
public partial class Character : object
{
private System.Guid Id;
private string Name;
private System.Nullable<System.DateTime> DateCreated;
private int32 CharPlayer;
private bool Playable;
}
Stepping through the controller in Visual Studio, I can see that a new Character object is returned and the newCharacterObject looks like this:
Id "1e957dca-3fe1-4214-b251-a96e0106997a"
Name "Test Character"
DateCreated "2/12/2019"
CharPlayer 3821
Playable "true"
In my view (cshtml), I have the model set to this:
#model IEnumerable<Character>
However, when I run it, I get this error:
InvalidOperationException: The model item passed into the
ViewDataDictionary is of type 'Character', but this
ViewDataDictionary instance requires a model item of type
'System.Collections.Generic.IEnumerable`1[Character]'.
So how do I get my view to display a single Character object(newCharacterObject) or how would I convert "newCharacterObject" into what the view needs?
Thanks!
Edit: Formatting

If you only need one character model, you can just use #model Character at the top of your cshtml
IEnumerable is used for iterating through a collection of objects.

Related

Unable to write to bot state property after accessor has been created

Perhaps a misunderstanding on my behalf, but in botframework SDKv4, once I create a property accessor for a set of variables I wish to capture in an adaptive dialog, I can no longer write to them directly from input actions.
e.g. prior to initializing a property I can have an input action such as
new TextInput()
{
Prompt = new ActivityTemplate("${RequestPhoneNumber()}"),
Property = "user.profile.mobilenumber",
}
Then later I can refer to that property in an adaptive expression, or by checking the dialog context
var phone = dc.State["user.profile.mobilenumber"];
and I'll find the text the user entered inside that property, as expected.
However, if I go and create a data class like:
public class Person
{
public string MobileNumber { get; set; }
}
then create it in my dialog
var personAccessor = userState.CreateProperty<Person>("profile");
If I read the mobilephone property later I'll find the property null after the TextInput action has completed
Person profile = await personAccessor.GetAsync(dc.Context, () => new Profile());
Console.WriteLine(profile.MobileNumber);
The output on the console is null, and I can't quite work out what I'm doing wrong or misunderstanding.

xamarin: binding object and object parent to property change

I have a model "optionModel" with "data" member as a list of "options". Option has title and status members:
class optionModel
{
...
public List<option> data {get; private set;}
}
class option
{
...
public String title{get;set}
public bool status {get;set}
}
My view for the model is a grid, which is populating like this:
optionModel _m = new optionModel();
Grid g = new Grid();
foreach (option op in _m.data)
{
..
TextSwitch tc = new TextSwitch(); // view class
tc.BindingContext = op;
tc.SetBinding(TextSwitch.IsToggledProperty, "status");
g.Children.Add(tc, 0, iRow);
..
}
Everything works as expected. "status" property of the "option" is updated, I am happy.
My question is: In addition to "option" I also want "optionModel" to be notified when any of the "option" change their status. One immediate solution is
to construct the "option" to have pointer to the optionModel and notify optionModel it within "status" set method of "option".
But perhaps there is a way to do it via Xamarin binding mechanism without introducing optionModel as a member of "option"?
Thanks!

How to bind a dynamic list of fields in a JSP

I am building a JSP page for entering football game results. I get a list of unsettled games and I want to list them like this:
team1 vs team4
[hidden field: game id]
[input field for home goals]
[input field for away goals]
team2 vs team5
[hidden field: game id]
[input field for home goals]
[input field for away goals]
I never know how many games will be listed. I am trying to figure out how to set up the binding so that the controller can access these fields after the form is submitted.
Can someone please guide me in the right direction. I am using Spring MVC 3.1
Spring can bind indexed properties, so you need to create a list of game info objects on your command, like:
public class Command {
private List<Game> games = new ArrayList<Game>();
// setter, getter
}
public class Game {
private int id;
private int awayGoals;
private int homeGoals;
// setters, getters
}
In your controller:
#RequestMapping(value = "/test", method = RequestMethod.POST)
public String test(#ModelAttribute Command cmd) {
// cmd.getGames() ....
return "...";
}
In your JSP you will have to set the paths for the inputs like:
games[0].id
games[0].awayGoals
games[0].homeGoals
games[1].id
games[1].awayGoals
games[1].homeGoals
games[2].id
games[2].awayGoals
games[2].homeGoals
....
If I'm not mistaken, in Spring 3 the auto-growing collections is now the default behavior for binding lists, but for lower versions you had to use an AutoPopulatingList instead of just an ArrayList (just as a reference: Spring MVC and handling dynamic form data: The AutoPopulatingList).

How to get value dynamically added textbox values in MVC 3

I want to get the values of dynamically added Textbox on submit button in MVC 3.
I am storing the values in hidden field and getting using FromCollection. Is there any better approach?
If you name your values something like
MyValues[x] where x is a zero based, continuously increasing integer, you can receive the string values as a list of strings named MyValues.
This trick also works for properties if the main model object, if needed.
You should check some articles about how to bind to collections In ASP mvc, they could give you some ideas.
For example http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
You could do something like this (written very quickly outside of editor, so may have typos/issues):
Make a view model:
public class DynamicTextBoxViewModel
{
public IList<string> DynamicTextBox { get; set; }
public int OtherStuffInViewModel { get; set; }
}
Then in your Get Action:
var model = new YourViewModel
{
DynamicTextBoxList =
new List<DynamicTextBox>
{
new DynamicTextBox
{
TextBoxText = string.Empty,
}
},
OtherStuffInViewModel = xxx,
};
return View(model)
Then in your Post Action:
You would bind everything where you wanted it.
The idea is to move all the data into a ViewModel and pass that around so you gain the benefits of the ViewModel instead of passing around FormCollection - which is sloppier and more error prone.

MVC Model Binding a Complex Type to a Simple Type and Vice Versa

Here's a scenario:
I have an autocomplete plugin (custom) that keeps a hidden field of JSON objects (using a specific struct).
I've created an Html helper that helps me easily bind to a specific custom model (basically, it has a JSON property that is for two-way binding and a property that lets me deserialize the JSON into the appropriate struct):
public class AutoCompleteModel {
public string JSON { get; set; }
public IEnumerable<Person> People {
get {
return new JavaScriptSerializer().Deserialize<Person>(this.JSON);
}
set {
this.JSON = new JavaScriptSerializer().Serialize(value);
}
}
}
This works great and I can model bind using the default binder #Html.Autocomplete(viewModel => viewModel.AutoCompleteModelTest). The HTML helper generates HTML like:
<input type="text" id="AutoCompleteModelTest_ac" name="AutoCompleteModelTest_ac" value="" />
<input type="hidden" id="AutoCompleteModelTest_JSON" name="AutoCompleteModelTest.JSON" value="{JSON}" />
The problem is this is not the best way for consumers. They have to manually set the People property to an array of Person structs. In my data layer, my domain objects probably will not be storing the full struct, only the person's ID (a corporate ID). The autocomplete will take care of looking up the person itself if only given an ID.
The best scenario will be to call it like this:
#Html.Autocomplete(domainObject => domainObject.PersonID) or
#Html.Autocomplete(domainObject => domainObject.ListOfPersonIDs
I would like it to work against the string property AND against the custom AutoCompleteModel. The autocompleter only updates a single hidden field, and that field name is passed back on postback (the value looks like: [{ "Id":"12345", "FullName":"A Name"},{ "Id":"12347", "FullName":"Another Name" }]).
The problem is, of course, that those domain object properties only have an ID or array of IDs, not a full Person struct (so cannot be directly serialized into JSON). In the HTML helper, I can transform those property values into a struct, but I don't know how to transform it back into a simple type on POST. The solution I need would transform an ID into a new Person struct on page load, serializing it into the hidden field. On POST, it would deserialize the generated JSON back into a simple array of IDs.
Is a custom model binder the solution I need? How can I tell it to work both with a custom model AND simple types (because I don't want it applied to EVERY string property, just need it to deal with the values given by the HTML helper).
I figured it out, it's possible!
To clarify, I needed to: transform a string or string array (of IDs) into a JSON structure for my hidden field value, then on post back, deserialize the JSON in the hidden field and transform the struct back into a simple string or string array (of IDs) for my domain object's property.
Step 1: Create a HTML helper
I had done this already, but only for accepting my custom AutoCompleteModel type. I needed one for a string and an Enumerable of string type.
All I did was generate my Person struct(s) from the value of the property and serialize them into JSON for the hidden field the Autocompleter uses (this is an example of the string helper, I also have a nearly identical one for IEnumerable<string>):
public static MvcHtmlString AutoComplete<TModel>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, string>> idProp)
where TModel : class
{
TModel model = htmlHelper.ViewData.Model;
string id = idProp.Compile().Invoke(model);
string propertyName = idProp.GetPropertyName();
Person[] people = new Person[] {
new Person() { ID = id }
};
// Don't name the textbox the same name as the property,
// otherwise the value will be whatever the textbox is,
// if you care.
MvcHtmlString textBox = htmlHelper.TextBox(propertyName + "_ac", string.Empty);
// For me, the JSON is the value I want to postback
MvcHtmlString hidden = htmlHelper.Hidden(propertyName, new JavaScriptSerializer().Serialize(people));
return MvcHtmlString.Create(
"<span class=\"AutoComplete\">" +
textBox.ToHtmlString() +
hidden.ToHtmlString() +
"</span>");
}
Usage: #Html.AutoComplete(model => model.ID)
Step 2: Create a custom model binder
The crux of my issue was that I needed this binder to only apply to certain properties, and they were strings or string arrays.
I was inspired by this article because it used Generics. I decided, hey, we can just ask people what property they want to apply the binder for.
public class AutoCompleteBinder<T> : DefaultModelBinder
where T : class
{
private IEnumerable<string> PropertyNames { get; set; }
public AutoCompleteBinder(params Expression<Func<T, object>>[] idProperties)
{
this.PropertyNames = idProperties.Select(x => x.GetPropertyName());
}
protected override object GetPropertyValue(
ControllerContext controllerContext,
ModelBindingContext bindingContext,
PropertyDescriptor propertyDescriptor,
IModelBinder propertyBinder)
{
var submittedValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (submittedValue != null && this.PropertyNames.Contains(propertyDescriptor.Name))
{
string json = submittedValue.AttemptedValue;
Person[] people = new JavaScriptSerializer().Deserialize<Person[]>(json);
if (people != null && people.Any())
{
string[] IDs = people.Where(x => !string.IsNullOrEmpty(x.ID)).Select(x => x.ID).ToArray();
bool isArray = bindingContext.ModelType != typeof(string) &&
(bindingContext.ModelType == typeof(string[]) ||
bindingContext.ModelType.HasInterface<IEnumerable>());
if (IDs.Count() == 1 && !isArray)
return IDs.First(); // return string
else if (IDs.Count() > 0 && isArray)
return IDs.ToArray(); // return string[]
else
return null;
}
else
{
return null;
}
}
return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
}
}
GetPropertyName() (translate LINQ expression into a string, i.e. m => m.ID = ID) and HasInterface() are just two utility methods I have.
Step 3: Register
Register the binder on your domain objects and their properties in Application_Start:
ModelBinders.Binders.Add(typeof(Employee), new AutoCompleteBinder<Employee>(e => e.ID, e => e.TeamIDs));
It's only a little bit annoying to have to register the binder for specific properties, but it's not the end of the world and provides a nice, smooth experience working with my autocompleter.
Any comments are welcome.

Resources