No Knockout model binding on automatically filled inputs - c#

I have a knockout model that is automatically created from a C#-ViewModel:
ViewModels:
public class SearchModel
{
public ActualLocationModel Location { get; set; }
}
public class ActualLocationModel
{
public string Address { get; set; }
}
search.js:
function Search(model) {
var self = this;
self._model = model;
ko.applyBindings(self._model, document.getElementById("searchForm"));
$('#submitButton').click(function () {
alert(self._model.ActualLocation.Address); // proof!
});
}
Search.cshtml:
#model ViewModels.SearchModel
<div id="searchForm">
<input data-bind="value: ActualLocation.Address" type="text">
<input type="submit" id="submitButton" value="Find" />
</div>
<script type="text/javascript">
$(function () {
window.search= new Search(#Html.Raw(Json.Encode(Model)));
});
</script>
So, the databinding is working as expected as long as I enter the values by hand. But in my case the values are filled automatically by geolocation. In this case the binding doesn't do what it should be (output is always null). Is there a way to get the Knockout databinding working on automatic filled inputs?
Thanks for any help!

Alright, here are the relevant bits to get this working for you:
<script type="text/javascript">
$(function() {
window.search = new Search(JSON.parse('#Html.Raw(Json.Encode(Model))'));
});
</script>
Notice we're parsing the model which was converted to JSON so we can use the resulting Javascript object in our model. If you want to go straight from JSON to your model, consider the ko.mapping library, though it is no longer actively maintained, I believe.
<form id="searchForm">
<input data-bind="value: Location.Address" type="text">
<input type="submit" id="submitButton" value="Find" />
</form>
Here, notice the change from AdditionalLocation.Address to just Location.Address.
function Search(model) {
var self = this;
self._model = model;
ko.applyBindings(self._model, document.getElementById("searchForm"));
$('#submitButton').click(function () {
alert(self._model.Location.Address); // proof!
});
}
Finally, we fix up the alert to reflect the shape of our data (AdditionaLocation to Location).
Using the above, everything works as expected for me.

Related

RESTfull web service display info on page, using visual studio 2015

I have trouble with building a web service can read information of NBA players from a txt file and then display it on the web page.
Firstly, I build a class in the Models folder.
namespace zuoye4.Models
{
public class Players
{
public string Registration_ID { get; set; }
public string Player_name { get; set; }
public string Team_name { get; set; }
public DateTime Date_of_birth { get; set; }
}
}
Secondly I create a Controller to read file and add all players to an list. I also define a GetAllPlayers method to return the list.
Testing shows this
Then I create a html page to display the list. Here is my code.
<!DOCTYPE html>
<html>
<head>
<title>PLALYERS</title>
<meta charset="utf-8" />
</head>
<body>
<div>
<h2>All Players</h2>
<ul id="players" />
</div>
<div>
<h2>Search by ID</h2>
<input type="text" id="prodId" size="5" />
<input type="button" value="Search" onclick="find();" />
<p id="product" />
</div>
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.3.min.js"></script>
<script>
var uri = 'api/Players';
$(document).ready(function () {
// Send an AJAX request
$.getJSON(uri)
.done(function (data) {
// On success, 'data' contains a list of players.
$.each(data, function (key, item) {
// Add a list item for the player.
$('<li>', { text: formatItem(item) }).appendTo($('#playerList'));
});
});
});
function formatItem(item) {
return item.Registration_ID + ': $' + item.Player_name;
}
</script>
</body>
</html>
It should shows something like this.
But I get nothing.
What I've done wrong???
Here is the tutorial follow.
https://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-api
You seem to be appending the players list item to an element with id "playerList":
$('<li>', { text: formatItem(item) }).appendTo($('#playerList'));
Problem is, "playerList" doesn't seem to exist, I see you have a "players" instead?
<ul id="players" />

Add a new row to my view dynamically

Ok, so I have this class:
public class BackstoreInventoryUtility
{
public BackstoreInventoryInfo Item { get; set; }
public List<ItemListingUtility> ListItemUtility { get; set; }
public BackstoreInventoryUtility()
{
Item = new BackstoreInventoryInfo();
ListItemUtility = new List<ItemListingUtility>();
}
}
And here's the ListItemUtility class:
public class ItemListingUtility
{
public int Quantity { get; set; }
public string Duration { get; set; }
public List<string> AvailableDurations { get; set; }
public ItemListingUtility()
{
AvailableDurations = new List<string>();
}
}
In a view I am building, I am displaying 1 BackstoreInventoryUtility based on a BackstoreInventoryInfo item my user is currently browsing.
The ListItemUtility is a class allowing the user to proceed to certain action, like display for a set time a set quantity.
The view renders like this:
#model MyApp.Utilities.BackstoreInventoryUtility
#using (Html.BeginForm())
{
<div>
#if (Model.Item.Quantity > 0)
{
<input type="submit" value="Display"/>
}
#Html.HiddenFor(_item => _item.Item.BackstoreInventoryID)
<div class="bigFontSize bold formStyle">
<label class="center">Options will eventually be displayed here.</label>
<div>
<div class="float-left">Quantity Allocated:</div>
<div class="float-right">#Html.DisplayFor(_item => _item.Item.Quantity)
#Html.HiddenFor(_item => _item.Item.Quantity)
</div>
<div class="clear"></div>
</div>
<div class="formStyle" id="itemUtilityZone">
<label>Options</label>
#for (int i = 0; i < Model.ListItemUtility.Count; i++)
{
<div>
<div class="float-left">
Quantity To Display:
</div>
<div class="float-right">
#Html.TextBoxFor(_item => _item.ListItemUtility[i].Quantity, new { #class = "positive-integer numberTextBox" })
</div>
<div class="clear"></div>
</div>
}
</div>
#if (Model.Item.Quantity > 0)
{
<input type="submit" value="Display"/>
}
</div>
}
I'd like my user to dynamically add a new row to the view, and then when the view is submitted, all the rows would be included.
So far I am at the beginning and I am trying this:
[HttpGet]
public ActionResult AddItemUtilityRow()
{
return PartialView(new ItemListingUtility());
}
Where the partial view rendered would be identical to the div used in the table. But I am not sure how could I make this happen, should I use a jQuery call? How might I do this?
EDIT Okay, so I have tried something in jquery which VISUALLY does what I want:
<script type="text/javascript">
$(document).ready(function() {
$("#addUtility").click(function() {
$.get("#Url.Action("AddItemUtilityRow")", {
}, function(data) {
$('#itemUtilityZone').append(data);
});
});
});
</script>
So, as I said, this works but only partially because when the user submits only the default number of items in the list is submitted. How can I make it so that each time the user add a row it adds up to the model and gets later submitted?
Woah! It was more complex than I thought, but thanks to this link : http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/ I was able to make the whole thing work!
I first transfered every row created in a partial view like this:
<div class="formStyle" id="itemUtilityZone">
<label>Options</label>
#foreach (var utilityRow in Model.ListItemUtility)
{
Html.RenderPartial("ItemUtilityRow", utilityRow);
}
</div>
Which renders like this:
#using HtmlHelpers.BeginCollectionItem
#model MyApp.Utilities.ItemListingUtility
#using (Html.BeginCollectionItem("listItems"))
{
<div>
<div class="float-left">
Quantity To Display:
</div>
<div class="float-right">
#Html.TextBoxFor(_item => _item.Quantity, new { #class = "positive-integer numberTextBox" })
</div>
<div class="clear"></div>
</div>
}
Note: for the Html.BeginCollectionItem Html Helper, I had to search a bit for Steven Sanderson's Helper which he mentions in the upper link. You can find it here:
https://github.com/danludwig/BeginCollectionItem
Next, my javascript call looks like this:
$(document).ready(function() {
$("#addUtility").click(function () {
$.ajax({
url: '#Url.Action("AddItemUtilityRow")',
cache: false,
success: function(html) {
$('#ItemUtilityZone').append(html);
}
});
});
});
And the controller method that adds a new row:
[HttpGet]
public ActionResult AddEbayUtilityRow()
{
return PartialView("ItemUtilityRow", new ItemListingUtility());
}
And the rows shows just fine now. The catch is, how do I catch it back in my post method? Well, following Steve Sanderson's blog, I understood that the listItems variable was actually the name of the collection which would be sent back to the post method.
So by adding this parameter to the controller post method:
IEnumerable<EBayListingUtility> listItems
The list is indeed sent back to the post method with the count being what it is supposed to be. Hurray!
We approach this in one of two ways:
1.) Client-side approach - you can use jquery/knockout whatever to append items to your table. This is fine for simple additions, but negates the use of c# in the view.
2.) Server-side approach (and usually used) - Basically, post your viewmodel back to an action that manually adds a list item;
[HttpGet]
public ActionResult AddItemUtilityRow()
{
return PartialView(new ItemListingUtility());
}
[HttpPost]
public ActionResult AddItemUtilityRow(BackstoreInventoryUtility viewModel)
{
viewModel.ListItemUtility.Add(new ItemListingUtility());
return PartialView(viewModel);
}
We have a number of ways using jquery of 'posting' to a different action (the one that simply adds an item). I would consider using jquery's ajax call to accomplish this.
But the premise is the same:
send the data from your page to the server
manipulate the data
reuse the view you created

