Wicket query API and write data to database on click AjaxEventBehavior - onclick

I have a list of professional league of legends player components (wicket panels) displayed in a grid and for each player, they have their own modal view(also a wicket panel). My problem is on the initial load of the site, since all wicket panels are being created (both player and player-modal panels), the modal panel has a listview of player info and this is taking quite some time to load all players info and write it to a database table. Is there some way that I can make the query happen only when a player is clicked? I thought about putting the query in an AjaxEventBehavior, but i'm getting a NotSerialiableException on my dao. I also considered adding the modal panel ONLY when the player is clicked (also using ajaxeventbehavior) and I couldn't figure out how to dynamically add the html:
What is the best approach so I don't have to query the api/database on initial load of the site?
Here is the Players Panel and you can see I am adding the modalpanel when the player panel is selected:
public PlayerPanel(String id, final IModel<?> model) {
super(id, model);
Label proName = (new Label("proName", model));
proName.add(new AttributeModifier("data-target", "#modal" + model.getClass().toString()));
add(proName);
//initializing daos for player
ApplicationContext context = new ClassPathXmlApplicationContext("Spring-Module.xml");
ProPlayersDaoJdbc proPlayersDao = (ProPlayersDaoJdbc) context.getBean("proPlayersDaoJdbc");
ProGamesDaoJdbc proGamesDao = (ProGamesDaoJdbc) context.getBean("proGamesDaoJdbc");
ArrayList<Players> allPlayers = (ArrayList<Players>) proPlayersDao.listPlayers();
final ArrayList<Games> games = new ArrayList<Games>();
for(Players p : allPlayers){
if(p.getProName().equals(model.getObject().toString())){
region = p.getRegion();
player = p;
Image proImage = new Image("proImage", p.getThumbnailPath());
proImage.add(new AttributeModifier("src", p.getThumbnailPath()));
proImage.add(new AttributeModifier("data-target", "#modal" + model.getObject().toString()));
try {
ArrayList<Games> gamesList = updateGamesPlayed(player.getSummonerId());
for(Games game : gamesList){
games.add(game);
}
} catch (RiotApiException e) {
e.printStackTrace();
}
add(proImage);
}
}
if(!games.isEmpty()){
for(Games game : games){
proGamesDao.create(game);
}
}
add(new ModalPanel("modalPanel", model));
}
The updateGamesPlayed(...) method queries the API and the proGamesDao.create(game) writes it to the db table.

You could have player image/link on click method making database query, set the result as default model object of child panel, and have page or grid refresh via target.add. on the child panel, you could control the size.

Related

Is it possible to create multiple draft items on createdatasource?

I am building an application that will have the ability to create agenda items to discuss in a meeting. The agenda item might include one or more attachments to discuss so there is a one to many relation between the AgendaItems and the AgendaDocs models. So far, I have an insert form that looks like this:
The "Select File" button is a drive picker and the code I have inside the onDocumentSelect event is the following:
var docs = result.docs;
var createDataSource = app.datasources.AgendaDocs.modes.create;
for(var i=0; i<docs.length-1; i++){
var uniqueDraft = createDataSource.item;
createDataSource.items.push(uniqueDraft);
}
for(var i=0; i<createDataSource.items.length-1; i++){
var draft = createDataSource.item;
createDataSource.items[i].DocTitle = docs[i].name;
createDataSource.items[i].DocURL = docs[i].url;
createDataSource.items[i].DriveID = docs[i].id;
}
console.log(createDataSource.items);
The code is supposed to fill out the the List widget below the "Select File" button, but as how you see, the three items are the same. The datasource of the List widget is "AgendaDocs.modes.create" and the datasource of the insert form is "AgendaItems.modes.create".
Reading the official documentation from appmaker, makes me think it is possible since the properties of "CreateDataSource" includes "items". I need help from an expert here. Is this possible? Am I using the wrong approach?
First things first, it seems that you are trying to create records from different models and relationship between them in a one call... at this time App Maker is not that smart to digest such a complex meal. Most likely you'll need to break your flow into multiple steps:
Create (persist) Agenda Item
Create AgendaDocs records and relation with AgendaItem
Similar flow is implemented in Travel Approval template app, but it is not exactly the same as yours, since it doesn't create associations in batches.
Going back to the original question. Yep, it is possible to have multiple drafts, but not with the Create Datasource. You are looking for Manual Save Mode. Somewhere in perfect world your code would look similar to this:
// AgendaItems in Manual Save mode
var agendaDs = app.datasources.AgendaItems;
// this line will create item on client and automatically push it
// to ds.items and set ds.item to it.
agendaDs.createItem();
var agendaDraft = agendaDs.item;
// Field values can be populated from UI via bindings...
agendaDraft.Type = 'X';
agendaDraft.Description = 'Y';
// onDocumentSelect Drive Picker's event handler
var docsDs = agendaDs.relations.AgendaDocs;
result.docs.forEach(function(doc) {
// this line will create item on client and automatically push it
// to ds.items and set ds.item to it...however it will throw an exception
// with this message:
// Cannot save a foreign key association for the 'AgendaItem'
// relation because the target record has not been persisted
// to the server. To fix this, call saveChanges()
// on the data source for that record's model: AgendaItem
docsDs.createItem();
var docDraft = docsDs.item;
docDraft.DocTitle = doc.name;
docDraft.DocURL = doc.url;
docDraft.DriveID = doc.id;
});
// submit button click
agendaDraft.saveChanges();

