How to export selected data with form post - angular-ui-grid

I'm trying to export selected data from my ui-grid into another web application I'm building which accepts data via a form post. So, I've added a custom menu item with a function...
gridMenuCustomItems: [
{
title: 'Export to Texter',
action: function($event) {
console.log($scope.gridApi);
alert('do the export here');
},
order: 210
}
],
This works so far, but can someone point me in the direction of how to get the selected rows, then export all the values of one column (RegID, in my case) via a form post?
Here's my plunker: https://plnkr.co/edit/qsWac1FtIiblBKL3qALM?p=preview

I've got something working. I'm sure someone can improve it (I had to resort to jQuery for the form submit because I'm familiar with it and it's available), but this is working for me.
I should note that I needed the whole page to redirect to the new app, so AJAX handling wasn't enough.
gridMenuCustomItems: [
{
title: 'Export selected to Some App',
action: function($event) {
var currentSelection = $scope.gridApi.selection.getSelectedRows();
var currentSelectionUserIds = [];
currentSelection.forEach(function(entry) {
currentSelectionUserIds.push(entry.userId);
});
$myExportForm = $('<form id="ExportForm" action="/Link/To/My/App/" method="post"><input type="hidden" name="UserIDs" value="' + currentSelectionUserIds + '"></form>');
$('body').append($myExportForm);
$myExportForm.submit();
},
order: 210
}
],

Related

Select2 AJAX doesn't update when changed programatically

I have a Select2 that fetches its data remotely, but I would also like to set its value programatically. When trying to change it programatically, it updates the value of the select, and Select2 notices the change, but it doesn't update its label.
https://jsfiddle.net/Glutnix/ut6xLnuq/
$('#set-email-manually').click(function(e) {
e.preventDefault();
// THIS DOESN'T WORK PROPERLY!?
$('#user-email-address') // Select2 select box
.empty()
.append('<option selected value="test#test.com">test#test.com</option>');
$('#user-email-address').trigger('change');
});
I've tried a lot of different things, but I can't get it going. I suspect it might be a bug, so have filed an issue on the project.
reading the docs I think maybe you are setting the options in the wrong way, you may use
data: {}
instead of
data, {}
and set the options included inside {} separated by "," like this:
{
option1: value1,
option2: value2
}
so I have changed this part of your code:
$('#user-email-address').select2('data', {
id: 'test#test.com',
label: 'test#test.com'
});
to:
$('#user-email-address').select2({'data': {
id: 'test#test.com',
label: 'test#test.com'
}
});
and the label is updating now.
updated fiddle
hope it helps.
Edit:
I correct myself, it seems like you can pass the data the way you were doing data,{}
the problem is with the data template..
reading the docs again it seems that the data template should be {id, text} while your ajax result is {id, email}, the set manual section does not work since it tries to return the email from an object of {id, text} with no email. so you either need to change your format selection function to return the text as well instead of email only or remap the ajax result.
I prefer remapping the ajax results and go the standard way since this will make your placeholder work as well which is not working at the moment because the placeholder template is {id,text} also it seems.
so I have changed this part of your code:
processResults: function(data, params) {
var payload = {
results: $.map(data, function(item) {
return { id: item.email, text: item.email };
})
};
return payload;
}
and removed these since they are not needed anymore:
templateResult: function(result) {
return result.email;
},
templateSelection: function(selection) {
return selection.email;
}
updated fiddle: updated fiddle
For me, without AJAX worked like this:
var select = $('.user-email-address');
var option = $('<option></option>').
attr('selected', true).
text(event.target.value).
val(event.target.id);
/* insert the option (which is already 'selected'!) into the select */
option.appendTo(select);
/* Let select2 do whatever it likes with this */
select.trigger('change');
Kevin-Brown on GitHub replied and said:
The issue is that your templating methods are not falling back to text if email is not specified. The data objects being passed in should have the text of the <option> tag in the text property.
It turns out the result parameter to these two methods have more data in them than just the AJAX response!
templateResult: function(result) {
console.log('templateResult', result);
return result.email || result.text;
},
templateSelection: function(selection) {
console.log('templateSelection', selection);
return selection.email || selection.id;
},
Here's the fully functional updated fiddle.

Alfresco: linking directly to workflow

