I am new to angularjs. I am implementing a angularjs datatable with some json data. I am facing a problem that each time if a user had permission (which is stored in json) can export csv but when the user has no permission, the datatable export button is hidden or does not show. I googled some articles but am unable to find answer.
I used below code to implement datatable buttons:
vm.dtOptions = DTOptionsBuilder.fromSource('')
.withFnServerData(serverData)
.withOption('createdRow', createdRow)
.withDataProp('data')
.withOption('order', [0, 'desc'])
.withOption('processing', true)
.withOption('serverSide', true)
.withOption('headerCallback', function(header) {
if (!vm.headerCompiled) {
// Use this headerCompiled field to only compile header once
vm.headerCompiled = true;
$compile(angular.element(header).contents())($scope);
}
})
.withPaginationType('full_numbers')
.withOption('lengthMenu', [
[10, 50, 100, 200, 500, -1],
[10, 50, 100, 200, 500, "All"]
])
.withButtons([
{
className:'fa fa-upload',
key: '1',
action: function (e, dt, node, config) {
vm.openImportFileDialog();
}
},
{
action:function () {
if (!vm.permissions.assets_EDIT) {
alert('You have no permission to download file.')
}
},
extend: "csv",
text:' Export(CSV)',
className:'fa fa-download',
exportOptions: {
columns:[0,1,2,3,4,5,6]
},
exportData: {decodeEntities:true}
},
{
className:'fa fa-exchange p-left-5',
text:' Export(HTML)',
// enabled: false,
action:function () {
vm.openNewTab();
}
}
])
.withBootstrap();
Here is my html content:-
<table datatable="" dt-options="vm.dtOptions" dt-columns="vm.dtColumns" dt-instance="vm.dtInstance"
class="row-border hover">
</table>
Thanks for your precious time.
You should manage two set of buttons, one for users with permissions, another for users without. It could look like this :
var buttonsNoPermission = [{ extend: 'colvis' }];
var buttonsWithPermission = [{ extend: 'colvis' }, { extend: 'csvHtml5' }];
Simply port the params from your .withButtons to variables. Then you can create the relevant buttons for the user by a ternary in the initialization itself :
.withButtons( permissions.asset_EDIT ? buttonsWithPermission : buttonsNoPermission)
Here is a small demo, try change permissions.asset_EDIT :
http://plnkr.co/edit/s6lainDw4eQC9zaoZGdG?p=preview
Note: I am not using the vm approach in the demo, so dont get confused, refer to vm.permissions.asset_EDIT in your own code. Have never really understood the concept behind vm, besides it is a way for lazy programmers to accidently add a new layer of complexity to their code :)
Based on value of say vm.showExportOption you can add a class to the parent table element. Then with a little bit of CSS Logic you can hide the exact button by targeting its class based on whether or not a certain class is present on its parent.
Here's the rough idea:
On Table element:
ng-class="vm.showExportOption ? 'show-export' : 'hide-export'"
Then in CSS something like:
table.show-export .fa-exchange{
display:block;
}
table.hide-export .fa-exchange{
display:none;
}
Remember this is not to copy-paste but to give you an idea on how to do this. If you can create a JSFiddle to show your problem, I will gladly port it, but this should get you on you way.
I would try adding an ng-show tag element tag, in your case the export button, with a backing Boolean variable that you can set in your code:
<table ng-show="!vm.dtIsHidden" datatable="" dt-options="vm.dtOptions" dt-columns="vm.dtColumns" dt-instance="vm.dtInstance"
class="row-border hover">
</table>
Then in your view model, set this value up with the proper logic:
vm.dtIsHidden = whatever;
And this should solve your problem.
Related
I have created a view (a panel) with 3 subpanels ...
When the view loads , I want a function to run in viewController and based on its outcome , I want subpanel 1 to be visible(subpanel2 to be invisible) or subpanel2 to be visible(subpanel1 to be invisible)
How can I accomplish this ?
You are looking for card layout. It is already implemented. So you don't have to implement again. Just tell it witch panel gonna be active it will do all layout things itself. Checkout this api doc.
May be the Accordion layout can help you:
This is a layout that manages multiple Panels in an expandable accordion style such that by default only one Panel can be expanded at any given time
Here's a full example, it's quite straight forward:
Fiddle
Ext.define('FooController', {
extend: 'Ext.app.ViewController',
alias: 'controller.foo',
init: function(view) {
var child = Math.random() < 0.5 ? 'p1' : 'p2';
view.setActiveItem(this.lookupReference(child));
}
})
Ext.define('Foo', {
extend: 'Ext.container.Container',
layout: 'card',
controller: 'foo',
items: [{
title: 'P1',
reference: 'p1'
}, {
title: 'P2',
reference: 'p2'
}]
});
Ext.onReady(function() {
new Foo({
renderTo: document.body,
width: 200,
height: 200
});
});
Give itemId to all three panel and then fireEvent.
Listener of view
listeners:{
show: function(){
me.fireEvent('showHidePanel');
}
}
define showHidePanel method in Controller and in that method get panel by using down() with item id and hide/show panel by using hide()/show() method.
Using the example from https://help.rallydev.com/apps/2.0rc2/doc/#!/guide/timebox_filtering for a timebox required app, how do I convert the cardBoard view into a grid?
This is the base code:
Ext.define('Rally.guide.ReleaseFilteredBoard', {
extend: 'Rally.app.TimeboxScopedApp',
scopeType: 'release',
onScopeChange: function(scope) {
if(!this.board) {
this.board = this.add({
xtype: 'rallycardboard',
storeConfig: {
filters: [scope.getQueryFilter()]
}
});
} else {
this.board.refresh({
storeConfig: {
filters: [scope.getQueryFilter()]
}
});
}
}
});
It seems that I can simply change the xtype to 'rallygrid' and based on docs it should work but it seems to need a model defined as well - how do I get the model details out of the timebox scope to feed into the grid?
You may wish to check out the example code for Rally.ui.grid.Grid.
Here's a quick sample of how one might apply the Timebox filter to a grid:
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
grid: null,
launch: function() {
var filters = [];
var timeboxScope = this.getContext().getTimeboxScope();
if(timeboxScope) {
filters.push(timeboxScope.getQueryFilter());
}
this.getFilteredStoryModel(filters);
},
onTimeboxScopeChange: function(newTimeboxScope) {
var newFilters = [];
var updatedTimeboxScope = this.getContext().getTimeboxScope();
if (this.grid) {
this.grid.destroy();
}
if (updatedTimeboxScope) {
newFilters.push(newTimeboxScope.getQueryFilter());
}
this.getFilteredStoryModel(newFilters);
},
getFilteredStoryModel: function(queryFilters) {
Rally.data.ModelFactory.getModel({
type: 'UserStory',
success: function(model) {
this.grid = this.add({
xtype: 'rallygrid',
model: model,
columnCfgs: [
'FormattedID',
'Name',
'Owner',
'Iteration'
],
storeConfig: {
filters: queryFilters
}
});
},
scope: this
});
}
});
To show the timebox filter, choose the type of filter you desire when first setting up your Custom Page in Rally. The "onTimeboxScopeChange" callback responds to events triggered by the timebox selector setup and configured on the Custom Page container itself. No code is needed to setup the timebox selector, rather you do it via the Rally UI when creating a new Custom Page:
Select the type of filter (Release or Iteration):
(1) Note that the filter shows on the "My New Custom Page" Container. (2) Any app that you add to "My New Custom Page" will then have the timebox filter available/applied:
Add Custom HTML App:
Paste in Code and Save:
Iteration-filtered Grid:
Alternatively, if you don't want a timebox filter that applies to an entire Custom Page container, you can elect to use Rally.ui.combobox.ReleaseComboBox or Rally.ui.combobox.IterationComboBox
In your App code itself, and manage the filtering via callbacks from either of these components. This filtering would be totally "within-app" and wouldn't rely on the Custom Page-wide timebox component.
New StaticDataSource will construct datasource for fuelux grid. The formatter will set the property to generate editable button in datagrid cell. The code is as follows:
app.PlayersView = Backbone.View.extend({
template: _.template( $("#players-template").html() ),
events: {
"click #addUser" : "addUser",
},
initialize: function () {
if(this.collection){
this.collection.fetch();
}
this.listenTo(this.collection, 'all', this.render);
},
render: function () {
this.$el.html( this.template );
var dataSource = new StaticDataSource({
columns: [
{
property: 'username',
label: 'Username',
sortable: true
},
{
property: 'group',
label: 'Groups',
sortable: true
},
{
property: 'edit',
label: 'Edit',
sortable: true
}
],
formatter: function (items) {
$.each(items, function (index, item) {
item.edit = '<div class="btn-group"><a id="editGroup" class="btn">Edit</a></div>';
});
},
data: this.collection.toJSON(),
delay: 250
});
$('#MyGrid').datagrid({
dataSource: dataSource,
stretchHeight: true
});
});
The app.Playerview object is created somewhere in bakcbone router as follows:
new app.PlayersView({
collection : new app.UsersCollection
}));
Here, column are username, groups and edit. The edit column for each row contains edit button. When I click the edit button in any row, I want to pass the particular row modal or row data to any other backbone view. How can we do that?
Actualy I will open the dialog that will allow to edit that particular row. I want modal to be pre-populated by that row value.
Thanks in advance
From a Backbone point of view you have no control whatsoever on your grid. So basically, at the moment, your rows don't represent any model.
As things are now, if you only want to pre-populate your modal, I guess you could do it easily with jQuery.
However I'll assume you also want your models to be changed at the same time. I don't know Fuel UX so I don't know exactly how you could do this but (but I'm still pretty sure you can, and should if you want your models to be updated): wait until the grid is created, bind your models to the grid by creating a view which element would be the row for each, and inside this view listen to clicks on your button. Here you would have direct access to your model, and therefore your data.
If you'd post some more code (the definition of your model for example) and the HTML of the grid (or a simplified version as I'm sure it's full of custom classes and so on), I could help if you wish so.
If your players have unique identifiers you can change your edit buttons as follows, to include a player ID:
Before:
<a id="editGroup" class="btn">Edit</a>
After:
<a id="editGroup" data-id="3" class="btn">Edit</a>
Of course, you would plug in the correct ID inside your existing formatter function. You would then have access to this ID from the click event in order to populate your dialog and choose the correct model to update.
I need to load a JSON from server and i want to enable a user to click and edit the value.
But when they edit, it should not call server. i mean i am not going to update immediately. So i dont want editurl. So i tried
'ClientArray' But still it shows Url is not set alert box. But i need
all the edited values when the user click Add Commented Items button this button will fire AddSelectedItemsToSummary() to save those in server
MVC HTML Script
<div>
<table id="persons-summary-grid"></table>
<input type="hidden" id="hdn-deptsk" value="2"/>
<button id="AddSelectedItems" onclick="AddSelectedItemsToSummary();" />
</div>
$(document).ready(function(){
showSummaryGrid(); //When the page loads it loads the persons for Dept
});
JSON Data
{"total":2,"page":1,"records":2,
"rows":[{"PersonSK":1,"Type":"Contract","Attribute":"Organization
Activity","Comment":"Good and helping og"},
{"PersonSK":2,"Type":"Permanant","Attribute":"Team Management",
"Comment":"Need to improve leadership skill"}
]}
jQGRID code
var localSummaryArray;
function showSummaryGrid(){
var summaryGrid = $("#persons-summary-grid");
// doing this because it is not firing second time using .trigger('reloadGrid')
summaryGrid.jqGrid('GridUnload');
var deptSk = $('#hdn-deptsk').val();
summaryGrid.jqGrid({
url: '/dept/GetPersonSummary',
datatype: "json",
mtype: "POST",
postData: { deptSK: deptSk },
colNames: [
'SK', 'Type', 'Field Name', 'Comments'],
colModel: [
{ name: 'PersonSK', index: 'PersonSK', hidden: true },
{ name: 'Type', index: 'Type', width: 100 },
{ name: 'Attribute', index: 'Attribute', width: 150 },
{ name: 'Comment', index: 'Comment', editable: true,
edittype: 'textarea', width: 200 }
],
cellEdit: true,
cellsubmit: 'clientArray',
editurl: 'clientArray',
rowNum: 1000,
rowList: [],
pgbuttons: false,
pgtext: null,
viewrecords: false,
emptyrecords: "No records to view",
gridview: true,
caption: 'dept person Summary',
height: '250',
jsonReader: {
repeatitems: false
},
loadComplete: function (data) {
localSummaryArray= data;
summaryGrid.setGridParam({ datatype: 'local' });
summaryGrid.setGridParam({ data: localSummaryArray});
}
});
)
Button click function
function AddSelectedItemsToSummary() {
//get all the items that has comments
//entered using cell edit and save only those.
// I need to prepare the array of items and send it to MVC controller method
// Also need to reload summary grid
}
Could any one help on this? why i am getting that URL is not set error?
EDIT:
This code is working after loadComplete changes. Before it was showing
No URL Set alert
I don't understand the problem with cell editing which you describe. Moreover you wrote "i need the edited value when the user click + icon in a row". Where is the "+" icon? Do you mean "trash.gif" icon? If you want to use cell editing, how you imagine it in case of clicking on the icon on the row? Which cell should start be editing on clicking "trash.gif" icon? You can start editing some other cell as the cell with "trash.gif" icon ising editCell method, but I don't think that it would be comfortable for the user because for the users point of view he will start editing of one cell on clicking of another cell. It seems me uncomfortable. Probably you want implement inline editing?
One clear error in your code is usage of showSummaryGrid inside of RemoveFromSummary. The function RemoveFromSummary create jqGrid and not just fill it. So one should call it only once. To refresh the body of the grid you should call $("#persons-summary-grid").trigger("refreshGrid"); instead. Instead of usage postData: { deptSK: deptSk } you should use
postData: { deptSK: function () { return $('#hdn-deptsk').val(); } }
In the case triggering of refreshGrid would be enough and it will send to the server the current value from the '#hdn-deptsk'. See the answer for more information.
UPDATED: I couldn't reproduce the problem which you described, but I prepared the demo which do what you need (if I understand your requirements correctly). The most important part of the code which you probably need you will find below
$("#AddSelectedItems").click(function () {
var savedRow = summaryGrid.jqGrid("getGridParam", "savedRow"),
$editedRows,
modifications = [];
if (savedRow && savedRow.length > 0) {
// save currently editing row if any exist
summaryGrid.jqGrid("saveCell", savedRow[0].id, savedRow[0].ic);
}
// now we find all rows where cells are edited
summaryGrid.find("tr.jqgrow:has(td.dirty-cell)").each(function () {
var id = this.id;
modifications.push({
PersonSK: id,
Comment: $(summaryGrid[0].rows[id].cells[2]).text() // 2 - column name of the column "Comment"
});
});
// here you can send modifications per ajax to the server and call
// reloadGrid inside of success callback of the ajax call
// we simulate it by usage alert
alert(JSON.stringify(modifications));
summaryGrid.jqGrid("setGridParam", {datatype: "json"}).trigger("reloadGrid");
});
I'm using ExtJS 4 (beta 3) and I have a TreePanel that is my kind of navigation menu.
It is something like this:
Jobs
Add Job
List All Jobs
...
...
...
(this will be made on a permission system base, but that's another story)
On ExtJS 3, do something when i clicked "Add Job" was as simple as adding
...
leaf:true,
listeners:{
click:function(n){
//my code...
}
}
...
to the root children elements.
Now It's not that simple. The closer i got was with (on the treepanel)
listeners:{
click : {
element : 'el',
fn : function(eve, elem, obj){
console.log(node);
console.log(elem);
console.log(obj);
}
}
}
So, maybe i'm just a noob, maybe i have already a strong hatred for ExtJS, maybe is just a problem in this beta version, but...
How do I add a listener to the click event on the tree nodes? (the Select event won't do what i need)
Thank you guys.
EDIT: Currently testing with this, and it's not working.
... = Ext.create('Ext.tree.TreePanel', {
region : 'west',
collapsible : false,
title : 'ITMI',
width : 220,
margins : '5 5 5 5',
cmargins : '5 5 5 5',
hideHeaders : true,
useArrows : true,
rootVisible : false,
headers: [{
xtype : 'treeheader',
text : 'Nome',
flex : 1,
dataIndex: 'nome'
}],
store: store,
listeners:{
itemclick: function(n){
console.info(n);
}
}
...
EDIT 2: The itemclick event now works (on EXJS 4 final), It still doesn't solve my problem. I'd Like to call a specific function when i call each treenode. Before it was really easy. Now i can't figure it out.
in ext4 beta3 (maybe in final release too)... there is no longer click event....
this has changed to itemclick more info
var tree = Ext.create('Ext.tree.Panel', {
store: store,
renderTo: Ext.getBody(),
height: 300,
width: 250,
title: 'Files',
listeners:{
itemclick: function(n){
console.info(n);
}
}
});
So, It may help some people who may be struggling with the same issue I did then.
The "itemclick" event is the way to handle leafs clicks, and it didn't work then for reasons I don't remember.
I accomplished what I needed by splitting the config I had in the database, something like
controllerName|functionName
and then call this code on the handler of the "itemclick:
this.getController(ctr)[fn]();
where ctr is the controllerName and fn is the functionName. This could easily be done with eval, but I prefer not to.
I could not get itemclick to fire with IE (fine in Chrome). I modified my code to use 'checkchange' and it works fine.