I want to use kendo AutoComplete in a kendoGrid for inline editing. When user inputs anything I'd use it to call a RESTful web service to return a list of products with names that start with the input value.
My questions are:
My web service expects a request looks like http://localhost/myService/appl where "appl" is the value that user enters and the prefix. However, kendo seems to always format the request something like http://localhost/myService?product=appl. How do I change the format?
How do I get the value that user has input in the grid (the AutoComplete textbox) so I can pass it in the request URL?
Define in the DataSource of your autocomplete an url function.
In that function, you can get typed value as:
var val = op.filter.filters[0].value;
and then return the url with the composed value.
Then it is something like:
dataSource: new kendo.data.DataSource({
transport: {
read: {
url: function (op) {
var val = op.filter.filters[0].value;
return "/myService/" + val;
}
}
}
})
Related
I'm creating an ASP.NET Core WebAPI that has a route with an optional parameter. In the return result I am embedding links to "previous" and "next" pages. I'm using Url.Link() to generate these links, but the link created is not generating a proper URL.
Example code:
[HttpGet("{page:int?}", Name = "GetResultsRoute")]
public IActionResult GetResults([FromQuery]int page = 0)
{
...
var prevUrl = Url.Link("GetResultsRoute", new { page = page - 1 });
var nextUrl = Url.Link("GetResultsRoute", new { page = page + 1 });
...
The URL generated will be something like:
"http://localhost:65061/api/results/1"
What I want is:
"http://localhost:65061/api/results?page=1"
What am I doing wrong here?
I think you are doing everything right here. It just MVC is able to match your [page] value in Url.Link to the route constraint HttpGet("{page:int?}", thus appending it as a part of the URL path, e.g. results/1
From the docs - Ambient values that don't match a parameter are ignored, and ambient values are also ignored when an explicitly-provided value overrides it, going from left to right in the URL. Values that are explicitly provided but which don't match anything are added to the query string.
In your case,
var prevUrl = Url.Link("GetResultsRoute", new { pageX = page - 1 });
would result in
http://localhost:65061/api/results?pageX=1
So ether append the query string parameter yourself, remove the constraint, or switch to MVC recommend format of URL, e.g. /api/results/xxx
I am building a Chrome extension which should write new rows into a Google Spreadsheet. I manage to read the sheet content but am not able to write an additional row. Currently my error is "400 (Bad Request)". Any idea what I am doing wrong here?
I have gone through the Google Sheets API documentation and other posted questions here but was not able to find any solution.
Here is the code which I use to GET the sheet content (this works):
function loadSpreadsheet(token) {
var y = new XMLHttpRequest();
y.open('GET', 'https://spreadsheets.google.com/feeds/list/spreadsheet_id/default/private/values?access_token=' + token);
y.onload = function() {
console.log(y.response);
};
y.send();
}
And this is the code I try to POST a new row (gives me "400 - Bad Request"):
function appendRow(token){
function constructAtomXML(foo){
var atom = ["<?xml version='1.0' encoding='UTF-8'?>",
'<entry xmlns="http://www.w3.org/2005/Atom" xmlns:gsx="http://schemas.google.com/spreadsheets/2006/extended">',//'--END_OF_PART\r\n',
'<gsx:name>',foo,'</gsx:name>',//'--END_OF_PART\r\n',
'</entry>'].join('');
return atom;
};
var params = {
'body': constructAtomXML("foo")
};
url = 'https://spreadsheets.google.com/feeds/list/spreadsheet_id/default/private/full?alt=json&access_token=' + token;
var z = new XMLHttpRequest();
z.open("POST", url, true);
z.setRequestHeader("Content-type", "application/atom+xml");
z.setRequestHeader("GData-Version", "3.0");
z.setRequestHeader("Authorization", 'Bearer '+ token);
z.onreadystatechange = function() {//Call a function when the state changes.
if(z.readyState == 4 && z.status == 200) {
alert(z.responseText);
}
}
z.send(params);
}
Note: spreadsheet_id is a placeholder for my actual sheet ID.
Follow the protocol and to make it work.
Assume spreadsheet ID is '1TCLgzG-AFsERoibIUOUUE8aNftoE7476TWYKqXQ0xb8'
First use the spreadsheet ID to retrieve list of worksheets:
GET https://spreadsheets.google.com/feeds/worksheets/1TCLgzG-AFsERoibIUOUUE8aNftoE7476TWYKqXQ0xb8/private/full?alt=json
There you can read list of worksheets and their IDs. Let use the first worksheet from the example. You'll find its id in feed > entry[0] > link array. Look for "rel" equal 'http://schemas.google.com/spreadsheets/2006#listfeed'.
In my example the URL for this worksheet is (Worksheet URL): https://spreadsheets.google.com/feeds/list/1TCLgzG-AFsERoibIUOUUE8aNftoE7476TWYKqXQ0xb8/ofs6ake/private/full
Now, to read its content use:
GET [Worksheet URL]?alt=json
Besides list-row feed, you'll also find a "post" URL which should be used to alter spreadsheet using list-row feed. It's the one where "rel" equals "http://schemas.google.com/g/2005#post" under feed > link.
It happens that it is the same URL as for GET request. In my case: https://spreadsheets.google.com/feeds/list/1TCLgzG-AFsERoibIUOUUE8aNftoE7476TWYKqXQ0xb8/ofs6ake/private/full. Just be sure to not append alt=json.
Now, to insert a new row using list-row feed you need to send POST with payload which is specified in docs. You need to send a column name prefixed with "gsx:" as a tag name. However it may not be the same as the column name in the spreadsheet. You need to remove any white-spaces, make it all lowercase and without any national characters. So to make your example work you need to replace <gsx:Name> with <gsx:name>.
Before the change you probably had the following payload message:
Blank rows cannot be written; use delete instead.
It's because the API didn't understand what the "Name" is and it just dropped this part of entry from the request. Without it there were no more items and the row was blank.
Alternatively you can read column names from the GET response. Keys from objects in feed > entry array that begins with gsk$ are columns definitions (everything after $ sign is a column name).
=================================================================
EDIT
To answer a question from the comments.
I've changed two things from your example:
function appendRow(token){
function constructAtomXML(foo){
var atom = ["<?xml version='1.0' encoding='UTF-8'?>",
'<entry xmlns="http://www.w3.org/2005/Atom" xmlns:gsx="http://schemas.google.com/spreadsheets/2006/extended">',
'<gsx:name>',foo,'</gsx:name>',
'</entry>'].join('');
return atom;
};
/*
var params = {
'body': constructAtomXML("foo")
};
*/
var params = constructAtomXML("foo");
url = 'https://spreadsheets.google.com/feeds/list/'+spredsheetId+'/default/private/full?alt=json&access_token=' + token;
var z = new XMLHttpRequest();
z.open("POST", url, true);
z.setRequestHeader("Content-type", "application/atom+xml");
z.setRequestHeader("GData-Version", "3.0");
z.setRequestHeader("Authorization", 'Bearer '+ token);
z.onreadystatechange = function() {//Call a function when the state changes.
if(z.readyState == 4 && z.status == 200) {
alert(z.responseText);
}
}
z.send(params);
}
1) <gsx:Name> to <gsx:name>. Without it you'll receive an error.
2) params object should be a String! Not an object with some 'body' key. You just need to pass a value you want to send to the server.
I just read that the mtype option in the jqGrid will determine how we will do the ajax call. GET will retrieve data and POST will send data.
When i load my jqGrid, i want to pass an extra parameter to my controller, in my js file:
url: 'Controller/Action1',
mtype: 'POST',
datatype: 'json',
postData: { ParentId: selectedParentId },
In my controller I have this:
public JsonResult Action1(ParentId)
{
// Retrieve child properties from db using ParentId
// Return json result
}
How will my jqGrid load the returned json data if my mtype is POST?
In my action, could i still get the other options of my jqGrid as a parameter like sort order, page size selected? Could i use something like this.Request.Param["sidx"] in my action?
In your controller you would take all the parameters the jqGrid would pass you:
public ActionResult GetGridData(string sidx, string sord, int page, int rows, bool _search, string filters, string ParentId)
{
....
int totalRecords = wholeList.Count();
var pagedQuery = wholeList.OrderBy(sidx + " " + sord).Skip((page - 1) * rows).Take(rows).ToList();
var jsonData = new
{
total = (totalRecords + rows - 1) / rows,
page = page,
records = totalRecords,
rows = (
from tempItem in pagedQuery
select new
{
cell = new string[] {
tempItem.ToString(),
...
}
}).ToArray()
return Json(jsonData, JsonRequestBehavior.AllowGet);
}
You can use the extra parameter to change what you feed back to the jqGrid, and you will also see you are passing in and using the parameters to handle paging.
mtype option defines the type of the HTTP request:
if it's set to GET (by default): the request parameters are appended in the http query in the Addressbar like this .../Controller/Action1?ParentId=selectedParentId
if it's set to POST, the request parameters are hidden when sending http query
In fact, the two methods send the same parameters with diffrent ways. So there is any diffrent on loading returned JSON data with GET or POST method
Or course you can get the other options of your jqGrid as a parameter like sort order
Sorry for my bad english
When I create a new item in the server-side using a Kendo UI data source, how do I update the ID of the client-side data item with the ID of the new record inserted in the database in the server-side?
Doing more research I have found this extremely useful information which, indeed, should be in the docs, but it is "hidden" in a not-so-easy-to-find forum search message:
http://www.kendoui.com/forums/ui/grid/refresh-grid-after-datasource-sync.aspx#2124402
I am not sure if this is the best approach, but it resolved my problem!
This solution simply uses the data source read method to update the model instances with data from server.
The precious info is where it is done: in the "complete" event of the transport.create object!
Here is the code:
transport: {
read: {
url: "http://myurl.json"
},
create: {
url: "http://mycreate.json",
type: "POST",
complete: function(e) {
$("#grid").data("kendoGrid").dataSource.read();
}
},
To avoid the additional server call introduced by the read method, if you have your create method return an object the Data Source will automaticly insert it for you.
Knowing that all you need to do is set the id field from the database and return the model.
e.g. psudo code for ASP MVC action for create.
public JsonResult CreateNewRow(RowModel rowModel)
{
// rowModel.id will be defaulted to 0
// save row to server and get new id back
var newId = SaveRowToServer(rowModel);
// set new id to model
rowModel.id = newId;
return Json(rowModel);
}
I've had the same problem and think I may have found the answer. If in the schema you define the object that holds the results, you must return the result of the created link in that same object. Example:
schema: {
data: "Results",
total: "ResultsCount", ....
}
Example MVC method:
public JsonResult CreateNewRow(RowModel rowModel)
{
// rowModel.id will be defaulted to 0
// save row to server and get new id back
var newId = SaveRowToServer(rowModel);
// set new id to model
rowModel.id = newId;
return Json(new {Results = new[] {rowModel}});
}
Just to add to Jack's answer (I don't have the reputation to comment), if your Create and Update actions return data with the same schema as defined in the kendo DataSource, the DataSource will automatically update the Id field as well as any other fields that may have been modified by the action call. You don't have to do anything other that form your results correctly. I use this feature to calculate a bunch of stuff on the server side and present the client with the results w/o requiring a complete reload of the data.
I am trying to access an object with form data sent to my controller. However, when I try to access objects I get values of null or 0. I used two methods, the first by serializing and the second by storing names and values in one object. (the code below sends/posts serialized)
Here is my JS...
$("#createUser").click(function() {
//store input values
var inputs = $('#newUserForm :input');
var input = $('#newUserForm :input').serializeArray();
console.log(input);
//if I want just the values in one object
var values = {};
$(inputs).each(function() {
values[this.name] = $(this).val();
});
console.log(values);
if(LiveValidation.massValidate( validObj )){
$.post('./adminPanel/createUser', function(input){
alert('Load was performed.');
//test confirmation box
$("#msgbox").html("Grrrrreat");
//drop down confirmation
$("#msgbox").slideDown();
});
} else {
//test fail box
$("#failbox").html("Fail");
$("#failbox").slideDown();
}
});
In the controller side I try to access data the following way...
$this->input->post("firstName")
where firstName is the name of the field.
Below is an image of the objects passed.
Top being serialized array and the bottom a single object with all the names and values of form...
If you're using jQuery, you can use jQuery's built in serialize/query string functions to get the data from a form: http://api.jquery.com/serialize/
In your case:
var data = $('#newUserForm').serialize(); // is a string like "firstName=jon"