I'm trying to populate a GXT Grid using data retrieved from an online API (for instance, going to www.example.com/documents returns a JSON array of documents). In addition, I need to paginate the result.
I've read all the various blogs and tutorials, but most of them populate the pagination proxy using something like TestData.GetDocuments(). However, I want to get that info using HTTP GET.
I've managed to populate a grid, but without pagination, using a RequestBuilder + proxy + reader + loader. But it seems as though the actual loading of the data is "put off" until some hidden stage deep inside the GXT code. Pagination requires that data from the start, so I'm not sure what to do.
Can someone provide a simple code example which does what I need?
Thank you.
I managed to get this going, here is what I did:
First I defined the proxy and loader for my data along with the paging toolbat:
private PagingModelMemoryProxy proxy;
private PagingLoader<PagingLoadResult<ModelData>> loader;
private PagingToolBar toolBar;
Next is the creation of each one, initializing with an empty ArrayList.
proxy = new PagingModelMemoryProxy(new ArrayList<EquipmentModel>());
loader = new BasePagingLoader<PagingLoadResult<ModelData>>(proxy);
loader.setRemoteSort(true);
toolBar = new PagingToolBar(100);
toolBar.bind(loader);
loader.load(0, 100);
Last, I have a set method in my view that gets called when the AJAX call is complete, but you could trigger it anywhere. Here is my entire set method, Equipment and EquipmentModel are my database and view models respectively.
public void setEquipmentData(List<Equipment> data)
{
Collections.sort(data);
// build a list of models to be loaded
List<EquipmentModel> models = new ArrayList<EquipmentModel>();
for (Equipment equipment : data)
{
EquipmentModel model = new EquipmentModel(equipment);
models.add(model);
}
// load the list of models into the proxy and reconfigure the grid to
// refresh display.
proxy.setData(models);
ListStore<EquipmentModel> equipmentStore = new ListStore<EquipmentModel>(loader);
equipmentGrid.reconfigure(equipmentStore, equipmentColumnModel);
loader.load(0, 100);
}
The key here for me was re-creating the store with the same loader, the column model was pre-created and gets reused.
Related
I have a very weird problem with Unity here. I have the following:
public class UnityConfig
{
public static void RegisterTypes(IUnityContainer container)
container.RegisterType<IDBContext, MyDbContext>(new PerThreadLifetimeManager());
container.RegisterType<IUserDbContext>(new PerThreadLifetimeManager(), new InjectionFactory(c =>
{
var tenantConnectionString = c.Resolve<ITenantConnectionResolver>().ResolveConnectionString();
return new UserDbContext(tenantConnectionString);
}));
}
}
and then in the WebApiConfig.cs file within the Reigster method:
var container = new UnityContainer();
UnityConfig.RegisterTypes(container);
config.DependencyResolver = new UnityResolver(container);
Basically, what I want to happen in the above code is on every request to the API, I want Unity to new up a UserDbContext based on the user (multi-tenant kind of environment). Now the TenantConnectionResolver is responsible for figuring out the Connection String and then I use that connection string to new up UserDbContext.
Also note (not shown above) that TenantConnectionResolver takes an IDbConext in its constructor because I need it to figure out the connection string based on user information in that database.
But for some reason, the code within the InjectionFactory runs at random times. For example, I call //mysite.com/controller/action/1 repetitively from a browser, the code in the InjectionFactory will occasionally run but not on each request.
Am I incorrectly configuring Unity? Has anybody encountered anything similar to this?
Thanks in advance
The problem is very likely related to the LifetimeManager you are using. PerThreadLifetimeManager is not adapted in a web context, as threads are pooled and will serve multiple requests in sequence.
PerRequestLifetimeManager is probably what you want to use.
I am binding an aggregation to a table . I couldn't find an event which is triggered after the binding is complete . There is "updateFinished" event for sap.m.List , which is exactly what I am looking for in a Table (and a dropodown). I thought of using attachRequestCompleted() on the model , but the model is used at other places where I do not want this event to trigger.
Is there anyway to trigger a event once the databinding is complete on a Table (and a dropdown)?
Any help is appreciated.
Thanks in advance.
update: There is "updateFinished" event for table extended from ListBase. I am still not sure how I missed it before I posted this question. But, the question is still valid for a dropdown and TableSelectDialog controls.
I also stumbled upon that problem, but in a different Context.
I have a Grid layout in which I dynamically load Panels via an oData Model.
Therefore I have entered the path in my XML Grid-View element.
<l:Grid id="grid" content="{some path...}">...</l:Grid>
Now I wanted to set the grid view busy and when the data is loaded revert this.
Therefore I use the Binding of the grid view.
In the Controllers onInit method I have added:
this._oGrid = this.getView().byId("grid");
this.getRouter().attachRouteMatched(this._onRouteMatch.bind(this));
Please note that the bind method is not available in every browser. You need to apply a polyfill. (See https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Function/bind)
Also Bind has nothing to do with the binding :D
I needed to do this because the Binding is not available in the onInit function.
The _onRouteMatched function:
var oContent = this._oGrid.getBinding("content");
oContent.attachDataReceived(function(oData) {
this._oGrid.setBusy(false);
}.bind(this));
Now when the data is received the busy option is set to false.
If you want to show a 'loading' indicator for your table while the data is still loading (and thus not bound), I think the best approach is the following:
Use a dedicated JSONModel which holds only UI-specific stuff, like toggling enabled/readonly/busy/etc properties of controls.
In your case, something like:
var oUIModelData = {
tableIsBusy : false,
//etc, things like :
btnSubmitEnabled : false,
someControlVisible : true
};
var oUIModel = new sap.ui.model.json.JSONModel();
oUIModel.setData(oUIModelData);
sap.ui.getCore().setModel(oUIModel, "UIModel");
In your table definition, bind the busy property to {UIModel>/tableIsBusy} and set the table's property busyIndicatorDelay to 0 to avoid any delays
Just before you do your OData service call, set the property tableBusy to true. This will immediately show the busy overlay to your table:
sap.ui.getCore().getModel("UIModel").setProperty(tableIsBusy, true);
//here your OData read call
In your OData service's requestCompleted (and if needed, also in requestFailed) event handlers, set the busy property of the UIModel back to false:
sap.ui.getCore().getModel("UIModel").setProperty(tableIsBusy, false);
The big benefit of this approach is (IMHO) instead of relying on each control to check whether the data has been loaded, simply do it during the actual load.
And by binding these UI-related things to a (separate) model saves you from writing lots of extra code ;-)
In general you could solve the problem by using batch processing on the OData service. According to https://sapui5.netweaver.ondemand.com/docs/guide/6c47b2b39db9404582994070ec3d57a2.html:
Use OData model v2.
var oModel = new sap.ui.model.odata.v2.ODataModel(myServiceUrl);
Define a set of deferred batch groups.
oModel.setDeferredBatchGroups(["myGroupId", "myGroupId2"]);
Add the batch group information to the corresponding bindings, e.g:
{
path:"/myEntities",
parameters: {
batchGroupId: "myGroupId"
}
}
All read/query actions on bindings in a certain batch group will be held back until a .submitChanges(.) call on the batch group is made.
oModel.submitChanges({
batchGroupId: "myGroupId",
success: mySuccessHandler,
error: myErrorHandler
});
Use the success/error handlers to execute actions.
This rather generic approach gives you additional control such as trigger actions, grouping and event handling on the OData model.
I realize that this is a pretty specific question but I would imagine someone has run into this before. So I've got about fifty pages or so that were created about a year ago. We're trying to revamp the page with new components specifically in the header and the footer. Except the content in the main-content area will stay the same. So I'm trying to move over everything from the old pages to the new pages but just keep the main-content area. The problem is I can't just change the resource type on the old page to point to the new page components because the content is different and I'll have a bunch of nodes in the header and footer that I don't want. For example here is my current content structure:
Old Content
star-trek
jcr:content
header
nav
social
chat
main-content
column-one
column-two
footer
sign-up
mega-menu
New Content
star-wars
jcr:content
masthead
mega-menu
main-content
column-one
column-two
bottom-footer
left-links
right-links
Does anybody have any ideas on how to move just the content in the main-content node and somehow remove the other nodes. I'm trying to somehow do this programmatically cause I don't want to create 50 pages from scratch. Any help is appreciated!
You can use the JCR API to move things around at will, I would
Block users from accessing the content in question. Can be done with temporary ACLs, or by closing access on the front-end if you can.
Run a script or servlet that changes the content using JCR APIs
Check the results
Let users access the content again
For the content modification script I suggest a script that modifies a single page (i.e. you call it with an HTTP request that ends in /content/star-trek.modify.txt) so that you can run it either on a single page, for testing, or on a group of pages once it's good.
The script starts form the current node, recurses into it to find nodes that it knowns how to modify (based on their sling:resourceType), modifies them and reports what it did in the logs or on its output.
To modify nodes the script uses the JCR Node APIs to move things around (and maybe Worskpace.move).
It is indeed possible to write a code which does what you need :
package com.test;
import java.io.File;
import java.io.IOException;
import javax.jcr.ItemExistsException;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.jcr.Node;
import org.apache.jackrabbit.commons.JcrUtils;
import org.apache.jackrabbit.core.TransientRepository;
import org.xml.sax.SAXException;
public class test {
public void test(Document doc) throws RepositoryException {
try {
// Create a connection to the CQ repository running on local host
Repository repository = JcrUtils
.getRepository("http://localhost:4502/crx/server");
System.out.println("rep is created");
// Create a Session
javax.jcr.Session session = repository.login(new SimpleCredentials(
"admin", "admin".toCharArray()));
System.out.println("session is created");
String starTrekNodePath = "/content/path/";
String starWarsNodePath = "/content/anotherPath"
Node starTrekpageJcrNode = null;
Node starWarstext = null;
setProperty(java.lang.String name, Node value)
boolean starTrekNodeFlag = session.nodeExists(starTrekNodePath);
boolean starWarsNodeFlag = session.nodeExists(starWarsNodePath);
if (starTrekNodeFlag && starWarsNodeFlag) {
System.out.println("to infinity and beyond");
Node starTrekNode = session.getNode(starTrekNodePath);
Node starWarsNodeFlag = session.getNode(starWarsNodePath);
//apply nested looping logic here; to loop through all pages under this node
//assumption is that you have similar page titles or something
//on these lines to determine target and destination nodes
//2nd assumption is that destination pages exist with the component structures in q
//once we have the target nodes, the following segment should be all you need
Node starTrekChildNode = iterator.next();//assuming you use some form of iterator for looping logic
Node starWarsChildNode = iterator1.next();//assuming you use some form of iterator for looping logic
//next we get the jcr:content node of the target and child nodes
Node starTrekChildJcrNode = starTrekChildNode.getNode("jcr:content");
Node starWarsChildJcrNode = starWarsChildNode.getNode("jcr:content");
// now we fetch the main-component nodes.
Node starTrekChildMCNode = starTrekChildJcrNode.getNode("main-content");
Node starWarsChildMCNode = starWarsChildJcrNode.getNode("main-content");
//now we fetch each component node
Node starTrekChildC1Node = starTrekChildMCNode.getNode("column-one");
Node starTrekChildC2Node = starTrekChildMCNode.getNode("column-two");
Node starWarsChildC1Node = starWarsChildMCNode.getNode("column-one");
Node starWarsChildC2Node = starWarsChildMCNode.getNode("column-two");
// fetch the properties for each node of column-one and column-two from target
String prop1;
String prop2;
PropertyIterator iterator = starTrekChildC1Node.getProperties(propName);
while (iterator.hasNext()) {
Property prop = iterator.nextProperty();
prop1 = prop.getString();
}
PropertyIterator iterator = starTrekChildC2Node.getProperties(propName);
while (iterator.hasNext()) {
Property prop = iterator.nextProperty();
prop2 = prop.getString();
}
// and now we set the values
starWarsChildC1Node.setProperty("property-name",prop1);
starWarsChildC2Node.setProperty("property-name",prop2);
//end loops as appropriate
}
Hopefully this should set you on the right track. You'd have to figure out how you want to identify destination and target pages, based on your folder structure in /content, but the essential logic should be the same
The problem with the results I'm seeing here is that you are writing servlets that execute JCR operations to move things around. While technically that works, it's not really a scalable or reusable way to do this. You have to write some very specific code, deploy it, execute it, then delete it (or it lives out there forever). It's kind of impractical and not totally RESTful.
Here are two better options:
One of my colleagues wrote the CQ Groovy Console, which gives you the ability to use Groovy to script changes to the repository. We frequently use it for content transformations, like you've described. The advantage to using Groovy is that it's script (not compiled/deployed code). You still have access to the JCR API if you need it, but the console has a bunch of helper methods to make things even easier. I highly recommend this approach.
https://github.com/Citytechinc/cq-groovy-console
The other approach is to use the Bulk Editor tool in AEM. You can export a TSV of content, make changes, then reimport. You'll have to turn the import feature on using an administrative function, but I've used this with some success. Beware, it's a bit buggy though, with array value properties.
I am using smart gwt 2.5 List grid.
In this I am using check box for simple selection.below is the code for that:
getGrid().setSelectionAppearance(SelectionAppearance.CHECKBOX);
getGrid().setSelectionType(SelectionStyle.SIMPLE);
I am using data source for the list grid.For feeding data calling this method:
public void setTestData(DataClass[] testData) {
setAttribute("testData", testData, true);
}
My grid is rendering data properly.Now I want to set checked some of records (Row) depending in some condition.Lets say I want row number 1 and 5 should be checked.
For this after feeding data to data source I write code like below to check data:
getGrid().selectRecord(1);`
getGrid().selectRecord(5);
But is not doing any checked operations to check box.I am not getting at what point I am doing mistake.
same thing when I tried with out data source and I feed data to list grid simply by setdata It was working.
Please help me out.Thanks in advance
When you use a DataSource, including a clientOnly DataSource, fetching data is asynchronous, so your selectRecord() calls are happening when data is not loaded yet. Wait for DataArrived before attempting to select records.
One work around which is working for me is that:
I removed selection appearance and selected type from my grid and took a column for check box
like below:
NTListGridField customCheckBoxField = new NTListGridField("select", "Select", 25);
customCheckBoxField.setAlign(Alignment.LEFT);
customCheckBoxField.setType(ListGridFieldType.BOOLEAN);
customCheckBoxField.setCanFilter(false);
customCheckBoxField.setCanEdit(true);
customCheckBoxField.setCanToggle(true);
Now for selecting records,
record.setAttribute("select", true);
I've been looking on forums for 2 days now and can't find a good answer so I'll just post it.
I appear to be having a problem posting JSON back to the controller to save. The JSON should map to model view but it keeps getting default(constructor)values rather then the values from the POST.
We have a series of JS widgets that contain a data field with json in them. We do all our data manipulation in these widget objects on the client side. When a user wants to save we grab the data we need from the widgets involved and we put it into another JSON object that matches a ViewModel and POST that back to the server.
For example:
$("#Save").click(function () {
if (itemDetails.preparedForSubmit() && itemConnections.preparedForSubmit()) {
itemComposite.data.Details = itemDetails.data;
itemComposite.data.Connections= itemConnections.data;
$.post(MYURL, itemComposite.data);
} else {
alert("failed to save");
}
});
The preparedForSubmit() method simple does stuff like any validation checks or last minute formatting you might need to do client side.
The itemDetails widgets data matches a ViewModel.
The itemConnections widgets data matches a collection of ViewModels.
The Controller looks like this:
[HttpPost]
virtual public JsonResult SaveItemDetailsComposite(ItemComposite inItemData)
{
if (ModelState.IsValid)
{
try
{
_Mapper.Save(itemComposite.Details , itemComposite.Connections);
return Json(true);
}
catch (Exception ex)
{
_log.Error("Exception " + ex.InnerException.Message);
throw;
}
}
return Json(SiteMasterUtilities.CreateValidationErrorResponse(ModelState));
}
The ItemComposite Class is a simple View Model that contains a single itemDetails object and a collection of itemConnections. When it returns data to here it is just getting the default data as if it got a new ItemComposite rather than converting the POST data.
in Firebug I see the data is posted. Although it looks weird not automatically formatted in firebug.
Are you saying that itemComposite.data is formatted as a JSON object? If so, I'm pretty sure you're going to have to de-serialize it before you can cast it to your object. Something like:
ItemComposite ic = jsSerializer.Deserialize<ItemComposite>(this.HttpContext.Request.Params[0]);
You may want to look into a framework like JSON.NET to ensure that your data is being serialized properly when it gets supplied to your Action.
JSON.NET seems like it's one of the main stream frameworks: http://json.codeplex.com/releases/view/43775
Hope this helps.
Cory
You could also use the JSON Serializer in WCF: http://msdn.microsoft.com/en-us/library/system.runtime.serialization.json.datacontractjsonserializer.aspx
SO wouldn't let me put both links in one answer, sorry for the split answer.
Thanks everyone. I think I have solved my problem and I'm pretty sure that I had four issues. For the most part I followed thatSteveguys's suggestion and read more on this article: http://haacked.com/archive/2010/04/15/sending-json-to-an-asp-net-mvc-action-method-argument.aspx
Using jQuery's post() method and specifying json as the type didn't seem to actually send it as json. By using the ajax() method and specifying json it sent it as json.
The JSON.serialize() method was also need to cleanly send over the json.
Also my ViewModel design was a big problem. We are using the MS code analytic build junk and it didn't want me having a setter for my collections in the ViewModel. So me being from a java/hibernate world, thought it didn't need them to bind and it would just come in as a serialized object magically. Once I just suppressed the error and reset up my setters. I am getting the collections now in my controller.
I believe using the MVC2 Future's Value Providers are doing something but it still doesn't convert json dates robustly, So I am still investigating the best way to do that.
I hope my issues help out others.
UPDATE: using this method to update collections of data appears to be super slow. A collection with 200 entries in it and 8 fields per entry takes 3 minutes to get to the controller. Just 1 or 2 entries take very little time. The only thing I know of that is happening between here is data binding to the model view. I don't know if MVC2 provides a easy way to send this much data and bind it.