I have a bit of a weird situation...
I have a DataView that's backed by a store. However, in the XTemplate, I'm trying to show the quantity of a certain type of JSON record. Each record has a property value pair that denotes its type. For example:
data: [
{ type: 'W' },
{ type: 'A' },
{ type: 'W' },
{ type: 'W' }
]
So there are 3 JSON objects of 'W' and 1 JSON object of type 'A'. The number of 'W' should be displayed in the HTML element with the class 'total-pending'; 'A', in the HTML element with the class 'total-approved.'
My XTemplate:
tpl: Ext.create('Ext.XTemplate',
'<div style="text-align:center">Leave Requests</div>',
'<table cellpadding="0" class="table-wrap">',
'<tr>',
'<td class="x-leave-request approved"><span class="total-approved">0</span>Approved</td>',
'<td class="x-leave-request pending"><span class="total-pending">0</span>Pending</td>'denied">0</span>Denied</td>',
'</tr>',
'</table>'
)
I've read a bit about templates and stores, but I can't seem to figure out how to update those numbers. I tried to add a function callback to store.on('load', function{}) to change the HTML element via Ext.get(Ext.query('span.total-pending')[0]).update(3) but it had no effect.. Any ideas?
I am not sure will help your request but try and share result with us.
Try to make a function to count total values of W and A then call appropiate function inside the XTemplate via this.function()
tpl: Ext.create('Ext.XTemplate',
'<div style="text-align:center">Leave Requests</div>',
'<table cellpadding="0" class="table-wrap">',
'<tr>',
'<td class="x-leave-request approved"><span class="total-approved">{totalw:this.getTotalw()}</span>Approved</td>',
'<td class="x-leave-request pending"><span class="total-pending">{totala:this.getTotala()}</span>Pending</td>'denied">0</span>Denied</td>',
'</tr>',
'</table>'
)
I solved it using the hint that Oguz gave. What I ended up doing was creating an Ext.util.Hashmap instance, whose pairs were status type and total (e.g. <'W', 10>). Then I called:
<tpl for=".">
{[this.updateStatusTotal(values.type)]}
</tpl>
...
updateStatusTotal : function(type) {
var me = this,
map = me.map,
total = map.get(type);
map.put(type, ++total);
}
then later in the code where the <td> elements are, I call:
{[this.getStatusTotal('W')]}
Done :)
Related
Before upgrading to Extjs 6 we were using a tagfield component where its displayField was Ext.String.htmlEncode('description') where description was the description field of the store's record. This field doesn't only contain simple text but instead is in the form of: <img src="link..." style="..."/> RECORD_DESCRIPTION. Using the htmlEncode function it would actually render the tagfield's records with the linked image and the record's description.
However after upgrading to version 6 this stopped working. It now just produces the full text instead of rendering the image. Its like htmlEncode stopped working all of a sudden. The thing is that in an ItemSelector field where we do the same thing everything works perfectly using the exact same method. Did they break the tagfield component so as to not be able to show html?
Regardless, how can I reproduce what I did before? I tried with displayTpl config but it doesn't seem to work.
Instead of Ext.String.htmlEncode('description') try just 'description' in displayField. DisplayField affects what you see when you are selecting, and for that, it will display your HTML. It also affects how it displays the selections you have made, and there doesn't seem to be a way to display HTML there. It displays as plain text. There is another attribute, labelTpl, that formats the items you have already selected. You can use that to display just the names of things you have already selected. (LabelTpl used to display formatted HTML, but doesn't in ExtJs6.) Suppose your store record also had a 'name' field that is just plain text; you would put that inside curly braces:
xtype:'tagfield',
displayField: 'description',
labelTpl: '{name}',
...
Then you will get formatted HTML while making a selection, and plain text in the selected items. That's the best I've come up with so far.
Using ExtJS 6.5.3
This is the best I was able to figure out for a tag field in terms of creating a complex display statement for both list and tags.
The display supports CSS and other HTML tags
The labelTpl does not support anything but plain text, but you can override it's x-tagfield-item-text style however, it's all or nothing.
{
cls: "triggerstagfield",
displayField: '[(values.key != "all") ? values.label + " (<b>" + values.key + "</b>)" : values.label]',
labelTpl: '{[(values.key != "all") ? values.label + " (" + values.key + ")" : values.label]}',
valueField: "key",
store: [{
"key": "all",
"label": "All"
}, {
"key": "880",
"label": "International House of Pancakes"
}]
},
and this is the CSS
.triggerstagfield .x-tagfield-item-text {
font-weight: bold;
font-size: 0.9em;
}
I worked around it by extending the built in tag field, overriding the function getMultiSelectItemMarkup. In that function I removed the call to Ext.String.htmlEncode on the label.
With this you can use
{
xtype: 'tagfieldhtmllabel',
labelTpl: '<span style="background-color:#{colorbg};">{name}</span>',
...
}
This is the code that works on Ext 6.2.1
Ext.define("Private.ui.TagFieldHtmlLabel", {
extend: 'Ext.form.field.Tag',
alias: 'widget.tagfieldhtmllabel',
// TODO EXT UPGRADE. WHEN UPGRADING FROM 6.2.1 update this
getMultiSelectItemMarkup: function() {
var me = this,
childElCls = (me._getChildElCls && me._getChildElCls()) || '';
// hook for rtl cls
if (!me.multiSelectItemTpl) {
if (!me.labelTpl) {
me.labelTpl = '{' + me.displayField + '}';
}
me.labelTpl = me.lookupTpl('labelTpl');
if (me.tipTpl) {
me.tipTpl = me.lookupTpl('tipTpl');
}
me.multiSelectItemTpl = new Ext.XTemplate([
'<tpl for=".">',
'<li data-selectionIndex="{[xindex - 1]}" data-recordId="{internalId}" role="presentation" class="' + me.tagItemCls + childElCls,
'<tpl if="this.isSelected(values)">',
' ' + me.tagSelectedCls,
'</tpl>',
'{%',
'values = values.data;',
'%}',
me.tipTpl ? '" data-qtip="{[this.getTip(values)]}">' : '">',
'<div role="presentation" class="' + me.tagItemTextCls + '">{[this.getItemLabel(values)]}</div>',
'<div role="presentation" class="' + me.tagItemCloseCls + childElCls + '"></div>',
'</li>',
'</tpl>',
{
isSelected: function(rec) {
return me.selectionModel.isSelected(rec);
},
getItemLabel: function(values) {
// UPGRADE - removed htmlEncode here
return me.labelTpl.apply(values);
},
getTip: function(values) {
return Ext.String.htmlEncode(me.tipTpl.apply(values));
},
strict: true
}
]);
}
if (!me.multiSelectItemTpl.isTemplate) {
me.multiSelectItemTpl = this.lookupTpl('multiSelectItemTpl');
}
return me.multiSelectItemTpl.apply(me.valueCollection.getRange());
}
});
I want to display a single number (that would change, that's why I'm not hard coding it) from am object. This, however, doesn't display anything.
var game_data = {
amount: 1,
yes_left: 3,
nos_left: 1
};
var game_panel = {
view: "form", id:"game_panel",
rows:[
{cols:[
{
template:'<span class="main_title">Money: $#amount#.00 </span>',
data:{amount:game_data.amount},align:"center"
},
}
]
};
I've also tried returning it as a variable:
template: function(game_data) {return '<span class="main_title">Money: ' + game_data.amount + '.00 </span>'}
Any ideas how to get that to display?
The code that you are using is correct. You can check the the next working snippet http://webix.com/snippet/82566e82
If you plan to update this number dynamically, it will be better to use a bit different syntax
id:"t1",
template:'<span class="main_title">Money: $#amount#.00 </span>',
data: game_data, align:"center"
and for number updating, be sure to call template.refresh, as template doesn't track data changes automatically.
game_data.amount += 2;
$$("t1").refresh();
http://webix.com/snippet/e3b0450d
I have a column in a Kendo grid that I want to perform some specific logic for when rendering, and am using Angular. I have the grid columns set up using the k-columns directive.
After looking at the documentation, it seemed simple: I could add the template option to my column, define the function to perform my logic, and pass the dataItem value in. What I have looks something like this:
k-columns='[{ field: "Name", title: "Name",
template: function (dataItem){
// Perform logic on value with dataItem.Name
// Return a string
}
}]'
However, running this causes a syntax error complaining about the character '{' that forms the opening of the block in my function.
I have seen several examples of defining a template function in this format. Is there something else that needs to be done for this to work? Am I doing something incorrectly? Is there another way of defining the template as a function and passing the column data to it? (I tried making a function on my $scope, which worked, except I couldn't figure out how to get data passed into the function.)
Thank you for your help.
It appears that defining a column template in this fashion isn't supported when using AngularJS and Kendo. This approach works for projects that do not use Angular (standard MVVM), but fails with its inclusion.
The workaround that a colleague of mine discovered is to build the template using ng-bind to specify a templating function on the $scope, all inside of a span:
template: "<span ng-bind=templateFunction(dataItem.Name)>#: data.Name# </span>"
This is the default column templating approach that is implemented by Telerik in their Kendo-Angular source code. I don't know yet if the data.Name value is required or not, but this works for us.
Warning: Don't have access to Kendo to test the code at the moment, but this should be very close
In your case, you are assigning a a string to the value of k-columns and that string contains the the word function and your curly brace {
You need to make sure the function gets executed ... something like this:
k-columns=[
{
field: "Name",
title: "Name",
template: (function (dataItem){
// Perform logic on value with dataItem.Name
// Return a string
}())
}
];
Note the difference:
We create an object -- a real honest-to-goodness object, and we use an IIFE to populate the template property.
Maybe, it will be useful for someone - this code works for me too:
columns: [
{
field: "processed",
title:"Processed",
width: "100px",
template: '<input type="checkbox" ng-model="dataItem.processed" />'
},
and you get the two-way binding with something like this:
<div class="col-md-2">
<label class="checkbox-inline">
<input type="checkbox" ng-model="vm.selectedInvoice.processed">
processed
</label>
</div>
This can be done via the columns.template parameter by supplying a callback function whose parameter is an object representing the row. If you give the row a field named name, this will be the property of the object you reference:
$("#grid").kendoGrid({
columns: [ {
field: "name",
title: "Name",
template: function(data) {
return data.name + "has my respect."
}
}],
dataSource: [ { name: "Jane Doe" }, { name: "John Doe" } ]
});
More information is available on Kendo's columns.template reference page.
After hours of searching. Here is the conclusion that worked:
access your grid data as {{dataItem.masterNoteId}} and your $scope data as simply the property name or function.
Example
template: '<i class="fa fa-edit"></i>',
I really hope this safes somebody life :)
just use like my example:
}, {
field: "TrackingNumber",
title: "#T("Admin.Orders.Shipments.TrackingNumber")",
//template: '<a class="k-link" href="#Url.Content("~/Admin/Shipment/ShipmentDetails/")#=Id#">#=kendo.htmlEncode(TrackingNumber)#</a>'
}, {
field: "ShippingMethodName",
title: "#T("Admin.Orders.Shipments.ShippingMethodName")",
template:function(dataItem) {
var template;
var ShippingMethodPluginName = dataItem.ShippingMethodPluginName;
var IsReferanceActive = dataItem.IsReferanceActive;
var ShippingMethodName = dataItem.ShippingMethodName;
var CargoReferanceNo = dataItem.CargoReferanceNo;
var ShipmentStatusId = dataItem.ShipmentStatusId;
if (ShipmentStatusId == 7) {
return "<div align='center'><label class='label-control'><b style='color:red'>Sipariş İptal Edildi<b></label></div>";
} else {
if (ShippingMethodPluginName == "Shipping.ArasCargo" || ShippingMethodPluginName == "Shipping.ArasCargoMP") {
template =
"<div align='center'><img src = '/content/images/aras-kargo-logo.png' width = '80' height = '40'/> <label class='label-control'><b>Delopi Aras Kargo Kodu<b></label>";
if (IsReferanceActive) {
template =
template +
"<label class='label-control'><b style='color:red; font-size:20px'>"+CargoReferanceNo+"<b></label></div>";
}
return template;
}
we are really having problems finding a way on how to create a menu within an ng-grid.
Something similar to the ECB block within share point.
So, there would be a button that when clicked would show options for that particular row.
We may be looking in the wrong places, but we can't find any help or examples on the internet..
As I told you few days ago you should use row and celltemplates to achieve something like this.
Here is the changed code to better match the current state of your question:
$scope.gridOptions = {
data: 'myData',
rowTemplate: '<div ng-style="{\'cursor\': row.cursor, \'z-index\': col.zIndex() }" ' +
'ng-repeat="col in renderedColumns" ng-class="col.colIndex()" ' +
'class="ngCell {{col.cellClass}}" ng-cell></div>' +
'<div ng-show="showmenu" class="hmenu">' +
'<ul>' +
'<li><button ng-click="viewme(row.entity.name)">View</button></li>' +
'<li><button ng-click="editme(row.entity.name)">Edit</button></li>' +
'</ul>' +
'</div>',
columnDefs: [{
field: 'name',
displayName: 'Name'
}, {
field: 'age',
displayName: 'Age',
cellTemplate: '<div class="ngCellText">{{row.getProperty(col.field)}}' +
'<span ng-click="$parent.showmenu=!$parent.showmenu"><button>click me</button></span>' +
'</div>'
}]
};
True, this is no beauty, but it shows you how to go on. Do your styling in style.css and the row template. This is the best I can do with my weekend laptop.
Here is the Plunker
You could potentially set each CellTemplate to have its own ng-if/ng-show block that's hidden/shown based on a variable on your row.entity object. Then, when your function fires for the selection of the row, you can set that variable ($scope.whatever) to hide/show what's necessary. On my phone at the moment, but I'll try to make a plnkr/jsfiddle later.
I want to use some good coding standards for the current project. The overall outcome is achievable now (in my project), but i want help regarding following a better approach.
I'll try my best to explain what I am doing.
I have a xml Response for System and Product information (The xml is too big to accomodate here 1MB approx)
I have two panes left Pane (an accordion listing what is to be clicked) right Pane (JQUERY UI TABS inside which is JPANEL accomodating JQGRID inside it)
When the left Pane any item is clicked 3 params are sent, id of the anchor tag, Service Name & an alias (which decides what tab content should be) to function infa9Services(id,sname,alias){...}
<a id="infa9Services"\>DataIntegrationService</a> //this is the Service Name
alias is coming from a map, which maps service Name to a alias DIS (in one case)
The Right Pane contains a <div id="detailTable"></div> which gets populated from infa9Services function.
Content of infa9Services function
var tabs = [ "Service", "Property", "Additional Info" ];
//Above array creates the Jquery Tab heading
var lis="";
var divs="";
var panels=[];
var tpanels="";
var adtrs="";
var service=[];
var property=[];
var aDoctor=[];
var homeFileList=[];
var ssaprlist=[];
var refData=[];
fetching data from xml response xml
$(xml).find('product').each(function(){
$(this).children('DomainMeta').each(function(){
$(this).children('Service').each(function(){
if(sname==$(this).attr('name')) //There are more than one service in the xml, so checking which Service name was clicked, in this case DataIntegrationService
{
$(this).children('OptionGroup').each(function(){
if($(this).attr('name').toLowerCase().indexOf("service") >= 0)
{
$(this).children('Option').each(function(){
var srow={};
srow.name=$(this).attr('name');
srow.value=$(this).attr('value');
service.push(srow);
});
}
else
{
$(this).children('Option').each(function(){
var prow={};
prow.name=$(this).attr('name');
prow.value=$(this).attr('value');
property.push(prow);
});
}
});
}
});
});
Now here I am checking using if else what alias is it, based on which i'll fetch data
if(alias==="PCIS")
{
$(this).children('addressDoctor').each(function(){
$(this).children('addressDoctor_info').each(function(){
//adding xml values to aDoctor array
});
});
$(this).children('homefilelist').each(function(){
$(this).children('homefilelist_info').each(function(){
//adding xml values to homeFileList array
});
});
}
else if(alias==="PCRS")
{
$(this).children('homefilelist').each(function(){
$(this).children('homefilelist_info').each(function(){
//adding xml values to homeFileList array
});
});
}
else if(alias==="DIS")
{
$(this).children('addressDoctorEngVerDIS').each(function(){
adtrs='<tr width="100%">'+
'<th class="thPanel" align="left" valign="top" width="40%">'+$(this).children('addressDoctorEngVer_info').attr('name')+'</th>'+
'<td align="left" width="60%">'+$(this).children('addressDoctorEngVer_info').attr('value')+'</td>'+
'</tr>';
});
}
else if(alias==="CMS")
{
$(this).children('referenceDataLocation').each(function(){
$(this).children('referenceDataLocation_info').each(function(){
//adding xml values to refData array
});
});
$(this).children('ssaprDirList').each(function(){
$(this).children('ssaprDirList_info').each(function(){
//adding xml values to ssaprlist array
});
});
}
});
dynamically adding li 's (for UI tabs)
$.each(tabs, function(i, item) {
lis+='<li>'+item+'</li>';
});
dynamically adding div's with class="class" (for JPanel) is there a better way here?
panels[0]='<div title="General Information" class="class">'+
'<div class="jqUIDiv">'+
'<table id="tblService" width="100%"></table>'+
'<div id="ServiceGridpager"></div>'+
'</div>'+
'</div>';
panels[1]='<div title="General Information" class="class">'+
'<div class="jqUIDiv">'+
'<table id="tblProperty" width="100%"></table>'+
'<div id="PropertyGridpager"></div>'+
'</div>'+
'</div>';
Here again I am making use of if else what alias is it, based on which i'll populate data from the vars which were fetched before
if(alias==="PCIS")
{
panels[2]='<div title="Address Doctor" class="class" >'+
'<table class="jpanelTable">'+
'<tbody>'+adtrs+'</tbody>'+
'</table>'+
'</div>';
panels[2]+='<div title="Address Doctor Configuration" class="class">'+
'<div class="jqUIDiv">'+
'<table id="tblAdditional" width="100%"></table>'+
'<div id="AdditionalGridpager"></div>'+
'</div>'+
'</div>';
panels[2]+='<div title="Home File List" class="class">'+
'<div class="jqUIDiv">'+
'<table id="tblHomeFileList" width="100%"></table>'+
'<div id="HomeFileListGridpager"></div>'+
'</div>'+
'</div>';
}
else if(alias==="PCRS")
{
panels[2]='<div title="Home File List" class="class">'+
'<div class="jqUIDiv">'+
'<table id="tblHomeFileList" width="100%"></table>'+
'<div id="HomeFileListGridpager"></div>'+
'</div>'+
'</div>';
}
else if(alias==="DIS")
{
panels[2]='<div title="Address Doctor" class="class" >'+
'<table class="jpanelTable">'+
'<tbody>'+adtrs+'</tbody>'+
'</table>'+
'</div>';
}
else if(alias==="CMS")
{
panels[2]='<div title="Reference Data Location" class="class">'+
'<div class="jqUIDiv">'+
'<table id="tblRefData" width="100%"></table>'+
'<div id="RefDataGridpager"></div>'+
'</div>'+
'</div>';
panels[2]+='<div title="SSAPR" class="class">'+
'<div class="jqUIDiv">'+
'<table id="tblSSAPR" width="100%"></table>'+
'<div id="SSAPRGridpager"></div>'+
'</div>'+
'</div>';
}
else
{
panels[2]='<div title="Additional Information" class="class" >'+
'<table class="jpanelTable">'+
'<tbody>'+
'<tr width="100%">'+
'<td align="left" style="word-break:break-all;">'+NA+'</td>'+
'</tr>'+
'</tbody>'+
'</table>'+
'</div>';
}
Note: panel[0],panel1,panel[2] are contents of the 3 tabs
dynamically adding div 's (content for Jquery UI Tab)
$.each(panels, function(i, item) {
divs+='<div id="tabs-'+i+'">'+item+'</div>';
});
adding the whole thing to detailTable (Right Pane)
$('#detailTable').empty();
$('#detailTable').html('<div class="titleBlue">Configuration>'+productname+'>'+sname+' ('+alias+')</div>');
$("<div id='loadingDiv'><table id='detailTable' width='100%'><tbody><tr><td align='center'>Please wait</td></tr><tr><td align='center'><img src='/csm/view/include/images/loading.gif' alt='Loading'/></td></tr></tbody></table></div>").appendTo('#detailTable');
$('<div id="tabs">'+'<ul>'+lis+'</ul>'+divs+'</div>').appendTo('#detailTable').delay(10).queue(function(){
$('.class').jPanel({
'effect' : 'fade',
'speed' : 'medium',
'easing' : 'swing'
});
});
More.. JQGrids to populate tblService, tblSSAPR, tblRefData etc tables (not included here)
How can I make use of something like Factory or something more Generic in such a case? If suppose there are a lot of alias to be checked it will be very cumbersome to do it the way I am doing it currently. Any help would be appreciated. Thanks
Screen shot
Update
My XML content
<?xml-stylesheet type="text/xsl" href="csmclientinfa9.xsl"?>
<csmclient product="infa9" date="12/16/11 12:10 PM" version="1.0">
<system>
</system>
<product>
<DomainMeta>
<Service type="PCIntegrationService" name="TestPCInteg" version="" licenseName="lic_PCIS"> //THIS IS PCIS SERVICE
<ServiceProcess>
<Node name="N_1a63931"></Node>
<PreStartCommand> null</PreStartCommand>
<PostStartCommand> null</PostStartCommand>
<JvmOptions> </JvmOptions>
<OptionGroup name="IntegrationServiceProcessOptions">
<Option name="Codepage_Id" value="4" ></Option>
<Option name="$PMSourceFileDir" value="$PMRootDir/SrcFiles" ></Option>
</OptionGroup>
</ServiceProcess>
<OptionGroup name="IntegrationServiceOptions">
<Option name="ServiceResilienceTimeout" value="180" ></Option>
</OptionGroup>
</Service>
<Service type="DataIntegrationService" name="TestDIS" version="" licenseName="lic_DIS"> //THIS IS DIS SERVICE
<ServiceProcess>
<Node name="N_1163931"></Node>
<PreStartCommand> null</PreStartCommand>
<PostStartCommand> null</PostStartCommand>
<JvmOptions> -Dfile.encoding=UTF-8 -server -Xms256M -Xmx512M -XX:GCTimeRatio=19 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:ParallelGCThreads=4 -XX:NewRatio=2</JvmOptions>
<OptionGroup name="SQLServiceOptions">
<Option name="MaxConcurrentConnections" value="100" ></Option>
<Option name="MaxPlanCacheSize" value="100" ></Option>
</OptionGroup>
<OptionGroup name="WebServiceOptions">
<Option name="MaxPlanCacheSize" value="100" ></Option>
</OptionGroup>
</ServiceProcess>
<OptionGroup name="SQLServiceOptions">
<Option name="ConnectionCleanupPeriod" value="30000" ></Option>
<Option name="DTMKeepAliveTime" value="0" ></Option>
</OptionGroup>
<OptionGroup name="CoreServiceOptions">
<Option name="SafeModeLevel" value="0" ></Option>
</OptionGroup>
</Service>
</DomainMeta>
<homefilelist>
<homefilelist_info permission='-rwxr-xr-x' hardlink='1' owner='idp' group='support' fsize='61597' date='22 Mar 2011' filename='libpmwspsvrcmn.so' />
<homefilelist_info permission='-rwxr-xr-x' hardlink='1' owner='idp' group='support' fsize='21778' date='22 Mar 2011' filename='libpmorablk.so' />
</homefilelist>
<addressDoctor filename='AD100.cfg'>
<addressDoctor_info name='MemoryUsage' value='1048' />
<addressDoctor_info name='PreloadingMethod' value='MAP' />
</addressDoctor>
<addressDoctorEngVerPCIS>
<addressDoctorEngVer_info name='EngineVersion' value='5.2.5' />
</addressDoctorEngVerPCIS>
<addressDoctorEngVerDIS>
<addressDoctorEngVer_info name='EngineVersion' value='5.2.6' />
</addressDoctorEngVerDIS>
<referenceDataLocation location='/home/idp/av/default'>
<referenceDataLocation_info permission='-rw-r--r--' hardlink='1' owner='idp' group='support' fsize='196' month='Aug' date='2' time='11:06' filename='copyright.txt'/>
</referenceDataLocation>
<ssaprDirList>
<ssaprDirList_info permission='-rw-r--r--' hardlink='1' owner='idp' group='support' fsize='2737558' month='Oct' date='14' time='2010' filename='turkey.ysp' />
</ssaprDirList>
</product>
</csmclient>
More Updates
var aDoctor=[];
var adtrs="";
var homeFileList=[];
if(alias==="PCIS")
{
$(this).children('addressDoctorEngVerPCIS').each(function(){
adtrs='<tr width="100%">'+
'<th class="thPanel" align="left" valign="top" width="40%">'+$(this).children('addressDoctorEngVer_info').attr('name')+'</th>'+
'<td align="left" width="60%">'+$(this).children('addressDoctorEngVer_info').attr('value')+'</td>'+
'</tr>';
});
$(this).children('addressDoctor').each(function(){
$(this).children('addressDoctor_info').each(function(){
var adevrow={};
adevrow.name=$(this).attr('name');
adevrow.value=$(this).attr('value');
aDoctor.push(adevrow);
});
});
$(this).children('homefilelist').each(function(){
$(this).children('homefilelist_info').each(function(){
var row={};
isPresent=true;
row.permission=$(this).attr('permission');
row.hardlink=$(this).attr('hardlink');
row.owner=$(this).attr('owner');
row.group=$(this).attr('group');
row.fsize=$(this).attr('fsize');
row.date=$(this).attr('date');
row.filename=$(this).attr('filename');
homeFileList.push(row);
});
});
}
/*moving on for HTML construction, panels[..]
panels[0] is for 1st jquery ui tab
panels[1] is for 2nd jquery ui tab
panels[2] is for 3rd jquery ui tab*/
/*I am also using jqGrid, so I am giving one example for PCIS, how I am using aDoctor array */
if(alias==="PCIS")
{
//Additional Information
if(aDoctor.length>0)
{
jQuery("#tblAdditional").jqGrid({
datatype: "local",
data: aDoctor,
colNames:['Name','Value'],
colModel:[
{name:'name',index:'name', align:"left"},
{name:'value',index:'value', align:"left"}
],
pager : '#AdditionalGridpager',
rowNum:10,
rowList:[10,50,100],
scrollOffset:0,
height: 'auto',
autowidth:true,
viewrecords: true,
gridview: true
});
jQuery("#tblAdditional").setGridParam({rowNum:10}).trigger("reloadGrid");
jQuery("#tblAdditional").jqGrid('filterToolbar',{stringResult: true, searchOnEnter: false, defaultSearch: 'cn'});
}
else //i am displaying none in the table
}
//So now my panel is complete for PCIS, if you refer my question, all code is available :) I know it's really big, but this is my 1st project and I want it to be better than this :) Thanks
The Factory design pattern must have a switch (if/else block or associative array) under the hood anyhow. You won't ever shorten code by using it. The only benefit you'll get here is that you might hide away or separate some of that long code. Returning a function from another function, or returning an array where one of the properties is a function (set in the factory) is about as close as you can get to this pattern in JavaScript. The code sample below show something very much like a factory that is just as good in the JavaScript case, and might be slightly less confusing.
I'm not sure what you mean by Generic. If you're referring to "generics", ala Java or C#, then it won't help you in this scenario. Generics in those languages help you get around static typing and avoid duplicating classes. You're not using classes at all here.
If you're simply trying to avoid repeating yourself, you should look for a functional refactor here. See what repeats, and what doesn't. Figure out how you can loop or map to the stuff that doesn't repeat, and write the code that does repeat only once.
For example, I'd use an associative array to map strings to functions for your big if/else block, and I'd extract that child-looping code into a more generialized function:
function forAll(parentName, childName, action) {
$(this).children(parentName).each(function(){
$(this).children(childName).each(function(){
action();
});
});
}
var aliasToHandlerMap =
{
"PCIS": function() {
forAll('addressDoctor', 'addressDoctor_info', function() {
//adding xml values to aDoctor array
});
forAll('homefilelist', 'homefilelist_info', function() {
//adding xml values to homeFileList array
});
},
"PCRS": function() {
forAll('homefilelist', 'homefilelist_info', function() {
//adding xml values to homeFileList array
});
},
"DIS": function() {
$(this).children('addressDoctorEngVerDIS').each(function(){
adtrs=
'<tr width="100%">'+
'<th class="thPanel" align="left" valign="top" width="40%">'+
$(this).children('addressDoctorEngVer_info').attr('name')+'</th>'+
'<td align="left" width="60%">'+
$(this).children('addressDoctorEngVer_info').attr('value')+'</td>'+
'</tr>';
});
},
"CMS": function() {
forAll('referenceDataLocation', 'referenceDataLocation_info', function() {
//adding xml values to refData array
});
forAll('ssaprDirList', 'ssaprDirList_info', function() {
//adding xml values to ssaprlist array
});
}
};
// Execute the appropriate handler for the particular alias
var handler = aliasToHandlerMap[alias];
handler();
In this case I'd also recommend you name each of those handlers, and build the map using named functions. I just wanted to show you visually how much code compression you get from this.
You should be able to do similar things for your long strings of HTML that you're building. If they follow a general pattern, make a function for them, and extract the parts that change into parameters that you pass in.
And you generally shouldn't be building styles into your elements (e.g. align="left" valign="top" width="40%"). You should use style sheets that apply to those specific items, applying only CSS classes when necessary, so that you to specify that in only one place. That should shorten a lot of the element creation code, and might let you use jQuery DOM creation to avoid messy string concatenation code.
All refactoring of long code boils down to an application of the same principle - find out what repeats, and what doesn't repeat. Extract the part that repeat, and build an iterable or indexable structure out of the part that doesn't repeat.
To start learning how to do generalized refactoring in languages using a Functional Programming style, I suggest you watch the SICP lecture videos. They use LISP, but teach you it while you're watching the videos, and the principles apply to every language out there. That language lends itself extremely well to this type of refactoring, which is why he uses it.
You could also look into CoffeeScript, which allows you to take java code and shrink it into a more compact syntax :) It compiles back into pretty-printed JavaScript, so you don't have to worry about server impact, installing code on client boxes, etc.
Also, check out a few of these links for more reading on general code refactoring:
http://en.wikipedia.org/wiki/Code_refactoring
http://martinfowler.com/refactoring/ (he wrote a book)
I think you are asking for a better way to make your code without having to hard code the html? If that is the case you could use jQuery.template() http://api.jquery.com/jQuery.template/. It's in beta but it should help you by not having to hardcode your html into the javascript.
For my projects we do our templating in PHP and just have jquery do ajax calls if the data or templates need to change.
If this is not what you are looking for can you be more descriptive with your question?
If you are looking at refactoring further you can use the same jPanel to buld tabs using just a simple div markup like you did for accordion just replace your jPanel js file with the latest 1.4 version that will save some markup junk for you and also you have too much of html as strings try using a javascript template engine here