Relational Query - 2 degrees away

I have three models:
Timesheets
Employee
Manager
I am looking for all timesheets that need to be approved by a manager (many timesheets per employee, one manager per employee).
I have tried creating datasources and prefetching both Employee and Employee.Manager, but I so far no success as of yet.
Is there a trick to this? Do I need to load the query and then do another load? Or create an intermediary datasource that holds both the Timesheet and Employee data or something else?
You can do it by applying a query filter to the datasource onDataLoad event or another event. For example, you could bind the value of a dropdown with Managers to:
#datasource.query.filters.Employee.Manager._equals
- assuming that the datasource of the widget is set to Timesheets.
If you are linking to the page from another page, you could also call a script instead of using a preset action. On the link click, invoke the script below, passing it the desired manager object from the linking page.
function loadPageTimesheets(manager){
app.showPage(app.pages.Timesheets);
app.pages.Timesheets.datasource.query.filters.Employee.Manager._equals = manager;
app.pages.Timesheets.datasource.load();
}
I would recommend to redesign your app a little bit to use full power of App Maker. You can go with Directory Model (Manager -> Employees) plus one table with data (Timesheets). In this case your timesheets query can look similar to this:
// Server side script
function getTimesheets(query) {
var managerEmail = query.parameters.ManagerEmail;
var dirQuery = app.models.Directory.newQuery();
dirQuery.filters.PrimaryEmail._equals = managerEmail;
dirQuery.prefetch.DirectReports._add();
var people = dirQuery.run();
if (people.length === 0) {
return [];
}
var manager = people[0];
// Subordinates lookup can look fancier if you need recursively
// include everybody down the hierarchy chart. In this case
// it also will make sense to update prefetch above to include
// reports of reports of reports...
var subortinatesEmails = manager.DirectReports.map(function(employee) {
return employee.PrimaryEmail;
});
var tsQuery = app.models.Timesheet.newQuery();
tsQuery.filters.EmployeeEmail._in = subortinatesEmails;
return tsQuery.run();
}

Switching between dynamically added user controls showing old values

Edit (more brief):
I have a control "configuration" with a tree view on the left and a list of panels on the right to which i add my controls. These elements are placed inside a table and surrounded by an ajax panel.
Let's say i have a tree node cars with two elements car1 and car2. When i click an item i save the ID and Type of the selected tree node inside hidden fields to be able to load the right user control on Page_Init
protected void Page_Init(object sender, EventArgs e)
{
var type = this.Request.Form["ctl00$MainContent$ctl00$typeId"];
var id = this.Request.Form["ctl00$MainContent$ctl00$entityId"];
if (!string.IsNullOrEmpty(type) && !string.IsNullOrEmpty(id))
{
this.LoadUserControl(type, id);
}
}
private void LoadUserControl(string type, string id)
{
if (Convert.ToInt32(type) != 0)
{
switch (Convert.ToInt32(type))
{
case (int)SkTopics.Product:
this.AddUserControl("../Modules/Product.ascx", this.Product, type, id);
break;
}
}
}
private void AddUserControl(string controlName, Panel panel, string type, string id)
{
// Init new user cotntrol
control = (BaseControl)this.LoadControl(controlName);
control.LoadEntityItem(Convert.ToInt32(id));
// Setting the user control ID
var replace = controlName.Replace("../Modules/", string.Empty);
string userControlId = replace.Split('.')[0] + id;
var targetControl = panel.FindControl(userControlId);
if (object.Equals(targetControl, null))
{
control.ID = userControlId;
control.DataBind();
panel.Controls.Add(control);
}
}
On LoadEntityItem the data for this car are collected from the database and the values are set as Text of textboxes. When i now switch to the second car, the new values are displayed.
Problem: I change the values inside the textboxes and press the save button to call my save method of the user control. From now on, when i switch to the car 1 again, the values for car 2 are still displayed, although the data for car1 are filled. tO see the data for car1 i have to switch to a complete different type of control and then back to the car1.
Another problem is, that i have a binary image inside the user control which Data i fill on init. When i swith to the the next car, where there i no image configured, i set the image data to null, but the image from the previous car i show. Onyl if i set a different image data the new image is displayed.
There is something else i noticed:
Inside the user control i call a javascript funtion but this one is only called on clicking the the save button. It seems that there is a different between the switch of the tree node and the save button click ?
ScriptManager.RegisterStartupScript(this, this.GetType(), "InitScrewPointsForDragging", "InitScrewPointsForDragging();", true);
I hope this was more brief than before.
It seems that the reason for the issue is some improper viewstate handling and after I disable the view state it is working correctly at my side. Here is the code that I updated to have it work at my side:
this.control.EnableViewState = false;
this.control.ID = userControlId;

