Lib.Web.Mvc, JqGridColumnModel class's CellAttributes not work - asp.net-mvc

I am using Lib.Web.Mvc (version 6.1.0) to generate jqgrid in client.
Most attribute of JqGridColumnModel work fine but CellAttributes has not any effection here.
This is my Code:
configuration.Settings.ColumnsModels.AddRange(new JqGridColumnModel[]
{
new JqGridColumnModel("ProductID") { Index = "ProductID" },
new JqGridColumnModel("ProductName") { Index = "ProductName" },
new JqGridColumnModel("SupplierID") { Index = "SupplierID" },
new JqGridColumnModel("CategoryID") { Index = "CategoryID" },
new JqGridColumnModel("QuantityPerUnit") { Index = "QuantityPerUnit"},
new JqGridColumnModel("UnitPrice") { Index = "UnitPrice", CellAttributes*="value=1"}
});
I don't know if CellAttributes still not work in lastes version 6.1.0? Or I don't know how to use it.
Please give me idea to add more attributes to cell from server side (controller).

The JqGridColumnModel.CellAttributes is a proxy to cellattr property of colModel. This property takes a JavaScript function which should return string with attributes. Below you can find corresponding part from jqGrid documentation:
This function add attributes to the cell during the creation of the data - i.e dynamically. By example all valid attributes for the table cell can be used or a style attribute with different properties. The function should return string. ...
And from Lib.Web.Mvc documentation:
Gets or sets the function which can add attributes to the cell during the creation of the data (dynamically).
So in your case the code should look like this:
configuration.Settings.ColumnsModels.AddRange(new JqGridColumnModel[]
{
new JqGridColumnModel("ProductID") { Index = "ProductID" },
new JqGridColumnModel("ProductName") { Index = "ProductName" },
new JqGridColumnModel("SupplierID") { Index = "SupplierID" },
new JqGridColumnModel("CategoryID") { Index = "CategoryID" },
new JqGridColumnModel("QuantityPerUnit") { Index = "QuantityPerUnit" },
new JqGridColumnModel("UnitPrice") { Index = "UnitPrice", CellAttributes = "function() { return 'value=1'; }" }
});
UPDATE
There is one special case, the configuration import and export functionality. In this case all the settings for JavaScript functions, events and callbacks are ignored as those can't be serialized to JSON (this is by design).

Related

Knockout inheritance reusing same instance of base ViewModel?

