Questions about ASP.NET MVC, Knockout.js and the mapping plugin abound. I have the following (very simple "getting started") example, which works. However, I would like feedback on whether this is the recommended approach. Primarily, I am interested in future models being significantly more complex (children, dropdowns, etc.). I feel maintaining two sets of models is not justifiable, thus my desire to use the mapping plugin.
PS: I wouldn't mind having the form submit, but I couldn't get that to work due to all different issues, apparently related to URL-encoding of string values with quotes by ko.utils.postJSON. Any updates on that would be appreciated also.
HTML
<h2>Simple View</h2>
<p>A simple model with 2 properties</p>
<p>Model property "Name": <span data-bind="text: Name"></span></p>
<p>Model property "Count" (just some integer): <span data-bind="text: Count"></span></p>
<form data-bind="submit: save">
<h3>Editing</h3>
<p>Name: <input type="text" data-bind="value: Name" /></p>
<p>Count: <input type="text" data-bind="value: Count" /></p>
<h3>Posting Back</h3>
<button type="submit">Update</button>
</form>
JavaScript
<script type="text/javascript">
var KoViewModel;
$(function () {
KoViewModel = ko.mapping.fromJSON('#Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model))');
ko.applyBindings(KoViewModel);
});
var save = function () {
$.ajax({
url: location.href,
type: 'POST',
data: ko.mapping.toJSON(KoViewModel),
contentType: 'application/json; charset=utf-8',
success: function (data) {
ko.mapping.fromJSON(data, KoViewModel);
}
});
}
</script>
Server-Side
public ActionResult Index()
{
Models.SimpleModel m = new Models.SimpleModel();
m.Name = "Some name";
m.Count = 1;
return View(m);
}
[HttpPost]
public JsonResult Index(Models.SimpleModel fc)
{
fc.Name += " (updated)";
fc.Count++;
return (new JsonResult() { Data = Newtonsoft.Json.JsonConvert.SerializeObject(fc) });
}
Thanks for any insights!
I would structure the script differently.
function SimpleModel(data) {
var self = this;
// data
self.Name = ko.observable();
self.Count = ko.observable();
// api
self.load = function (data) {
if (data) ko.mapping.fromJSON(data, {}, self);
};
self.save = function () {
return $.ajax({
type: 'POST',
url: location.href,
data: ko.mapping.toJSON(self),
contentType: 'application/json; charset=utf-8',
}).done(self.load);
};
// init
self.load(data);
}
// later... -------------------------------------------------------------------------
$(function () {
var data = '#Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model))';
ko.applyBindings( new SimpleModel(data) );
});
Advantages:
All data properties of the viewmodel are explicitly defined. This helps reduce bugs because it is immediately obvious what a viewmodel looks like. It also helps reduce run-time errors because properties will exist even if they happen to be missing on the JSON input. This way, the view can always bind without any problems.
You can move the viewmodel to a separate script file.
The viewmodel can initialize itself from the data you pass into the constructor. This will be useful if you refactor the application - for example when your viewmodel suddenly becomes a part of a larger viewmodel.
No awkward references to global variables or separate helper functions, the viewmodel is self-contained.
Returning the Ajax promise from the save method allows you to attach extra behavior without rewiring the viewmodel:
var vm = new SimpleModel(data);
// programmatically save the model
vm.save().done(function () {
// whatever
localStorage.setItem('lastAutoSave', Date.now());
});
Related
I get typeahead working in my project but only for names. I am not able to process id of that particular label coming in autocomplete.
Bootstrap typeahead-
<input type="text" name="names" value="" id="typeahead" data-provide="typeahead" />
Script-
<script type="text/javascript">
$(function () {
$('#typeahead').typeahead({
source: function (term, process) {
var url = '#Url.Content("~/Invoice/GetNames")';
return $.getJSON(url, { term: term }, function (data) {
return process(data);
});
}
});
})
</script>
Json method-
[HttpGet]
public JsonResult GetNames(string term)
{
var names = (from u in db.Contacts
where u.name.Contains(term)
select u).ToArray();
var results = names.Select(u => u.name);
return new JsonResult()
{
Data = results,
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
I am selecting whole table row on match. But How do I get Id with label name.
Or-
I can select whole row at server's side, But the problem persist for filtering result for label and name.
I used this info to also get the ID: http://fusiongrokker.com/post/heavily-customizing-a-bootstrap-typeahead (More complex... part)
I am have a problem binding a JSON result with knockout js. Below is my code
var AddDeparmentViewModel = function() {
var self = this;
self.AddDepartmentModel = {};
$.getJSON('/EventTracker/Department/GetEmptyModel/', function(data) {
ko.mapping.fromJS(data, {}, self.AddDepartmentModel);
});
};
$(document).ready(function() {
var departmentViewModel = new AddDeparmentViewModel();
ko.applyBindings(departmentViewModel);
})
Here is my HTML:
<div class="titleWrapper">
Add Department
</div>
<label>Department Name:</label>
<input data-bind="value: AddDepartmentModel.DepartmentName" id="departmentNameTextbox" type="text" />
<p data-bind="text: AddDepartmentModel.DepartmentName"></p>
The values I am returning are not appearing. Could someone point out where my mistake is. In this case the server is returning a single object not an array of objects.
Thanks!
Edit:
The Server returns this JSON:
{"DepartmentID":0,"DepartmentName":"Test","EVNTTRKR_Admins":[],"EVNTTRKR_Event": [],"EVNTTRKR_ItemCategories":[]}
Edit:
Here is the function GetEmptyModel:
public JsonResult GetEmptyModel()
{
var eventT = new EVNTTRKR_Departments();
eventT.DepartmentName = "Test";
return Json(eventT, JsonRequestBehavior.AllowGet);
}
The problem you are having seems to be fairly common. You are creating observables after binding the viewmodel. When this happens there is nothing for knockout to hook into and nothing gets rendered. A fix for this would be to make sure AddDepartmentModel is an observable and to set it to the return value rather than overwritting it with the fromJS.
var AddDeparmentViewModel = function() {
var self = this;
self.AddDepartmentModel = ko.observable({}); //important to have a default value so bindings dont break
$.getJSON('/echo/json/', function(data){
var mapped = ko.mapping.fromJS(data);
self.AddDepartmentModel(data); // here we are pushing values into the observable
});
};
This also requires fixes to the bindings as they now need to invoke AddDepartmentModel as a function:
<input data-bind="value: AddDepartmentModel().DepartmentName" id="departmentNameTextbox" type="text" />
Example fiddle with json request and population in callback.
http://jsfiddle.net/infiniteloops/YWC9N/
I created a jsfiddle here and replaced your getJson call with your server data (because I cannot call your getJson url). And it all seems to work which leads me to believe that an error is occuring in the getJson call.
Could you open your browser developer tools and look for any errors in the console. If there is an error and you're not sure what it means, add the error information to your original problem description.
var AddDeparmentViewModel = function() {
var self = this;
self.AddDepartmentModel = {};
var data =
{"DepartmentID":0,"DepartmentName":"Test","EVNTTRKR_Admins":[],"EVNTTRKR_Event": [],"EVNTTRKR_ItemCategories":[]
};
ko.mapping.fromJS(data, {}, self.AddDepartmentModel);
};
$(document).ready(function() {
var departmentViewModel = new AddDeparmentViewModel();
ko.applyBindings(departmentViewModel);
})
How to Pass value from text using #html.actionlink in asp.net mvc3 ?
None of the answers here really work. The accepted answer doesn't refresh the page as a normal action link would; the rest simply don't work at all or want you to abandon your question as stated and quit using ActionLink.
MVC3/4
You can use the htmlAttributes of the ActionLink method to do what you want:
Html.ActionLink("My Link Title", "MyAction", "MyController", null, new { onclick = "this.href += '&myRouteValueName=' + document.getElementById('myHtmlInputElementId').value;" })
MVC5
The following error has been reported, but I have not verified it:
A potentially dangerous Request.Path value was detected
Rather than passing your value using #Html.actionlink, try jquery to pass your textbox value to the controller as:
$(function () {
$('form').submit(function () {
$.ajax({
url: this.action,
type: this.method,
data: { search: $('#textboxid').val()},
success: function (result) {
$('#mydiv').html(result);
}
});
return false;
});
});
This code will post your textbox value to the controller and returns the output result which will be loaded in the div "mydiv".
to pass data from the client to the server you could use a html form:
#using (Html.BeginForm(actionName,controllerName)) {
<input type="text" name="myText"/>
<input type="submit" value="Check your value!">
}
be sure to catch your myText variable inside your controller's method
you can use this code (YourValue = TextBox.Text)
Html.ActionLink("Test", new { controller = "YourController", action = "YourAction", new { id = YourValue }, null );
public class YourController : Controller
{
public ActionResult YourAction(int id)
{
return View("here your value", id);
}
}
have a simple search form with a textbox. And upon submitting the form I send the contents of the textbox to a stored procedure which returns to me the results. I want the results to be displayed on the same page the form was, except just below it.
Right now I'm doing the following but it's not working out exactly the way I want:
sathishkumar,
You don't tag the question as being an ajax related solution or not. I'm going to present a simple ajax approach which may or may not be suitable.
in the controller:
public ActionResult Search(string searchTerm)
{
// you don't add code re your stored proc, so here is a repo based approach
var searchItems = _repository.Find(x => x.searchfield.Contains(searchTerm));
return PartialView("SearchPartial", searchItems);
}
main view (index.aspx or whatever) (below where your main content is defined, add):
<div id="searchResults"></div>
in another part of the page (semi psuedo-code):
<script type="text/javascript">
function getSearchResults() {
// #yoursearchbox is a textbox on the index.aspx aview
var tdata = { searchTerm: $('#yoursearchbox').val()};
// or your data in the format that will be used ??
$.ajax({
type: "GET",
data: tdata,
url : '<%= Url.Action("Search", "Home") %>',
success: function (result) { success(result); }
});
});
function success(result){
$("#searchResults").html(result);
}
</script>
You'd then add a partial view SearchPartial.ascx that contained your model for the search results.
Hope this helps.
You can use Ajax to solve the problem.
<div>
`#using (Ajax.BeginForm("action", "controller", new AjaxOptions
{
UpdateTargetId = "results",
HttpMethod = "GET",
}))
{
#Html.TextBox()
<input type="submit" value="Search" />
}`
<div id="results"></div>
</div>
I am having problems passing my model from my view via an Ajax call to my controller. All of the model properties that have Telerik html 'For' controls do not persist in the model. The only way I can access those values in the controller is using Request["control_name"]. All other standard controls like input type=text serialize just fine. What am I doing wrong?
Here is my ajax call:
function ImportLogFile() {
$.ajax({
url: '/Job/ImportLogFile',
type: 'POST',
data: $("form").serialize(),
success: function (data)
{
$('body').css('cursor', 'auto');
alert("Word Counts imported.");
},
error: function (xhr, status, error)
{
alert(status + ": " + strip(xhr.responseText).substring(0, 1000) + "...");
}
});
}
Controller:
[HttpPost]
public ActionResult ImportLogFile(tblJobTask model)
{
...
}
View:
#model viaLanguage.Jams.Data.tblJobTask
<html>
<head></head>
<body>
#using (Html.BeginForm())
{
<label class="editorLabel">CAT Tool Type:</label>
#{ Html.Telerik().ComboBoxFor(model => model.CatToolID)
.Name("JobTask_CatToolID")
.BindTo(new SelectList((IEnumerable)ViewData["CatTools"], "CatToolID", "Description"))
.HtmlAttributes(new { style = "width:220px;" });
}
<input id="btnImport" type="button" onclick="ImportLogFile();" />
}
</body>
</html>
i believe .Name("JobTask_CatToolID") is the source of problem. when you change Name attribute of combobox to something other than property name it will not automatically bound to the property by modelbinder. ModelBinder just looks at posted keys and then searches for matching properties in model and populates them accordingly. and when binder finds the key JobTask_CatToolID it probably finds no matching property in the model and hence it is not assigned to any property. you can check this by omitting the Name(---) method and then posting data to your controller.