I would like to start a workflow from the site links dashlet on my Alfresco site. Using Firebug to examine the POST gives me a URL that works, but it only displays the form without any UI:
http://localhost:8081/share/service/components/form?htmlid=template_x002e_start-workflow_x002e_start-workflow_x0023_default-startWorkflowForm-alf-id1&itemKind=workflow&itemId=activiti%24orpWorkflow&mode=create&submitType=json&showCaption=true&formUI=true&showCancelButton=true&destination=
Is this possible? And if so, how can I format the link to include the UI?
If not, are there custom dashlets out there designed for starting workflows?
When you select workflow from dropdown it will generate url based on selected workflow and redirect you to that.
Ex. For ParallelGroupReview workflow URL is.
http://localhost:8080/share/service/components/form?htmlid=template_x002e_start-workflow_x002e_start-workflow_x0023_default-startWorkflowForm-alf-id1&itemKind=workflow&itemId=activiti%24activitiParallelGroupReview&mode=create&submitType=json&showCaption=true&formUI=true&showCancelButton=true&destination=
Now if you use this url directly in browser you will be able to see same form but header and footer part will be missing, because those global components will not be avilable outside of share context.
If you see start-workflow.ftl you will be able to see header and footer components are inserted which are responsible for rest of the UI.
<#include "include/alfresco-template.ftl" />
<#templateHeader />
<#templateBody>
<#markup id="alf-hd">
<div id="alf-hd">
<#region scope="global" id="share-header" chromeless="true"/>
</div>
</#>
<#markup id="bd">
<div id="bd">
<div class="share-form">
<#region id="start-workflow" scope="template"/>
</div>
</div>
</#>
</#>
<#templateFooter>
<#markup id="al-ft">
<div id="alf-ft">
<#region id="footer" scope="global"/>
</div>
</#>
</#>
You can reuse same component just need to make sure header and footer are initialized properly.
I created an extension module which has the following target:
<targetPackageRoot>org.alfresco.components.workflow</targetPackageRoot>
I included the following piece in my extended start-workflow.get.html.ftl:
<#markup id="start-workflow-js" target="js" action="after">
<#script src="${url.context}/res/components/workflow/initiate-workflow.js" group="workflow"/>
</#>
to extend the default start-workflow.js of my own.
You'll need to change the following methods:
onReady: so it reads your param from the url to know which workflowdefinition to start and fire onWorkflowSelectChange
onWorkflowSelectChange: So it reads the workflowdefintion to load the form
You can make a little customization for Share.
For example, if you need to open the start form of any business process, you can find its index in the popup and add an additional parameter to the URL (let's say, openFormParam).
In start-workflow.js:
onReady: function StartWorkflow_onReady() {
// skipped ...
// get the additional parameter from the URL here
// var openFormParam = ...
if(openFormParam !== null) {
var p_aArgs = [];
var index = {index: 0}; // for the first workflow in the popup
p_aArgs.push(0, index);
this.onWorkflowSelectChange(null, p_aArgs);
}
return Alfresco.component.StartWorkflow.superclass.onReady.call(this);
},
// OOTB
onWorkflowSelectChange: function StartWorkflow_onWorkflowSelectChange(p_sType, p_aArgs) {
var i = p_aArgs[1].index;
if (i >= 0) {
// Update label of workflow menu button
var workflowDefinition = this.options.workflowDefinitions[i];
this.widgets.workflowDefinitionMenuButton.set("label", workflowDefinition.title + " " + Alfresco.constants.MENU_ARROW_SYMBOL);
this.widgets.workflowDefinitionMenuButton.set("title", workflowDefinition.description);
// Load the form for the specific workflow
Alfresco.util.Ajax.request({
url: Alfresco.constants.URL_SERVICECONTEXT + "components/form",
dataObj: {
htmlid: this.id + "-startWorkflowForm-" + Alfresco.util.generateDomId(),
itemKind: "workflow",
itemId: workflowDefinition.name,
mode: "create",
submitType: "json",
showCaption: true,
formUI: true,
showCancelButton: true,
destination: this.options.destination
},
successCallback: {
fn: this.onWorkflowFormLoaded,
scope: this
},
failureMessage: this.msg("message.failure"),
scope: this,
execScripts: true
});
}
},

Persisting jqGrid column preferences

I've got a few jqGrids on my ASP.NET MVC 3 application that have a number of columns. I added the following to the column definitions to default some columns to be hidden:
colModel: [
{ name: 'IceCreamID', hidden: true},
{ name: 'RecipeID', hidden: true }
and this works nicely. Those columns aren't visible on my grid.
Then I added this to implement the column chooser:
var grid = $('#icecreamGrid');
grid.jqGrid('navButtonAdd', '#icecreamPager',
{ caption: "Columns", buttonicon: "ui-icon-calculator",
title: "Choose Columns",
onClickButton: function() {
grid.jqGrid('columnChooser');
}
});
Great, brings up the column chooser now. I then added the following to columns I never wanted to show up in the column chooser:
colModel: [
{ name: 'IceCreamID', hidden: true, hidedlg: true},
So I can now hide/show columns just fine. Now, how would you persist this information? DB? As a cookie? Other way? Is there a preferred way to store this sort of information that is really a user preference rather than something related to the data itself?
More Info
Based on Oleg's comment below, I want to provide a little more information.
The point here is that I've got grids with 10-15 columns which could be display based on the user's preference. For a simple example, one of my grid's has the following 9 columns:
IceCream|ShortName|HasNuts|SugarAdded|LimitedRun|PromoItem|Facility|FirstRun|LastRun
Users can hide/show any of these 9 columns based on their personal preferences.
What I want to do is provide a way to persist which columns a particular user wants to see so that s/he doesn't have to re-choose those columns to view each time the page with that grid is shown.
I found you question very interesting. The question about saving the user state of grid are interesting in many cases. There are some interesting answers on such problems which uses cookie (see here for example).
In my opinion saving of the grid state in database on the server or in the localStorage is better way as the usage of cookie. The best way depends on the project's requirements in which you use it. For example the usage of the database storage on the server allows you to implement roaming state of the grid. If you use the localStorage instead of cookies the user preferences will be lost if the user goes to another computer or just if the user will use another web browser on the same computer.
Another problem with the grid state is the maintenance. The information about the columns of the grid you hold typically in the JavaScript or HTML files and not in the database. In the case the both sources can be not synchronous on the changes in the grid. Different scenarios of the update problem could you easy imagine. Nevertheless the advantages of user's preferences so large in some scenarios that the problems with small disadvantages are not so important and can be solved relatively easy.
So I'll spend some time to implement two demos which shows how it can be implemented. I used localStorage in my demos because of many reasons. I mention only two from there:
Cookies is the way which send permanently different information to or from the server which is not really requited. It increases the size of HTTP header and decreases the performance of the web site (see here for example).
Cookies have very hard restrictions. Corresponds to the section 6.3 of rfc2109 or 6.1 of rfc6265: At least 4096 bytes per cookie, at least 50 cookies per domain (20 in rfc2109), at least 3000 cookies total (300 in rfc2109). So the cookies one can't use to save too many information. For example if you would save state of every grid of every your web page you can quickly achieve the limits.
On the other side localStorage are supported by all modern browsers and will be supported in Internet Explorer starting with IE8 (see here). The localStorage will be automatically saved per origins (like a1.example.com, a2.example.com, a3.example.com, etc) and has arbitrary limit of 5 MB per origin (see here). So if you use the space carefully you will far from the any limits.
So I used in my demos the localStorage. I should additionally mention that there are some plugins like jStorage which use localStorage if it's supported by the browser and use another storage, but the same interface for you in case of old browsers like IE6/IE7. In the case you has only less size of storage: 128 kB instead of 5 MB, but it's better as 4K which one has for cookies (see here).
Now about the implementation. I creates two demos: this and it's extended version: this.
In the first demo the following states of grid will be saved and automatically restored on the page reload (F5 in the most web browsers):
which column are hidden
the order of columns
the width of every column
the name of the column by which the grid will be sorted and the sort direction
the current page number
the current filter of the grid and the flag whether the filter are applied. I used multipleSearch: true setting in the grid.
In the same way one can extend (or reduce) the list of options which are the part of the saved grid state.
The most important parts of the code from the demo you will find below:
var $grid = $("#list"),
saveObjectInLocalStorage = function (storageItemName, object) {
if (typeof window.localStorage !== 'undefined') {
window.localStorage.setItem(storageItemName, JSON.stringify(object));
}
},
removeObjectFromLocalStorage = function (storageItemName) {
if (typeof window.localStorage !== 'undefined') {
window.localStorage.removeItem(storageItemName);
}
},
getObjectFromLocalStorage = function (storageItemName) {
if (typeof window.localStorage !== 'undefined') {
return $.parseJSON(window.localStorage.getItem(storageItemName));
}
},
myColumnStateName = 'ColumnChooserAndLocalStorage.colState',
saveColumnState = function (perm) {
var colModel = this.jqGrid('getGridParam', 'colModel'), i, l = colModel.length, colItem, cmName,
postData = this.jqGrid('getGridParam', 'postData'),
columnsState = {
search: this.jqGrid('getGridParam', 'search'),
page: this.jqGrid('getGridParam', 'page'),
sortname: this.jqGrid('getGridParam', 'sortname'),
sortorder: this.jqGrid('getGridParam', 'sortorder'),
permutation: perm,
colStates: {}
},
colStates = columnsState.colStates;
if (typeof (postData.filters) !== 'undefined') {
columnsState.filters = postData.filters;
}
for (i = 0; i < l; i++) {
colItem = colModel[i];
cmName = colItem.name;
if (cmName !== 'rn' && cmName !== 'cb' && cmName !== 'subgrid') {
colStates[cmName] = {
width: colItem.width,
hidden: colItem.hidden
};
}
}
saveObjectInLocalStorage(myColumnStateName, columnsState);
},
myColumnsState,
isColState,
restoreColumnState = function (colModel) {
var colItem, i, l = colModel.length, colStates, cmName,
columnsState = getObjectFromLocalStorage(myColumnStateName);
if (columnsState) {
colStates = columnsState.colStates;
for (i = 0; i < l; i++) {
colItem = colModel[i];
cmName = colItem.name;
if (cmName !== 'rn' && cmName !== 'cb' && cmName !== 'subgrid') {
colModel[i] = $.extend(true, {}, colModel[i], colStates[cmName]);
}
}
}
return columnsState;
},
firstLoad = true;
myColumnsState = restoreColumnState(cm);
isColState = typeof (myColumnsState) !== 'undefined' && myColumnsState !== null;
$grid.jqGrid({
// ... other options
page: isColState ? myColumnsState.page : 1,
search: isColState ? myColumnsState.search : false,
postData: isColState ? { filters: myColumnsState.filters } : {},
sortname: isColState ? myColumnsState.sortname : 'invdate',
sortorder: isColState ? myColumnsState.sortorder : 'desc',
loadComplete: function () {
if (firstLoad) {
firstLoad = false;
if (isColState) {
$(this).jqGrid("remapColumns", myColumnsState.permutation, true);
}
}
saveColumnState.call($(this), this.p.remapColumns);
}
});
$grid.jqGrid('navButtonAdd', '#pager', {
caption: "",
buttonicon: "ui-icon-calculator",
title: "choose columns",
onClickButton: function () {
$(this).jqGrid('columnChooser', {
done: function (perm) {
if (perm) {
this.jqGrid("remapColumns", perm, true);
saveColumnState.call(this, perm);
}
}
});
}
});
$grid.jqGrid('navButtonAdd', '#pager', {
caption: "",
buttonicon: "ui-icon-closethick",
title: "clear saved grid's settings",
onClickButton: function () {
removeObjectFromLocalStorage(myColumnStateName);
}
});
Be carefully to define myColumnStateName (the value `'ColumnChooserAndLocalStorage.colState'``) in the demo) to different values on the different pages.
The second demo is the extension of the first one using the technique from my old answer to your another question. The demo use the searching toolbar and synchronize additionally information between the advanced searching form and the searching toolbar.
UPDATED: The next answer contains extended version of the code included above. It shows how to persist the selected rows (or row) additionally. Another answer shows how to persist the list of expanded nodes of the tree grid and expand the nodes on the relaoding of the page.

Writing custom controls in Sproutcore 2

I'm fairly new to Sproutcore, but I am familiar with Handlebars. I have walked through the Todo tutorial and checked out a few other samples as well.
I love everything about it and would like to use it over Backbone, but I am having a hard time understanding how to wire up custom controls. I can see where some of the data will play into the bindings, but triggering events I get lost in.
As an example, if I had a link list that I would like to use to filter data below it, how to do I tie into the events? I know in backbone you would use the event and selector: "click .link"
Any help would be greatly appreciated!
It sounds like you want to loop through a list of objects and create links that, when clicked, calls some JavaScript code that has access to the original objects.
At the moment, the easiest way to do that is to bind the template context to a new custom view. You can see everything in action at this JSFiddle: http://jsfiddle.net/67GQb/
Template:
{{#each App.people}}
{{#view App.PersonView contentBinding="this"}}
{{content.fullName}}
{{/view}}
{{/each}}
App Code:
App = SC.Application.create();
App.Person = SC.Object.extend({
fullName: function() {
return this.get('firstName') + ' ' + this.get('lastName');
}.property('firstName', 'lastName')
});
App.people = [
App.Person.create({ firstName: "Yehuda", lastName: "Katz" }),
App.Person.create({ firstName: "Tom", lastName: "Dale" })
];
App.PersonView = SC.View.extend({
mouseDown: function() {
// Note that content is bound to the current template
// context in the template above.
var person = this.get('content');
alert(person.get('firstName'));
}
});
That said, we understand that this is a bit cumbersome, and have some ideas for further streamlining the process that we will be working on in the upcoming weeks.
Here's are list of demo apps.
http://blog.sproutcore.com/announcing-the-winners-of-the-demo-apps-contest/
Here's a plug for my own open source demo app that I entered - chililog. I'm blogging about how I've used sproutcore at the blog.chililog.org.
Hope this helps.
An alternative way to achieve what Yehuda is doing above is to use the #collection directive:
Template code:
{{#collection App.PeopleView contentBinding="App.people"}}
{{content.fullName}}
{{/collection}}
App Code:
App = SC.Application.create();
App.Person = SC.Object.extend({
fullName: function() {
return this.get('firstName') + ' ' + this.get('lastName');
}.property('firstName', 'lastName')
});
App.people = [
App.Person.create({ firstName: "Yehuda", lastName: "Katz" }),
App.Person.create({ firstName: "Tom", lastName: "Dale" })
];
App.PeopleView = SC.CollectionView.extend({
itemViewClass: SC.View.extend({
mouseDown: function() {
var person = this.get('content');
alert(person.get('firstName'));
}
})
});

ASP.NET MVC + jqGrid without AJAX

I have an ASP.NET MVC application which is executing a search against a products database. I want to display the results in a jqGrid using the TreeGrid module. I don't really need the grid to be AJAX-y because the data is static and it is small enough that it can all be sent to the client at once.
First question: how do I set up jqGrid so that instead of pulling the JSON data from a URL it just looks in a JS variable or something?
Secondly, what is the most appropriate way to get ASP.NET MVC to put JSON data into a JavaScript variable? I already have the List in my controller and just want to somehow get it out into a JS variable after JSON-ing it.
Or am I fighting against the current too much and just accept the AJAX-y way that jqGrid seems to want to work?
Thanks,
~ Justin
Here is how to display a jqGrid tree using a JavaScript function.
$(document).ready(function() {
TreeDemo.setupGrid($("#tree"));
});
TreeDemo = {
data: { A: ["A1", "A2"], B: ["B1", "B2"] },
setupGrid: function(grid) {
grid.jqGrid({
colNames: ['Name'],
colModel: [
{ name: 'Name', index: 'Name', width: "250em" }
],
datatype: TreeDemo.treeData,
loadui: "none",
sortname: 'Number',
treeGrid: true,
treeGridModel: "adjacency",
sortorder: "asc"
})
},
treeData: function(postdata) {
var items = postdata.nodeid ? TreeDemo.data[postdata.nodeid] : TreeDemo.data;
var i = 0;
var rows = new Array();
for (val in items) {
var isLeaf = postdata.nodeid != undefined;
rows[i] = {
Name: val,
Id: val,
level: postdata.nodeid ? 1 : 0,
parent: postdata.nodeid || null,
isLeaf: isLeaf ? "true" : "false",
expanded: "false"
}
i++;
}
$("#tree")[0].addJSONData({
Total: 1,
Page: 1,
Records: 2,
Rows: rows
});
}
};
Note that there are lots of options for how you do this and my example is only one.
The way I would get the JSON into a JS var is to either:
Write a HTML Helper which emits a short script to the page.
Write an action which returns a JavaScriptResult to get the data in a file, if, for some reason, you can't have the data inline.
You create the JSON using the .NET JavaScript serializer. Look at the JsonResult.ExecuteResult in the MVC source code for an example.
See the Data Manipulation page in the jqGrid documentation wiki. There you'll find many ways to feed the data to the grid.
There is also a Table_to_jqGrid plugin that may be an useful option.

Resources