I am using Knockout.js on an MVC application.
I am attempting to use inheritance, but I think may BaseViewModel is being reused, instead of a new one being created for each ViewModel instance.
I have a single BaseViewModel, and multiple ViewModels that extend it. The behavior is the same for all Views\ViewModels - They display a list of items as an html table, and clicking the edit button of a row pops up the selected row in a modal window so that you can edit and save the changes to that row.
These all work just fine on their independent views. However, when I try to display them all on a single view, I am having issues.
The lists for each View are all binding just fine.
Clicking edit on a selected row of the LAST list/table works fine.
But clicking edit on a selected row of the FIRST list/table fails with error.
Uncaught ReferenceError: Unable to process binding "value: function (){return Name }"
"Name" is a property of the model on the the second ViewModel, not the first.
It appears that error is because only one BaseViewModel is getting created, and so properties of that BaseViewModel are being overwritten every time I add a new ViewModel to the View.
Here is a fiddle with Example of the issue.
JSFiddle
Take a look at the editItem function and bring up the console.
Then click on Edit of the second table, you will see it properly prints to console the values passed into the base class, and the Modal window opens fine.
If you then cancel and click edit of the first table, you will see it prints the SAME values that the second ViewModel passed into the BaseVM, and NOT the values that it itself passed in.
Here is my knockout BaseViewModel as well the derived ViewModels.
EnvironmentAssetBaseViewModel = function(modalWindowName, apiResourceName, modelDefinition) {
var self = this;
self.modalWindowName = modalWindowName;
self.apiResourceName = apiResourceName;
self.modelDefinition = modelDefinition;
self.itemList = ko.observableArray([]);
self.selectedRow = ko.observable(modelDefinition);
function getData() {
//make ajax API call and store data in itemList
var data = [];
if (apiResourceName == 'activityLogs') {
data = [
{ Id: "1", Action: "a", Details: "d"},
{ Id: "2", Action: "b", Details: "e"},
{ Id: "3", Action: "c", Details: "f"}
]
} else {
data = [
{ Id: "109", Name: "a", Description: "d" },
{ Id: "209", Name: "b", Description: "e" },
{ Id: "309", Name: "c", Description: "f" }
]
}
self.itemList(data);
}
function editItem(item) {
console.log(self.modalWindowName);
console.log(self.apiResourceName);
console.log(self.modelDefinition);
self.selectedRow(item);
$(modalWindowName).modal('show');
}
function cancel(item) {
$(modalWindowName).modal('hide');
}
return {
self: self,
itemList: self.itemList,
selectedRow: self.selectedRow,
getData: getData,
editItem: editItem,
cancel: cancel
}
};
ActivityLogsViewModel = function() {
var self = this;
var modalName = "#activityLogModal";
var apiResourcename = "activityLogs";
var modelDefinition = { Id: "", Action: "", Details: "" };
ko.utils.extend(self, new EnvironmentAssetBaseViewModel(modalName, apiResourcename, modelDefinition));
getData(); //initial data load
return {
self: self,
editItem: editItem,
cancel: cancel
}
};
ko.applyBindings(ActivityLogsViewModel, document.getElementById('activityLogsDiv'));
DomainsViewModel = function() {
var self = this;
var modalName = "#domainModal";
var apiResourcename = "domains";
var modelDefinition = { Id: "", Name: "", Description: "" };
ko.utils.extend(self, new EnvironmentAssetBaseViewModel(modalName, apiResourcename, modelDefinition));
getData(); //initial data load
return {
self: self,
editItem: editItem,
cancel: cancel
}
};
ko.applyBindings(DomainsViewModel, document.getElementById('domainsDiv'));
You're mixing up some methods of creating "instances" of functions.
You've defined constructor functions that "manually" return a plain object. This is perfectly fine, but it means you don't call them with the new keyword.
The new keyword creates a new this context, but since you're not using it, your first line:
var self = this;
will set self to whatever the context of the caller of the factory method is. In this case, window.
This means all your constructor functions/factories are polluting the window namespace, resulting in a weird combination of properties that are missing/overridden.
Try replacing var self = this with var self = {} to create a new object. Make sure you call your functions to create viewmodels: var activityVM = ActivityLogsViewModel().
You'll run in to some other bugs (for example, getData() needs to be self.getData()), but you should be able to figure it out.
Let me know if you need more help.

use specific field as a backbone's model data

I'm retrieving a json and want to use a field(don't know the right term) in the json to initialize backbone model after fetch.
It seems I have to implement parse function, but how?
1) You need to define defaults object on your model setting up properties you want to populate from a request; 2) then you need to filter (those) necessary fields and construct new object in the model's parse method:
var model = Backbone.Model.extend({
defaults: {
swordType: null
},
parse: function(data) {
// filter what you need
var swordTypeValue = null;
if(data.hasOwnProperty('swordType')) {
swordTypeValue = data.swordType;
}
// construct attributes from it
return { swordType: swordTypeValue };
}
});

Refreshing lookups in SPA with Breeze.js when value has changed

I have an application that records a transaction and the user can pick the category from a drop down.
Categories are loaded up at application startup as they are "mostly" static / rarely going to change.
So, in my datacontext.js I do the usual and prime my data;
var primeData = function () {
var promise = Q.all([
getLookups(),
getBankAccountPartials(null, true)])
.then(applyValidators);
return promise.then(success);
function success() {
datacontext.lookups = {
categories: getLocal('Categories', 'name', true),
transactiontypes: getLocal('TransactionTypes', 'name', true),
payees: getLocal('Payees', 'name', true)
};
log('Primed data', datacontext.lookups);
}
function applyValidators() {
model.applyBankAccountValidators(manager.metadataStore);
}
};
function getLookups() {
return EntityQuery.from('Lookups')
.using(manager).execute()
.then(processLookups)
.fail(queryFailed);
}
Now, occasionally in an Admin screen the user can edit and add a category.
In the categoryadd.js viewmodel my save code looks something like this (extract shown);
save = function () {
isSaving(true);
datacontext.saveChanges()
.then(goToEditView).fin(complete);
function goToEditView(result) {
router.replaceLocation('#/categorydetail/' + category().id());
}
function complete() {
isSaving(false);
}
},
How do I refresh just the Categories lookup data? Or, am I just doing this wrong and should perhaps NOT have categories as a lookup?
Thanks.
Breeze.js synchronises automatically and knows to search out the Category and update it in its lookup list.
I checked this by calling datacontext.lookups from the browser console after the save had been performed and inspecting the objects it showed me the category name had been refreshed.

how to create dynamic url in collection and model using backbone