(MVC4 - Razor) List of model in a model don't caught by the post method

I have MVC application with a model like this :
public class ListOfMyModel
{
public List<MyModel> MyModels { get; set; }
public Guid MyID { get; set; }
}
public class MyModel
{
// Some code like :
public string MyString { get; set; }
}
And my post method in my controller is like this :
[HttpPost]
public ActionResult EditMe(ListOfModel myList)
{
try
{
if (ModelState.IsValid)
{
List<MyModel> myModels = myList.MyModels;
foreach (MyModel model in myModels)
// Some code
return RedirectToAction("Index");
}
catch
{
// Some code
return View(myList)
}
return View(myList);
}
And my view : ( I use Kendo UI ) ( P.S : Some code has been stripped away and replaced by comment code )
#model MyApplication.Web.Models.ListOfMyModel
#{
ViewBag.Title = MyTitle;
Layout = "~/Views/Shared/_MyLayout.cshtml";
}
<div class="span1"></div>
<div class="span8">
<div id="list-wrapper">
<div class="k-content">
<form id="form" class="form-horizontal well span8 offset2" method="post" action="#Url.Action("EditMe")">
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery-1.9.1.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/kendo/2013.1.514/kendo.web.min.js")"></script>
<script src="#Url.Content("~/Scripts/kendo/2013.1.514/kendo.aspnetmvc.min.js")"></script>
<div class="offset2 span2">
<fieldset>
<legend> My title </legend>
<p>Some code :</p>
#Html.HiddenFor(m => m.MyID)
#for (int i = 0; i < Model.MyModels.Count; i++)
{
// Some code
<div class="control-group">
<label class="control-label">MyText : </label>
<div class="controls">
#(Html.Kendo().DropDownListFor(c => Model.MyModels[i].MyString)
.DataTextField("Text")
.DataValueField("Value")
.DataSource(dataSource => dataSource
.Read(read => read.Action("GetSomeThings", "MyController"))
)
.Value(Model.MyModels[i].MyString)
)
</div>
</div>
}
<div class="form-actions">
<button type="submit" class="btn btn-primary">Validate</button>
</div>
</fieldset>
</div>
</form>
</div>
</div>
</div>
But the problem is that when I push the submit button in my view, the method of my controller is called with all the data expected ( saw in Chrome ) but in this method, all of the model is null : The ID and the list... I don't known where the problem is ?
Thank you all for reading and trying to understand this, if you want more informations please tell me.
The MyId should be correctly received with your existing code.
The model binder can only match the value of inputs whose name have a matching property in the form. I.e. if you have an input like this <input name='bikes' ...> then your model should have a property Bikes so that the model binder can transfer the value from the input to the property. In your case you're creating input with dynamic names that doesn't have a matching property name. (NOTE: thei referes to the model you're using as the action parameter).
The farthest you can go is giving a series of input elements the same name, i.e. several elements like <input name='categories' ...> and receive it in an array parameter like string[] categories or a model which has a property like string[] Categories {get;set;}
In short, you have to redesign your model and view. If you used a List<string> instead of a List<MyModel>, and the view had a fixed name for the dropdow lists, like DropDownListFor(c => Model.MyModels, then the model binder would fill the MyModels property with the list of selected strings in each drop down list.
Hint: you can use a model for the View and receive a different model (or series of parameters) in the Action. In this way you can send more information to render the View, and receive a post with the essential data to process the user input.
See my answer to this question for alternatives. It explains something similar to this question.

In MVC3, How can the controller know the result of the unobtrusive validation?

I'm using C#, MVC3 and VS2010
I noticed that even though the valdiation results to false, the controller method still gets executed. This makes the validation useless on the server-side. Unless there is a way to get the result.
---- EDITS -----
Here is how i'm using it. Is it done properly? At least the validation messages show up, and the textbox turns red.
Model:
public class CategoriaModel
{
[Required(ErrorMessage="Nome é obrigatório!")]
[StringLength(10, ErrorMessage = "First Name max length is 10")]
public string Nome { get; set; }
}
View:
#Html.ValidationSummary(true)
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js") %>" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js") %>" type="text/javascript"></script>
#using (Html.BeginForm("Salvar", "Test", FormMethod.Post))
{
#Html.LabelFor(m => m.Nome)
#Html.EditorFor(m => m.Nome)
#Html.ValidationMessageFor(m=>m.Nome)
<input type="submit" value="Salvar" />
}
Controller:
public ActionResult Salvar(CategoriaModel catModel)
{
ViewBag.StatusMessage = "Ok!";
return View("Index");
}
testing ModelState.IsValid property, however if client side validation fails the Action should not be called. Check if you're actually validating all your Model properties.

MVC 3 - Adding multiple objects to a entity

I have a classes like this:
public class member
{
public string name {get;set;}
public IList<Note> notes {get;set;}
}
public class note
{
public string text {get;set;}
public datetime created {get;set;}
}
I want to have a page which inserts the member class - which i am fine with. My question lies in how to go about adding multiple notes to the member on the same page?
What would be the best way to go about this? (maybe some ajax solution to show sub forms for the note class)
Can anyone point me in the right direction of some related examples learning material?
Thanks in advance.
I'd create an Ajax form that posts to a method called AddNote(AddNoteViewModel viewModel) on your controller. AddNoteViewModel would contain all the information you need to create a new note. The AddNote Action Method would add the new note, SaveChanges and return a list of notes for the given Member. You can use a partial view for the content that is returned from AddNote.
On the Ajax form you should set UpdateTargetId to the id of the <div> you want to update with the latest list of notes.
Another option might be to use JQuery.
Here is a good example of both: Using Ajax.BeginForm with ASP.NET MVC 3 Razor
UPDATE : I've adapted Darin Dimitrov's example (from the link) to suit your scenario. This is off the top of my head so won't be perfect but it should give you a decent starting point
Model:
public class AddNoteViewModel
{
[Required]
public int MemberId { get; set; }
[Required]
public string Text { get; set; }
}
Controller:
[HttpPost]
public ActionResult AddNote(AddNoteViewModel model)
{
var member = //Get member from db using model.MemberId
member.Notes.Add(new Note{Text = model.Text, Created = DateTime.Now});
//SaveChanges();
var notes = //Get notes for member
return View(notes);
}
View:
#model AppName.Models.AddNoteViewModel
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<div id="result"></div>
#using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "result" }))
{
#Html.HiddenFor(x => x.MemberId)
#Html.EditorFor(x => x.Text)
#Html.ValidationMessageFor(x => x.Text)
<input type="submit" value="OK" />
}
Using JQuery:
View:
#model AppName.Models.AddNoteViewModel
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/index.js")" type="text/javascript"></script>
<div id="result"></div>
#using (Html.BeginForm())
{
#Html.HiddenFor(x => x.MemberId)
#Html.EditorFor(x => x.Text)
#Html.ValidationMessageFor(x => x.Text)
<input type="submit" value="OK" />
}
index.js:
$(function () {
$('form').submit(function () {
if ($(this).valid()) {
$.ajax({
url: this.action,
type: this.method,
success: function (result) {
$('#result').html(result);
}
});
}
return false;
});
});

Categories

Resources