Retrieve GridPanel model/store/columns dynamically from server side - c#

I have a GridPanel that must have its store model AND column model created dynamically, after a DB SP returns the columns of the table.
My question is how can I pass the value ( string or JSON )s from the server to the GridPanel?
Ext.define('Base.GridPanel', {
extend: 'Ext.grid.Panel',
xtype: 'gridpanel',
flex: #BFE.Frontend.Defaults.BaseGridPanel.flex,
hideMode: '#BFE.Frontend.Defaults.BaseGridPanel.hideMode',
collapsible: true,
constructor: function(id, title, columns, store)
{
this.id = id;
this.title = title;
this.columns = columns;
this.store = store;
this.callParent();
}
});
I use this custom defined GridPanel along with the following model and store for now.
Ext.define('Tasks', {
extend: 'Ext.data.Model',
fields:
[
{name: 'Case_ID', type: '#MCSJS.Models.DataType.Auto'},
{name: 'BP_Name', type: '#MCSJS.Models.DataType.Auto'},
{name: 'Project', type: '#MCSJS.Models.DataType.Auto'},
{name: 'Business_Unit', type: '#MCSJS.Models.DataType.Auto'},
{name: 'Task', type: '#MCSJS.Models.DataType.Auto'},
{name: 'Title', type: '#MCSJS.Models.DataType.Auto'},
{name: 'Last_Edit', type: '#MCSJS.Models.DataType.Auto'},
{name: 'Entity_Name', type: '#MCSJS.Models.DataType.Auto'},
{name: 'Process_Instance_ID', type: '#MCSJS.Models.DataType.Auto'},
{name: 'Start_of_Business', type: '#MCSJS.Models.DataType.Auto'},
{name: 'Last_User', type: '#MCSJS.Models.DataType.Auto'}
]
});
var myTaskStore = Ext.create('Ext.data.Store', {
storeId: 'myTasks',
model: 'Tasks',
autoLoad: true,
proxy:
{
type: 'ajax',
url: '/Task/GetMyTaskData',
reader:
{
type: 'json',
root: 'data'
}
}
});
This is how I create a GridPanel :
var columns = [ { text: 'Case ID', dataIndex: 'Case_ID' },
{ text: 'BP Name', dataIndex: 'BP_Name' } ];
new Base.GridPanel('#BFE.Frontend.MyTask.GridPanel', 'My Tasks', columns, myTaskStore)

