Updating MVC Model List using Knockout.js - asp.net-mvc-4

I am working on an app which connects to XSockets via WCF and am able to get the data on the client side. I want to display this data using Grid.Mvc and have seen samples of using knockout.js, but I am not sure how to push this into my IEnumerable model so that I can see the View updated.
I have tried using the following code
#{
var initialData = new JavaScriptSerializer().Serialize(Model); }
$(function() {
ws = new XSockets.WebSocket("ws://127.0.0.1:4502/Book");
var vm = ko.mapping.fromJSON('#Html.Raw(initialData)');
ko.applyBindings(vm);
//Just write to the console on open
ws.bind(XSockets.Events.open, function (client) {
console.log('OPEN', client);
ws.bind('SendBook', function (books) {
jQuery.ajax({
type: "POST",
url: "#Url.Action("BooksRead", "Home")",
data: JSON.stringify(books),
dataType: "json",
contentType: "application/json",
success: function (result) {
//This doesnt work
/vm.push({Name:'Treasure Island',Author:'Robert Louis Stevenson'});
//vm.pushAll(result)
},
error: function (result){},
async: false
});
});
});
I am always receiving a null value for the parameter in the the BooksRead JsonResult method.
The model is a simple one
public class BookModel
{
public string Name {get; set;}
public string Author {get; set;}
}
I am returning a BookModel IEnumerable as my Model from the home controller on load and would want to insert new books into it as I receive them in the socket bind. This is because I am using it to generate the grid.
#Html.Grid(Model).Columns(c =>
{
c.Add(b => b.Name).Titled("Title");
c.Add(b => b.Author);
})
I would appreciate any pointers and guidance as to how I can go about achieving this.Many thanks
UPDATE
I am now able to get values in the controller action method after removing the dataType & contentType parameters from the ajax call. The controller method is as follows
public JsonResult BooksRead(string books)
{
BookModel data = JsonConvert.DeserializeObject<BookModel>(books);
List<BookModel> bookList = (List<BookModel>) TempData["books"];
if (bookList != null && data != null)
{
bookList.Add(data);
var bookString = JsonConvert.SerializeObject(bookList);
return Json(bookString);
}
return Json("");
}
I have added a vm.push call in the success handler and am passing the result value to it, but it still doesnt seem to add the new book in the Model. It seems I am doing it the wrong way as I am new to knockout js, jquery & ajax and trying to learn as I go along so please pardon my ignorance
UPDATE 2
I have made a few more changes.Like Uffe said, I have removed the Ajax call. I am adapting the StockViewModel from the StockTicker example to my BookViewModel and have added a parameter to the ctor to take in my IEnumerable model. This works & the item is added. The AddOrUpdate is working fine too & the objects are added to the collection but how can I get my model to be updated in the grid.
#{
var initialData = #Html.Raw(JsonConvert.SerializeObject(Model));
}
$(function() {
vm = new BookViewModel(#Html.Raw(initialData));
ko.applyBindings(vm);
ws = new XSockets.WebSocket("ws://127.0.0.1:4502/Book");
//Just write to the console on open
ws.bind(XSockets.Events.open, function(client) {
console.log('OPEN', client);
ws.bind('SendBook', function (msg) {
vm.AddOrUpdate(msg.book);
});
ws.bind(XSockets.Events.onError, function (err) {
console.log('ERROR', err);
});
});
});
The ViewModel is as follows
var BookViewModel = function(data) {
//this.Books = ko.observableArray(data);
this.Books = ko.observableArray(ko.utils.arrayMap(data, function(book) {
return new BookItem(book);
}));
this.AddOrUpdate = function(book) {
this.Books.push(new BookItem(book));
};
};

Related

asp.net MVC autocomplete with Html.TextBoxFor

I am modifying a preexisting application. I am trying to add the jquery autocomplete functionality. I have it working and calling the controller but the problem is the name attribute in the input field is "someClass.someMethod" so because I can't put this in the controller parameter like this, but still want to satisfy asp.net's Model Binding rules, what can I do?
Controller:
public JsonResult GetPs(string pN, PSModel pS)
{
List<string> pNs = null;
pNs= pS.PEntryBL.BusinessLayerPS.PS
.Where(x => x.Text.StartsWith(pN)).Select(y => y.Text).ToList();
return Json(pNs, JsonRequestBehavior.AllowGet);
}
View:
$(function () {
$("#autoCompletePNBox").autocomplete({
source: '#Url.Action("GetPs", "PS", new {pS = #Model})'
});
});
In Form:
#Html.Label("Scan PN: ", new { #class = "DFont"})
#Html.TextBoxFor(r => r.PEntryBL.PS, new { #class = "pageFont", id = "autoCompletePNBox" })
Using This Post
I was able to get it working by grabbing the value of the input field and passing it on each function call (or each time a user enters a character).
Now when the user selects the item from the autocomplete list, and selects submit then the frameworks form model binding behind the scenes will still work as originally implemented.
Here is the jquery code change:
<script type="text/javascript">
$(function () {
var src = '#Url.Action("GetPs", "PS", new {pS = #Model})'
$("#autoCompletePNBox").autocomplete({
source: function (request, response) {
$.ajax({
url: src,
dataType: "json",
data: {
pN: $("#autoCompletePNBox").val()
},
success: function (data) {
response(data);
}
});
}
});
});
I couldn't figure this out right away as my Jquery skills are not very strong. Hopefully this helps someone.

MVC Failed to load resource: the server responded with a status of 500 (Internal Server Error)

I have problem with my first MVC project. I'm trying to change values of DropDownList of surnames when select DropDownList of doctor types. I think my action is working. But I cannot use result in view.
Here my codes:
$(function () {
$('select#mCB').change(function () {
var docId = $(this).val();
$.ajax({
dataType: 'json',
data: 'spec=' + docId,
method: 'GET',
url: 'LoadDoctors',
success: function (data) {
$.each(data, function (key, Docs) {
$('select#shCB').append('<option value="0">Select One</option>');
$.each(Docs, function (index, docc) {
$('select#shCB').append(
'<option value="' + docc.Id + '">' + docc.Name + '</option>');
});
});
},
error: function (docID) {
alert(' Error !');
}
});
});
});
Actions:
public static List<Docs> GetDoctorBySpec(string spec)
{
List<Docs> list = new List<Docs>();
string query = "select ID, Familiyasi, Speciality from Doktorlar where Speciality=#spec";
SqlConnection Connection = new SqlConnection(DataBase.ConnectionString);
Connection.Open();
SqlCommand cmd = new SqlCommand(query, Connection);
cmd.Parameters.Add("#spec", spec);
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
list.Add(new Docs
{
Id = dr.GetString(0),
Name = dr.GetString(1)
});
}
return list;
}
enter code here
enter code here
[HttpGet]
public ActionResult LoadDoctors(string spec)
{
List<Docs> list = DoctorsService.GetDoctorBySpec(spec);
if (list == null)
{
return Json(null);
}
return Json(list);
}
And here my DropDownLists:
<div class="editor-label">
#Html.LabelFor(model => model.DoktorTuri)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.DoktorTuri, new SelectList(ViewBag.Vrachlar), new { #id = "mCB", #class = "vrachlar" })
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Shifokori)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Shifokori, Enumerable.Empty<SelectListItem>(), new { #id = "shCB", #class = "vrachlar" })
</div>
Where is my mistake? Thanks for answers
A 500 (Internal Server Error) almost always means that your throwing an exception on the server. Best guess is in your case it's because your method
DoctorsService.GetDoctorBySpec(spec);
does not accept null as a parameter and the value of spec is null because your never pass it value to the controller. As stann1 has noted your ajax option needs to be
data: {spec: docId},
In addition, you do not specify the JsonRequestBehavior.AllowGet parameter which means the method will fail.
All of this can be easily determined by debugging your code, both on the server and by using your browser tools (in particular the Network tab to see what is being sent and received along with error messages)
However this is only one of many problems with your code.
Firstly, unless Docs contains only 2 properties (the values you need for the option's value attribute and display text), your unnecessarily wasting bandwidth and degrading performance by sending a whole lot of data to the client which is never used. Instead, send a collection of anonymous objects
[HttpGet]
public ActionResult LoadDoctors(string spec)
{
List<Docs> list = DoctorsService.GetDoctorBySpec(spec);
if (list == null)
{
return Json(null, JsonRequestBehavior.AllowGet);
}
var data = list.Select(d => new
{
Value = d.Id,
Text = d.Name
});
return Json(data, JsonRequestBehavior.AllowGet);
}
Next, your script will only ever generate multiple <option value="0">Select One</option> elements (one for each item in the collection) because data in $.each(data, function (key, Docs) { is your collection, and Docs is the item in the collection. Your second $.each() function will never produce anything because Docs is not a collection.
You script should be (note I have use the short version $.getJSON() rather than the longer $.ajax() and also used the default id attributes generated by the html helpers - its not clear why you would want to change the id's using new { id = "mCB" }?)
var url = '#Url.Action("LoadDoctors")'; // never hard code your url's
var shifokori = $('#Shifokori'); // cache it
$('#DoktorTuri').change(function () {
$.getJSON(url, { spec: $(this).val() }, function(data) {
if (!data) { // only necessary depending on the version of jquery
// oops
}
// clear existing options and append empty option with NULL value (not zero)
// so validation will work
shifokori.empty().append($('<option></option>').val('').text('Select One'));
$.each(data, function (index, doc) {
shifokori.append($('<option></option>').val(doc.Value).text(doc.Text));
});
}).fail(function (result) {
// oops
});
});
The data param of the call needs to be a Javascript object literal:
$.ajax({
dataType: 'json',
data: {spec: docId},
method: 'GET',
....
});
Also, try to debug your controller and use a rest extension (or Fiddler) to test the payload, you would catch such error easily yourself ;)

How we get and post api in Titanium alloy?

How can we get and post api in Titanium alloy?
I am having the api of userDetails, I just want that how can i code to get the data from api.
function getUserDetails(){
}
Thanks in advance.
As you mentioned, you are using Titanium alloy.
So another approach be to extend the Alloy's Model and Collection ( which are based on backbone.js concept ).
There are already some implementation at RestAPI Sync Adapter also proper description/usage at Titanium RestApi sync.
I also provide the description and methodology used, in-case link gets broken:
Create a Model : Alloy Models are extensions of Backbone.js Models, so when you're defining specific information about your data, you do it by implementing certain methods common to all Backbone Models, therefor overriding the parent methods. Here we will override the url() method of backbone to allow our custom url endpoint.
Path :/app/models/node.js
exports.definition = {
config: {
adapter: {
type: "rest",
collection_name: "node"
}
},
extendCollection: function(Collection) {
_.extend(Collection.prototype, {
url: function() {
return "http://www.example.com/ws/node";
},
});
return Collection;
}
};
Configure a REST sync adapter : The main purpose of a sync adapter is to override Backbone's default sync method with something that fetches your data. In our example, we'll run through a few integrity checks before calling a function to fetch our data using a Ti.Network.createHTTPClient() call. This will create an object that we can attach headers and handlers to and eventually open and send an xml http request to our server so we can then fetch the data and apply it to our collection.
Path :/app/assets/alloy/sync/rest.js (you may have to create alloy/sync folders first)
// Override the Backbone.sync method with our own sync
functionmodule.exports.sync = function (method, model, opts)
{
var methodMap = {
'create': 'POST',
'read': 'GET',
'update': 'PUT',
'delete': 'DELETE'
};
var type = methodMap[method];
var params = _.extend(
{}, opts);
params.type = type;
//set default headers
params.headers = params.headers || {};
// We need to ensure that we have a base url.
if (!params.url)
{
params.url = model.url();
if (!params.url)
{
Ti.API.error("[REST API] ERROR: NO BASE URL");
return;
}
}
//json data transfers
params.headers['Content-Type'] = 'application/json';
switch (method)
{
case 'delete':
case 'create':
case 'update':
throw "Not Implemented";
break;
case 'read':
fetchData(params, function (_response)
{
if (_response.success)
{
var data = JSON.parse(_response.responseText);
params.success(data, _response.responseText);
}
else
{
params.error(JSON.parse(_response.responseText), _response.responseText);
Ti.API.error('[REST API] ERROR: ' + _response.responseText);
}
});
break;
}
};
function fetchData(_options, _callback)
{
var xhr = Ti.Network.createHTTPClient(
{
timeout: 5000
});
//Prepare the request
xhr.open(_options.type, _options.url);
xhr.onload = function (e)
{
_callback(
{
success: true,
responseText: this.responseText || null,
responseData: this.responseData || null
});
};
//Handle error
xhr.onerror = function (e)
{
_callback(
{
'success': false,
'responseText': e.error
});
Ti.API.error('[REST API] fetchData ERROR: ' + xhr.responseText);
};
for (var header in _options.headers)
{
xhr.setRequestHeader(header, _options.headers[header]);
}
if (_options.beforeSend)
{
_options.beforeSend(xhr);
}
xhr.send(_options.data || null);
}
//we need underscore
var _ = require("alloy/underscore")._;
Setup your View for Model-view binding : Titanium has a feature called Model-View binding, which allows you to create repeatable objects in part of a view for each model in a collection. In our example we'll use a TableView element with the dataCollection property set to node, which is the name of our model, and we'll create a TableViewRow element inside. The row based element will magically repeat for every item in the collection.
Path :/app/views/index.xml
<Alloy>
<Collection src="node">
<Window class="container">
<TableView id="nodeTable" dataCollection="node">
<TableViewRow title="{title}" color="black" />
</TableView>
</Window>
</Alloy>
Finally Controller : Binding the Model to the View requires almost no code at the controller level, the only thing we have to do here is load our collection and initiate a fetch command and the data will be ready to be bound to the view.
Path :/app/controllers/index.js
$.index.open();
var node = Alloy.Collections.node;
node.fetch();
Further reading :
Alloy Models
Sync Adapters
Hope it is helpful.
this is the solution for your problem:-
var request = Titanium.Network.createHTTPClient();
var done=false;
request.onload = function() {
try {
if (this.readyState == 4 && !done) {
done=true;
if(this.status===200){
var content = JSON.parse(this.responseText);
}else{
alert('error code' + this.status);
}
}
} catch (err) {
Titanium.API.error(err);
Titanium.UI.createAlertDialog({
message : err,
title : "Remote Server Error"
});
}
};
request.onerror = function(e) {
Ti.API.info(e.error);
};
request.open("POST", "http://test.com");
request.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
request.send({ test: 'test'});
if you don't get your answer please let me know.
Thanks

Sencha Touch: setValue on TextField does not work

In Sencha Touch 2 I have a controller which calls a custom 'prepopulate' method on button tap:
Ext.Ajax.request
({
method: 'GET',
url: myurl, //defined outside
withCredentials: true,
headers:{Authorization : auth},
success: function(response){
var data;
if(response.responseText.length > 0)
data = Ext.JSON.decode(response.responseText.trim());
console.log(data);
var fv = me.getFiscal();
console.log(fv);
fv.prepopulate(data);
Ext.Viewport.animateActiveItem('fiscal', me.getSlideLeftTransition());
},
failure: function(response){
Ext.Msg.alert('Server Error', 'Server down :( please try again later');
}
}
);
View code:
prepopulate : function (data) {
var me = this;
var companyTextField = me.down('#fiscalForm').down('#companyTextField');
var vatField = me.down('#fiscalForm').down('#vatField');
var fiscalCodeTextField = me.down('#fiscalForm').down('#fiscalCodeTextField');
var addressTextField = me.down('#fiscalForm').down('#addressTextField');
var cityTextField = me.down('#fiscalForm').down('#cityTextField');
var zipTextField = me.down('#fiscalForm').down('#zipTextField');
var countryTextField = me.down('#fiscalForm').down('#countryTextField');
console.log(vatField);
console.log((data.vat));
if(data){
if(data.company_name)
companyTextField.setValue(data.company_name);
if(data.vat)
vatField.setValue(data.vat);
if(data.fiscal_code)
fiscalCodeTextField.setValue(data.fiscal_code);
if(data.address)
addressTextField.setValue(data.address);
if(data.city)
cityTextField.setValue(data.city);
if(data.zip)
zipTextField.setValue(data.zip);
if(data.country)
countryTextField.setValue(data.country);
}
console.log(vatField);
}
The AJAX call works fine and it calls on success the prepopulate method passing the data retrieved from the server.
I try to initialize the TextFields using setValue() but the form looks 'brand new' when I open it using the browser
console.log() tells me that the _value private field is correctly set though... I'm groping in the dark right now ... any insight?
Thank You in advance.
M.
As you suggest the data i correctly retrieved and display in the console with the console.log, nonetheless the browser don't find any visible fields to modify the value when the setValue() is called.
The solution so far is to modify the ajax request as follows:
Ext.Ajax.request
({
....
....
success: function(response){
....
Ext.Viewport.animateActiveItem('fiscal', me.getSlideLeftTransition());
//view must be in the viewport before modifying data:
var task = Ext.create('Ext.util.DelayedTask', function () {
var fv = me.getFiscal();
fv.prepopulate(data);
});
task.delay(1000);
.....
....
...
..
.

Internal Server Error for basic CRUD operations in MVC 4 .NET

I am just beginning learning .NET MVC 4 framework and am having troubles with some basic CRUD operations yielding errors. I have a select element that upon change, sends an Ajax request to my server to find the Course given the id. Here is the ajax call:
$('.courseSelect').change(function () {
$.ajax({
url: "#Url.Action("Find", "Roster")",
data: {
courseId: $('.courseSelect').val()
},
dataType : 'json',
type: "GET",
success: function (data) {
console.log(data);
},
error: function (xhr, err, type) {
console.log(xhr);
console.log(err);
console.log(type);
}
});
});
This correctly send a request in the form of http://localhost:62020/Roster/Find?courseId=2
I then have a Find ActionRequest in my Roster controller:
[HttpGet]
public ActionResult Find(int? courseId)
{
StudentsViewModel selectedCourse = new StudentsViewModel();
List<Course> courses = Db.Courses.Where(s => s.Id >= 0).ToList();
foreach (Course c in courses)
{
selectedCourse.AllCourses.Add(c);
}
selectedCourse.currentCourse = Db.Courses.Find(courseId);
selectedCourse.AllStudents = Db.Students.Where(s => s.Id >= 0).ToList();
return View(selectedCourse);
}
However, even though the controller logic seems correct when doing a run-time debug of each line, the Ajax fails with a 500 Internal Server Error.
You can try to change:
ActionResult Find(int? courseId)
return View(selectedCourse);
To:
JsonResult Find(int? courseId)
return Json(selectedCourse, JsonRequestBehavior.AllowGet);
Also, I would check the data response to make sure the framework is successful in parsing the JSON.