Adding TextBocex, CheckBoxes on Request C#

I will be starting a project soon at my company and the client would like to have the option in the portal to add textboxes or checkboxes as an administrator,,, so for instance initially I may have something like
Name [textBox]
Phone [textBox]
So the client would like to log in as an admin and be able to add
Name [textBox]
Phone [textBox]
Receive Brochure [checkBox] //added by client.
Forget about the portal and the admin part.. what I would like to know is what would be the best way to design this (the user to be able to add elements)
Any ideas would be much appreciated
Regards
You could create an additional aspx-form in which the User (or the Admin) is able to define and create his/her own forms, you supply the Variable names and they choose to add the controls, save it in a specific scheme in the Database, e.g.
UserForm:
UserID FormID
Form:
FormID FormName
FormElement:
FormID VariableName ControlType Index
Of course this could also be done by an administrator and be visible by everyone.
To view the specific forms you could add yet another aspx-page containing the following code:
protected void Page_Load(object sender, EventArgs e)
{
//you saved the FormName or ID to Session when you accessed it
string formName = Session["FormName"].ToString();
//this handles getting the elements for this Form from DB
List<FormElement> elementList = FormElement.GetForForm(formName);
this.renderForm(elementList);
}
private void renderForm(List<FormElement> eList)
{
foreach(FormElement fe in eList)
{
//Labels left, Controls right, of course this is just a design decision
if(fe.Index%2==1)
{
Label lbl = new Label();
lbl.Text = fe.Variable;
lbl.ID = fe.ControlType + fe.Variable;
divLeft.Controls.Add(lbl);
}
else
{
dynamic ctrl = null;
switch (fe.ControlType)
{
case "TextBox":
ctrl = new TextBox();
break;
case "CheckBox":
ctrl = new CheckBox();
break;
default:
break;
}
ctrl.ID = fe.ControlType + fe.Variable;
divRight.Controls.Add(ctrl);
}
}
}
Later on after the User hitting submit you'd be able to receive the values entered into those Controls by accessing divRight.FindControl(fe.ControlType + fe.Variable) since that should be unique per Form.
This approach assumes you're using .NET 4.0 (because of dynamic), but of course you can do this just fine without it, it'll just be more code.
Please let me know if this is what you searched for or if it was helpful.
Thanks,
Dennis
From what I have done in the past where I had to create a dynamic survey based on a client needs. I had a database that contained the following tables:
Survey - stores list of client surveys.
Controls - listed the type of controls that would be needed. For example, textbox, checkbox etc.
Survey Controls - links all the controls required for a survey.
User values - stores the values entered in a controll based on the survey used.
I then added some logic to dynamically create the controls based on a survey selected by reading the values from my database.

TreeView manipulation, saving adding etc

Here is what I am trying to do. I have a TreeView server side control (asp.net 2.0) and I need the user to be able to add nodes to it, then after all the nodes desired are added, the data should be saved to the database.
Here are some things I would like to pay attention to:
1) I don't want to save the tree data each time the new node is added, but rather keep the data in session until the user decides to save the entire tree. The question here is: can I bind the tree to ArrayList object and keep that object in session (rather than keeping the whole tree in session)? Then each time the node is added I will have to rebind the tree to the ArrayList rather than database.
2) I wish to minimize ViewState, any tips? What works best: compressing viewstate or keeping it all on the server at all times?
Thanks!
Use TreeNodeCollection as your internal array to hold in either ViewState or Session. Here's a rough mock-up of an approach you can use; far from perfect, but should set you on the right track.
TreeView tv = new TreeView();
// Button click event for 'Add Node' button
protected void AddNode(object sender, EventArgs e)
{
if (SaveNodeToDb(txtNewNode.Text, txtNavUrl.Text))
{
// Store user input details for new node in Session
Nodes.Add(new TreeNode() { Text = txtNewNode.Text, NavigateUrl = txtNavUrl.Text });
// Clear and re-add
tv.Nodes.Clear();
foreach (TreeNode n in Nodes)
tv.Nodes.Add(n);
}
}
public bool SaveNodeToDb(string name, string url)
{
// DB save action here.
}
public TreeNodeCollection Nodes
{
get
{
if (Session["UserNodes"] ! = null)
return (TreeNodeCollection) Session["UserNodes"];
else
return new TreeNodeCollection();
}
set
{
Session["UserNodes"] = value;
}
}

Resources