I want to use jaydata JSLQ (JavaScript Language Query) to flatten a list of Post with one-to-many PostData into my ViewModel
My EF Entity looks like this:
public partial class Post
{
public Post()
{
this.PostData = new HashSet<PostData>();
}
public int Id { get; set; }
public virtual ICollection<PostData> PostData { get; set; }
}
My database contains these records:
Table: Post
Id;...
1;...
Table: PostData
Id;PostId;FieldType; FieldValue
1; 1; 10; "foo"
2; 1; 12; "bar"
3; 1; 34; "blah"
I want my view model in the client to look like this:
{id:1, title:'foo'}
That means, i want to put a filter on PostData that returns only FieldName==10, and i want to flatten that to a simple object.
How would I do that?
context.Posts.toArray(function(posts){console.dir(posts);})
returns an array of post objects. what next?
This is achieved witht he SelectMany() function in EF and on this approach is not yet supported by JayData. You can however achieve the same output with directly querying aganst PostData and filtering on Post attributes.
context.PostDatas
.filter( function(pd) { return pd.FieldType == 10 } )
.map( function(pd) { return {
PostID: pd.Post.Id,
PostDataId: pd.Id,
Title: pd.FieldValue }})
.toArray( ... )
Related
I am trying to hide and show columns in my grid using Kendo UI hidecolumn and showcolumn methods, it works however it looks like the grid lags in showing and hiding column when there are many columns in grid. I have about 50 columns in my grid. Also after hiding the columns, the column doesn't realign in the grid even after using refresh method of the grid.
Does anyone know what's an alternative way to hide/show column without loosing the performance in the grid?
Thanks.
Sanjeev
Well, first of all, you just need to know, that columns in grid is nothing more than usual array. So you can create this array separately and assign into grid configuration.
I done it for configuration of whole grid but I try show only columns part, because example will be relative long because I will try show all important methods. Anyway I hope it helps you.
So process can be (but don't have to) be something like this:
Prepare database table. If you need save only visible/hid value, you can be satisfied with basic table like this:
CREATE TABLE dbo.TableSettings(
Id INT PRIMARY KEY IDENTITY,
[Key] VARCHAR(MAX) NOT NULL,
Name VARCHAR(MAX) NULL,
Value BIT NULL );
In my case table has more columns but for save this information this is enough. [Key] is table name in my case (dbo.[User] for example), Name is name of column (FirstName for example) and Value is Visible/hid (1/0).
Now you need to load columns settings from database and save it into array witch will be assigned into grid config.
RunSynchronousAjax(address, null, function (e) {
var options = JSON.parse(e.Config);
var columns = options.Columns;
//This part is needed only if you have some special cases which are prepared on server. It is because on server you cannot assign function like in javascript but you have to assign only name of function which need to be eval in javascript.
for (var i = 0; i < options.columns.length; i++) {
options.columns[i].filterable.ui = eval(options.columns[i].filterable.ui);
if (options.columns[i].filterable.itemTemplate != undefined) {
options.columns[i].filterable.itemTemplate = eval(options.columns[i].filterable.itemTemplate);
}
}
}, defaultErrorCallBack);
Note: RunSynchronousAjax is my helper method for processign ajax request because I didn't want write it everztime I need it. It looks like this:
function RunSynchronousAjax(url, data, successCallback, errorCallback) {
jQuery.ajax({
contentType: 'application/json; charset=utf-8',
url: url,
data: data,
type: "POST",
cache: false,
success: function (json) {
successCallback(json);
},
error: function (data) {
errorCallback(data);
},
async: false
});
}
function defaultErrorCallback(data) {
alert("Unexpected error. Please, try press CTRL+F5.");
}
This function is way how I load config for table, now let's look on method in controller.
public JsonResult GetSetting()
{
KendoGrid grid = new KendoGrid();
grid.PrepareColumns();
string json = JsonConvert.SerializeObject(grid);
return Json(new
{
Config = json
});
}
Objects looks like this
public class KendoGrid
{
...
private List<Column> _Columns = new List<Column>();
[JsonProperty("columns")]
public List<Column> Columns
{
get { return this._Columns; }
set { this._Columns = value; }
}
...
}
public class Column
{
[JsonProperty("field")]
public string Field { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("width")]
public string Width { get; set; }
[JsonProperty("filterable")]
public object Filterable { get; set; }
[JsonProperty("template")]
public string Template { get; set; }
[JsonProperty("order")]
public int Order { get; set; }
[JsonProperty("format")]
public string Format { get; set; }
.... and more ... just what you need
public Column(
string field,
string title = null,
string type = "string",
string width = null,
string template = null,
object filterable = null,
string format = null,
...)
{
this.Field = field;
this.Title = title ?? field;
this.Type = type;
this.Width = width;
this.Template = template;
this.Order = order;
this.Format = format;
....
}
In point 3 there is method PrepareColumns, it looks like this
public void PrepareColumns()
{
var listOfColumns = TableSettings.GetColumns("dbo.TableName");
this.Columns.AddNotNull(Column.GetColumn<ObjectWhichIsDisplayedInGrid>(x => x.Id, listOfColumns,
type: "number",
width: "140px",
...));
this.Columns.AddNotNull(Column.GetColumn<ObjectWhichIsDisplayedInGrid>(x => x.Name, listOfColumns,
width: "250px",
...));
.....
}
//This method is based on you, It just load records from db...
public static List<TableSettings> GetColumns(string key)
{
List<TableSettings> result = new List<TableSettings>();
var query = Sql.Builder
.Select("*")
.From("dbo.TableSettings")
.Where("[Key] = #0", key)
.OrderBy("OrderInTable");
using (IDatabase db = DbConnection.Connect())
{
result = db.Fetch<TableSettings>(query);
}
return result;
}
public static Column GetColumn<T>(
Expression<Func<T, object>> expression,
List<TableSettings> settings,
string type = "string",
string width = null,
string template = null,
...)
{
var fieldName = PropertyNameHelper.PropertyName<T>(expression);
var fieldDescription = PropertyNameHelper.PropertyDescription<T>(expression);
var column = settings.Where(c => c.Name.Replace(" ", string.Empty).ToLower() == fieldName.Replace(" ", string.Empty).ToLower()).FirstOrDefault();
Column col = new Column(
field: fieldName,
title: fieldDescription,
type: type,
....);
return col;
}
public static string PropertyName<T>(Expression<Func<T, object>> expression)
{
return GetPropertyName(expression);
}
public static string PropertyDescription<T>(Expression<Func<T, object>> expression)
{
var propertyName = GetPropertyName(expression);
PropertyInfo prop = typeof(T).GetProperty(propertyName);
if (prop.IsDefined(typeof(DisplayAttribute), false))
{
return (prop.GetCustomAttribute(typeof(DisplayAttribute), false) as DisplayAttribute).Name;
}
return propertyName;
}
That's it. First 5 points is about load columns configuration from database. What is saved as not visible, will not be loaded so kendo grid will know only about columns which are visible.
Now, I have talked about my own columnMenu. It is only usual popup window with options. Important is that grid has set columnMenu to false, so default functionality is off. I have added button into toolbar and click event has opened kendoWindow where I had checkboxes with columns names. I can check what I want to display and after popup is closed/confirmed, some ajax request is called - it is saved into database and grid is refreshed.
Anyway mainly it can gives you idea, I believe there is much more solutions for this. If you find way how to create columns array dynamically you will win.
I have a custom javascript on the client side that I use to build up a querystring and pass over to my asp.net-mvc controller
var templateQueryString = BuildTemplate();
$.ajax({
url: '/MyController/Save?' + templateQueryString,
type: 'post',
dataType: 'json',
success: function (data) {
}
}
and on my controller all of the properties leverage the model binding so it comes in as a single object on the server side. NOTE: that this is a pretty complex object with arrays and arrays of sub objects:
public ActionResult Save(MyTemplate template)
{
}
the issue now is that I need to be able to convert from my C# object back to a string that represents "myTemplateQueryString" on the client side.
Is there any recommended way to take an object and do the "reverse" model binding. They key here is that it generates a string that I could use as a query string again in the future to pass into another asp.ent-mvc controller action.
Here is an example of the querystring that I am storing locally:
<input type="hidden" value="showIds=false&showRisks=false&
amp;statusIds=2&statusIds=1&statusIds=6&statusIds=8&
amp;statusIds=3&statusIds=9&showCompleted=0"
name="filterQueryString" id="filterQueryString">
As #haim770 said it would be easier if you used JSON in the request payload, and not the query string to pass your complex object to the server.
Regarding creating the query string from a model there is not a built-in method that does something like that or any recommended approach as far as i know. An obvious solution is to use reflection and build the query string from your properties.
Assuming your BuildTemplate class looks something like:
public class BuildTemplate
{
public bool ShowIds { get; set; }
public bool ShowRisks { get; set; }
public bool ShowCompleted { get; set; }
public int[] StatusIds { get; set; }
}
You can develop an extension method to convert any object to a QueryString. Here is some initial code you can start with:
public static class ObjectExtensions
{
public static string ToQueryString(this Object obj)
{
var keyPairs = obj.GetType().GetProperties().Select(p =>
new KeyValuePair<string, object>(p.Name.ToLower(), p.GetValue(obj, null)));
var arr = new List<string>();
foreach (var item in keyPairs)
{
if (item.Value is IEnumerable && !(item.Value is String))
{
foreach (var arrayItem in (item.Value as IEnumerable))
{
arr.Add(String.Format("{0}={1}", item.Key, arrayItem.ToString().ToLower()));
}
}
else
arr.Add(String.Format("{0}={1}", item.Key, item.Value.ToString().ToLower()));
}
return "?" + String.Join("&", arr);
}
}
Then you can easily invoke this code on any object to generate a query string:
var person = new BuildTemplate() { StatusIds = new []{ 1, 5, 8, 9 }, ShowRisks = true };
var queryString = person.ToQueryString();
This would generate a query string like:
"?showids=false&showrisks=true&showcompleted=false&statusids=1&statusids=5&statusids=8&statusids=9"
This query string should work just fine with the default model binder for the BuildTemplate class.
that's my function:
<script> function Calculate()
{
var ItemPrice = document.getElementById("price");
var weight = document.getElementById("weight");
var SelWeight = weight.options[weight.selectedIndex].value;
alert(SelWeight);
var Category = document.getElementById("SelectedCategory");
var SelCategory = Category.options[Category.selectedIndex].value;
alert(SelCategory);
}
</script>
i want to get SelCategories.Tax and SelCategories.Duty to add them to weight value and total price to show the total in a label.. I'm using ASP.NET MVC 4 and this is my Model that i want to use
public class CategoriesModel
{
public int CategoryID { get; set; }
public string CategoryName { get; set; }
public decimal Duty { get; set; }
public decimal Tax { get; set; }
public IEnumerable<SelectListItem> CategoriesList { get; set; }
}
I think the best approach here is to use Json and something like Vue.js, Knockout.js, etc. (but also you can do it without these libraries, if your case is simple).
First, you need to install Json support with a command in PM console:
PM> install-package NewtonSoft.Json
Then, in your view you can convert your model to javascript object like this:
#model ...
#using Newtonsoft.Json
...
<script type="text/javascript">
var data = #Html.Raw(JsonConvert.SerializeObject(this.Model));
</script>
Then you can access all the properties in your model with in plain JavaScript:
var id = data.CategoryID;
That's it! Use knockout (update 2018: this is obsolete, there is no reason you should use knockout now) if your logic is complicated and you want to make your view more powerful. It could be a little bit confusing for newbie, but when you get it, you'll gain the super-powerful knowledge and will be able to simplify your view code significantly.
You need to create actions (methods in the controller) that return JsonResult.
From the client side, make ajax calls to the server to recover and use that data. The easiest way to do this is to use any of the jQuery ajax methods.
public JsonResult GetData(int id)
{
// This returned data is a sample. You should get it using some logic
// This can be an object or an anonymous object like this:
var returnedData = new
{
id,
age = 23,
name = "John Smith"
};
return Json(returnedData, JsonRequestBehavior.AllowGet);
}
When you use a jQuery get to the /ControllerName/GetData/id, you'll get a JavaScript object in the success callback that can be used in the browser. This JavaScript object will have exactly the same properties that you defined in the server side.
For example:
function getAjaxData(id) {
var data = { id: id };
$.get('/Extras/GetData/1', // url
data, // parameters for action
function (response) { // success callback
// response has the same properties as the server returnedObject
alert(JSON.stringify(response));
},
'json' // dataType
);
}
Of course, in the success callback, instead of making an alert, just use the response object, for example
if (response.age < 18) { ... };
Note that the age property defined in the server can be used in the JavaScript response.
If you prefer a class try jsmodel. After converting the mvc view model to javascript it adds the benefit of retrieving DOM updates.
var jsmodel = new JSModel(#Html.Raw(Json.Encode(Model)));
Then anytime you want to get the latest state of the DOM do this to update your variable:
var model = jsmodel.refresh();
Website:
http://chadkuehn.com/jquery-viewmodel-object-with-current-values/
There is also a nuget:
https://www.nuget.org/packages/jsmodel/
var errors = '#Html.Raw(Json.Encode(ViewData.ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage)))';
var errorMessage=JSON.parse(errors);
I am sending data from my client to a method on my WebApi controller that accepts a class of ResponseFromClient. As it's WebApi this handles all the conversion from JSON and changes any field names to match the case convention differences between javascript and C#:
public class ResponseFromClient
{
public int QuestionId { get; set; }
public ICollection<AnswerDetail> Answers { get; set; }
public string Task { get; set; }
}
public class AnswerDetail
{
public int AnswerId { get; set; }
public string Text { get; set; }
public bool? Correct { get; set; }
public bool? Response { get; set; }
}
In Javascript I have an array called ans with each element looking like this:
qs.ans[index].id = element.answerId;
qs.ans[index].c = null;
qs.ans[index].r = false;
qs.ans[index].text = element.text;
I need to send the contents of a Javascript object responseFromClient to the C# server program:
var responseFromClient = {
questionId: qs.q,
answers: qs.ans,
task: task
};
$http({
method: 'POST',
url: url,
data: responseFromClient
})
Is there a simple way that I can set the answers field in the javascript variable responseFromClient so that it receives an array that contains objects that map to the AnswerDetail class. In other words I need to change it so that the answers gets and array of objects that are like this:
old > new
id > AnswerID
c > Correct
t > Text
r > Response
I was thinking to do it like I do where I remap qs.q > questionId. However ans is an array so I am not sure how to do this.
Is it not easier allround to map the object onto the class in your JavaScript
qs.ans[index].answerId = element.answerId;
qs.ans[index].correct = null;
qs.ans[index].response = false;
qs.ans[index].text
or
in the ajax call
data: { "answerId" : responseFromClient.id,
"correct": responseFromClient.c,
"response": responseFromClient.t,
"text": responseFromClient.text }
You can simply remap the values you want with arrays of old => new keys. There are more advanced ways of doing this, but I don't know all the details of your case...
function remap(obj, old_keys, new_keys){
var len = old_keys.length;
var new_obj = {};
for (var i = 0; i < len; i++) {
new_obj[new_keys[i]] = obj[old_keys[i]];
}
return new_obj;
}
var len = ans.length;
var old_keys = ["id", "c", "r", "text"];
var new_keys = ["AnswerId", "Correct", "Response", "Text"]
for (var i = 0; i < len; i++) {
ans[i] = remap(ans[i], old_keys, new_keys);
}
I hope someone could put me through a code to learn to call asmx webservices from backbone collection. The example i have put here is extremely simple
Collection
window["Persons"] = Backbone.Collection.extend({
model: Person,
url: "service.asmx/GetPeople"
});
note: I do have a service.asmx file at the point
Asmx End point
[WebMethod]
[ScriptMethod]
public static List<Person> GetPeople()
{
List<Person> people = new List<Person>(10);
for (int i = 0; i < 10; i++)
{
people.Add(new Person(i.ToString()));
}
return people;
}
The Model
public class Person
{
public string Name { get; set; }
public Person(string name)
{
Name = name;
}
}
when i do the below chrome xhr inspector informs me of this error
var family = new Persons();family.fetch();
Request format is unrecognized for URL unexpectedly ending in
'/GetPeople'
You will want to override the Backbone.sync() function to customize the persistence and retrieval of models from the server.
You can take a look at the annotated source code of how the Backbone.sync() function is overwritten for a local storage alternative.