Ext provides some support for that. You can send the model configuration by adding a metaData property to the server response. You can configure the name of the property with the metaProperty option.
The documentation doesn't make it obvious, but you can reconfigure the fields of the model this way. Here's the kind of response that would do that:
{
data: [...]
,metaData: {
// This will be recognized and processed automatically
// by the proxy
fields: [
{name: "id", type: "int"},
{name: "myField", type: "string"},
...
]
// This one is for our own usage, see bellow
,columns: [
{dataIndex: "id", text: "ID},
{dataIndex: "myField", text: "My field"},
...
]
}
}
As noted in the doc, when the data model changes, you'll want to update your components as well. Sencha has provided the metachange for that. Notice that, while documented in the proxy, this event will be relayed by the store.
Finally, to update the grid's column model, you have the reconfigure method. For example, you could modify your grid class in the following way to make it reconfigure itself automatically from the server response:
Ext.define('Base.GridPanel', {
extend: 'Ext.grid.Panel'
// ...
// You can add your listener in initComponent, if you're
// reluctant to extend a method docuemented as private...
,bindStore: function(store) {
// unbind previously bind store, if any
var previous = this.getStore();
if (previous) {
previous.un('metachange', this.onMetaChange, this);
}
// bind to the meta change event
this.getStore().on('metachange', this.onMetaChange, this);
this.callParent(arguments);
}
,onMetaChange: function(store, meta) {
var columns = meta.columns;
if (columns) {
this.reconfigure(null, columns);
}
}
});
Update
The onMetaChange method is called when the metachange event is fired, because I have registered it as a listener with this line:
this.getStore().on('metachange', this.onMetaChange, this);
The event itself is fired when the proxy detects some meta data in the server response. Concretely, that happens when a metaData property (or whatever name you may have configured as the metaProperty of the proxy) exists in the server response.
The listener is effectively passed the raw metaData object, that is present in the response, as its second argument (named meta in my example). So your server can put any information you need in it (e.g. new field labels, tooltip texts, etc.).
bindStore is a method that is already present in GridPanel. Here I override it because I need a place to register my event listener on the store. As its name suggests, this method is called to bind a store to the component. It can be the initial store, or a new one. That's one I've preferred to override this method instead of initComponent. In case the store is changed later in the component life, my custom listener will be unbound from the old store and attached to the new one.
The arguments keyword is an idiosyncrasy of Javascript. It represents all the arguments that have been passed to a function. callParent is a sweety provided by Ext to call the parent method; it takes an array as the arguments that will be passed to the parent. So this.callParent(arguments) calls the parent method without having to know what were precisely all the arguments of the overridden method. That's easier, and that's also more resilient to future changes, if the arguments of the method were to change...
I'd be glad to point you to a comprehensive guide about overriding in Ext... Unfortunately I couldn't find one with a quick search :-/

Related

AJAX post with model property

I want to do Something like
$.ajax({
url: '/Home/AjaxTest',
data: {
id: #Model.Prop
},
type: 'post',
cache: false,
success: function (response) {
console.log(response);
}
...
However, It didn't work. I know that if I have a hidden field for it, like
#Html.HiddenFor(model => model.Id)
then I can get the property value by
data: { id: $('input[name="Id"]').val() },
Still I wonder. Are there any way else to access the Model property more directly?
data: { id: "#Model.Prop" } // may or may not need quotes depending on data type.
If you do this, it will be the value of the Model.Prop field at the time of rendering the page so any modifications to inputs using that property will not be reflected.
If you want the actual data from an input control that has been rendered using EditorFor, etc:
data: { #(Model.Prop.GetType().Name): $('input[name="#(ViewData.TemplateInfo.HtmlFieldPrefix + "." + Model.Prop.GetType().Name)"]').val() }
This will render the javascript using the property name as the json index and the same name but including the model (and any containing models) prefix as the name of the element to find the value of.
Yes you can do if you follow the Model pattern of java script.
This is your java script file.
var JSModel = (function(){
var model = {};
var init = function(){
//Perfome your operations
};
return {
init:init,
model :model //return beacuse we want to acccess it in cshtml
};
})();
$(document).ready(function() {
JSModel .init();
});
Now in cshtml, you will do this:
//Invlude your JS file here and then
<script>
JSModel.model = #Html.Raw(Json.Encode(Model)); // You will get the model in your js file. it will in JSON form
</script>

AJAX call passing all but one of the fields to the method called in C#

In my cshtml I'm calling this function
function checkOutGroup(listOfIDs, listOfSites, itemType, controller)
After an AJAX call that runs correctly, I have a second AJAX call that takes in some of the data from the first in the success function and calls the second AJAX call.
if(!haveCheckedOutItems)
{
alert(listOfIDs[0] + " " + listOfIDs[1]);
alert(listOfSites);
$.ajax({
type: 'POST',
url: '/WorkQueues/CheckOutMultiple',
data: {idsToCheckOut: listOfIDs, listOfSites:listOfSites, itemType : itemType, controller: controller, checkIn : false},
datatype: 'json',
beforesend: function() {},
complete: function() {},
success: function(checkedOutItems) {....
In the cs file, the WorkQueues controller has a method called CheckOutMultiple with the following method signature:
public ActionResult CheckOutMultiple(List<int> idsToCheckOut, List<int> listOfSites, string itemType, string controller, bool checkIn)
Inside this code, idsToCheckOut is null for some reason, but the other 4 things being passed have all of the correct data. I'm noting doing anything with the listOfIDs except passing a copy of it in the first method.
Any thoughts?
EDIT 1: The parsed version of the call from the chrome networknig tab is
idsToCheckOut[]:72431
idsToCheckOut[]:56361
listOfSites[]:400216
listOfSites[]:549003
itemType:Visit
controller:Visits
checkIn:false
The non-parsed version is :
idsToCheckOut%5B%5D=72431&idsToCheckOut%5B%5D=56361&listOfSites%5B%5D=400216&listOfSites%5B%5D=549003&itemType=Visit&controller=Visits&checkIn=false
EDIT 2: Somehow I've managed to get the listOfSites broken in the same fashion, and regardless of how far back I go, I can't undo the break.
Correction:
Your post to the server is using the form's protocol. This is because your jquery doesn't specify to use json when sending to the server.
If you want to continue to post using the forms posting method that it is using it seems that you need to tell jquery to post using the traditional method for array values as explained here.
Using forms:
$.ajax({
type: 'POST',
url: '/WorkQueues/CheckOutMultiple',
data: {idsToCheckOut: listOfIDs, listOfSites:listOfSites, itemType : itemType, controller: controller, checkIn : false},
...
traditional: true
Using json:
$.ajax({
type: 'POST',
url: '/WorkQueues/CheckOutMultiple',
data: {idsToCheckOut: listOfIDs, listOfSites:listOfSites, itemType : itemType, controller: controller, checkIn : false},
...
contentType : "application/json; charset=utf-8"
If you can verify that listOfIDs in the calling script is not null, then it should be serializing/deserializing correctly using the pattern you provided. One thing that sticks out to me however is that you're not surrounding the attributes in the data object with quotes; sometimes having the data in the form of { foo : bar } instead of { "foo" : bar } or even { "foo" : JSON.stringify(bar) } if the data is complex can present problems.
I also noticed that the name of the problematic variable differs between the script and server method signature (with the exception of the boolean, which could be defaulting to false since it's not nullable). You could be lucking into your method working when the variable names match.

Why is my Kendo AutoComplete widget not binding to a JSON object?

I have the following code in an MVC controller:
public JsonResult ARequest()
{
Dictionary<string, object> AnObject = new Dictionary<string,object>();
AnObject["foo"] = new object[] {"item 1", "item 2", "item 3"};
return Json(AnObject, JsonRequestBehavior.AllowGet);
}
And it works as expected; when I call it from a browser, I get the following JSON object:
{"foo":["item 1","item 2","item 3"]}
I have another file, this time with a Kendo UI Autocomplete Widget. Here is the code:
<input id="products" style="width: 250px" />
/*...*/
$("#products").kendoAutoComplete({
filter: "contains",
minLength: 3,
dataTextField: foo,
dataSource: {
type: "odata",
pageSize: 10,
transport: {
read: {
url: "education-portal/ARequest"
}
}
}
});
As per the official examples here and here.
The problem is, when I load the page I don't get anything. The AutoComplete is blank and it stays blank. No results show up when I type anything in the box. Any idea what went wrong? I can't see it for the life of me.
There are several problems:
You should not define dataTextField since your array of value are not objects but strings.
You should say where in the received data is actually the array of items.
Is the type odata or JSON?
It should be something like:
$("#products").kendoAutoComplete({
filter: "contains",
minLength: 3,
dataSource: {
type: "json",
pageSize: 10,
transport: {
read: {
url: "education-portal/ARequest"
},
schema : {
data: "foo"
}
}
}
});
Example here : http://jsfiddle.net/OnaBai/rSjpS/
Since you are wrapping the collection inside a field called foo, you should specify this via the dataSource.schema.data options.
e.g.
dataSource: {
schema: {
data: "foo"
}
}
You do not have to specify any datavaluefield or datatextfield

Kendo UI Autocomplete opens twice

I'm using Kendo UI Autocomplete with an ASMX Service which returns a string array like this :
<ArrayOfString>
<string>One string</string>
...
</ArrayOfString>
All works fine except that the items list opens twice. One click close one list and the "second behind" is still open.
When the list's opening we can see the second list opens behind the first.
Any idea ?
JS Code:
<input id="autoCompleteTest" />
<script>
var dataSource = new kendo.data.DataSource({
serverFiltering: true,
transport: {
read: {
data: {
startswith: function(){
return $("#autoCompleteTest").data("kendoAutoComplete").value();
}
},
url: "WebService.asmx/GetStrings",
type: "POST",
}
},
schema: {
// specify the the schema is XML
type: "xml",
// the XML element which represents a single data record
data: "/ArrayOfString/string",
// define the model - the object which will represent a single data record
model: {
// configure the fields of the object
fields: {
// the "title" field is mapped to the text of the "title" XML element
value: "text()"
}
}
}
});
$("#autoCompleteTest").kendoAutoComplete({
minLength: 3,
dataValueField : "value",
dataTextField : "value",
dataSource: dataSource
});
</script>
C# Code:
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public String[] GetStrings(string startswith)
{
using (var dataContext = new DataClassesDataContext())
{
var query = from x in dataContext.product where x.shortName.StartsWith(startswith) select x.shortName;
return query.ToArray();
}
}
I've run into a similar issue and posted here
Please confirm that your autocomplete control is not located inside of another control that forces the Kendo control to render a second time.
do you client code when dom is ready for kendo multiselect:
$(document).ready(function () {
..yourcode.});
see: http://docs.telerik.com/kendo-ui/controls/editors/multiselect/overview#accessing-an-existing-multiselect

De-serializing object in MVC4 action results in null values

I am using jQuery 1.9.1 and MVC 4.
I have the following javascript:
$.ajax({
url: '/Home/doSomething',
type: 'POST',
data: JSON.stringify({ devices: [{ Id: 123, Name: "something", MapName: "map" },{ Id: 321, Name: "a name", MapName: "another map" }] }),
dataType: 'json',
contentType: 'application/json'
}
And the following c# in my HomeController:
[HttpPost]
public string doSomething( Device[ ] devices )
{
//stuff consuming that array
}
The Device class is defined like this:
[Serializable]
public class Device
{
public long Id;
public string Name;
public string MapName;
}
According to the debugger, the devices array is a 2-element array with all properties of each Device element being null.
According to chrome, the post data looks like this:
{
"devices": [
{
"Id": 123,
"Name": "something",
"MapName": "map"
},
{
"Id": 321,
"Name": "a name",
"MapName": "another map"
}
]
}
What is wrong here that is making MVC swear up and down that those objects are null, yet still giving me an appropriate-length array of them?
I have tried posting the array directly, without calling JSON.stringify on it.
I have tried posting the array inside an object as a property called "devices", again without strigifying it.
I have tried posting the array inside an object as a property called "devices", while only stringifying the array itself.
All of these result in bad behavior of one form or another. Either chrome does not post the appropriate data in the first place, or MVC does not de-serialize it.
I have scoured the web and can't seem to find any examples of why this should be broken.
Edit 2013-02-21 13:12 UTC-5:
I have also tried this without using JSON and just letting jQuery post it as form data.
Here is the code for that:
var postData = { devices: [{ Id: 123, Name: "something", MapName: "map" }, { Id: 321, Name: "a name", MapName: "another map" }] };
$.ajax({
url: '/Home/doSomething',
type: 'POST',
data: postData
});
And the C# is still the same as above.
The behavior is still the same as when it was JSON, though.
MVC sees an array of 2 elements, but all values of the objects in that array are default (0 for the integer and null for the strings).
Here's what finally worked...
First, I changed the javascript to make the object with a property called "devices" that contains the stringified array:
var postData = { devices: JSON.stringify([{ Id: 123, Name: "something", MapName: "map" }, { Id: 321, Name: "a name", MapName: "another map" }]) };
$.ajax({
url: '/Home/doSomething',
type: 'POST',
dataType: 'json',
data: postData
}
Then I made the controller action take a single string called "devices" and de-serialized it manually using JSON.Net:
[HttpPost]
public string MakeMmLinks( string devices )
{
List<Device> devicesList = JsonConvert.DeserializeObject<List<Device>>( devices );
...
}
This worked fine, but what bothers me is that object de-serialization is supposed to be something that MVC handles natively.
If anyone has any insight on what happened here or what I did wrong, I'd love to hear it.
In cases like this I've found tradition: true is very important. Not sure you need stringify
dataType: 'json',
traditional: true
You are posting an object with a property that contains an array of 2 devices.
If you want to just post the device array, than try the following.
var devices = [{ Id: 123, Name: "something", MapName: "map" }, { Id: 321, Name: "a name", MapName: "another map" }];
$.ajax({
url: '/Home/doSomething',
type: 'POST',
data: JSON.stringify(devices),
dataType: 'json',
contentType: 'application/json'
});

Categories

Resources