My collection and model like this:
detail_userid = 0;
detail_contactid = 0;
var ContactDetail = Backbone.Model.extend({
urlRoot: URL_CONTACTS1+detail_userid+"/"+detail_contactid
});
var ContactDetailCollection = Backbone.Collection.extend({
model: ContactDetail,
url: URL_CONTACTS1+detail_userid+"/"+detail_contactid
})
The entrance is:
ContactDetailManagePageModel.prototype.init = function(m,n){
detail_userid = m;
detail_contactid = n;
var myContactDetails = new ContactDetailCollection();
var contactDetailListView = new ContactDetailListView({
collection: myContactDetails
});
myContactDetails.fetch({reset:true});
}
But when it runs,the url is :http://localhost:8080/ws/users/contacts/0/0,it means that the assignment to detail_userid and detail_contactid is unsuccessful,I don't know why.
Hope for your help.Thanks.
I think you are statically definining the urlRoot and url properties before you are running the init of the PageModel (not quite sure where you are getting m and n from though...)
Both url and urlRoot can be a function, so you can pass in options during instantiation and have them dynamically set on the model.
Simple example covering defining the collection and then creating one
var ContactDetailCollection = Backbone.Collection.extend({
model: ContactDetail,
url: function(){
return URL_CONTACTS1 + this.options.detail_userid + "/" + this.options.detail_contactid;
}
});
var myContactDetails = new ContactDetailCollection({
detail_userid: foo,
detail_contactid: bar
});
As I mentioned, I'm not sure what your init function is doing, I'm guessing it's something custom from your app that I don't need to worry about.
I'm fairly sure the main thing to take away is to set url and urlRoot dynamically
I would fulfill the accepted answer with few remarks.
First parameter when initializing Backbone.Collection is array of models, then options. To create an empty collection with options you should do next
var c = new Backbone.Collection(null, {opt1: val1, opt2: val2});
Actually, you can't access this.options in url function, bec. there are no options like in a model. What you can do, is assign required properties from options upon initialization.
initialize: function (models, options) {
// `parseInt()` is used for consistency that `id` is numeric, just to be sure
this.detail_userid = parseInt(options.detail_userid);
this.detail_contactid = parseInt(options.detail_contactid);
}
Later you can access them like this:
url: function() {
return URL_CONTACTS1 + this.detail_userid + "/" + this.detail_contactid;
}
I wanted to use the HATEOAS href from one model to fetch data of another model. It worked to simply set the url on the newly created collection instead of defining it right away in the constructor.
var DailyMeasuresCollection = Backbone.Collection.extend({
//url : set dynamically with collection.url = url
model : DailyMeasuresModel,
parse : function(data) {
return data._embedded.dailyMeasures;
}
});
var DailyMeasuresTopicListItemView = Backbone.View.extend({
//...
events : {
'click .select-topic' : 'onClick'
},
onClick : function() {
var topicMeasures = new DailyMeasuresCollection()
topicMeasures.url = this.model.attributes._links.measures.href // <- here assign
var topicMeasuresView = new DailyMeasuresListView({
collection : topicMeasures
});
topicMeasures.fetch()
}
});

Change title when opening dialog from knockout binding

I have based my code on this example
http://jsfiddle.net/rniemeyer/WpnTU/
When you select an item I want the title of the dialog to have a observable's value
I managed to to it by creating another custom binding
ko.bindingHandlers.dialogOptions = {
update: function(element, valueAccessor) {
var options = ko.utils.unwrapObservable(valueAccessor());
if (options ) {
$(element).dialog(options);
}
}
}
Added a new observable to viewmodel and set it when the item is selected
this.selectProduct = function(product) {
self.dialogOptions({ title: product.name() });
self.selectedProduct(product);
}
Working example: http://jsfiddle.net/WpnTU/76/
It works but I do not like it, it adds a new observable which is very coupled with the GUI, it would be much nicer if I could use the already exiting selectProduct observable and point out the name property in the GUI something like { title: selectProduct.name }
Here is a sample that moves the .dialog calls into the update function and unwraps the options, so that it will be triggered any time that something changes.
//custom binding to initialize a jQuery UI dialog
ko.bindingHandlers.jqDialog = {
init: function(element) {
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).dialog("destroy");
});
},
update: function(element, valueAccessor) {
var options = ko.toJS(valueAccessor());
if (options) {
$(element).dialog(options);
}
}
};
I added a computed observable to your sample just to handle the selectedProduct being null (could be done in-line).
http://jsfiddle.net/rniemeyer/Gt5Hw/

Resources