How to signal/notify super-controller of change in sub-controller? - scala

In JavaFX, how do you model the following:
I show a List of Customers in a Scene. On the left side there is a table on the right side (contentPane) the currently select customer's details are shown.
(Relevant part of) Main-Controller:
#jfxf.FXML
protected def newButtonPressed(): Unit =
{
contentPane.getChildren.clear
contentPane.getChildren.add(FXMLLoader.load(GUILoader.load("customers/form.fxml")))
}
There is a Button to add a new Customer. Upon clicking this button instead of opening a Popup, I replace the "details"-part of the scene and add a form there.
Now for this form (designed - like the rest of the GUI - in the SceneBuilder and saved as .fxml) I use another controller.
class Form extends Main with jfxf.Initializable
{
#jfxf.FXML
private var foreNameTextField: jfxsc.TextField = _
#jfxf.FXML
private var lastNameTextField: jfxsc.TextField = _
#jfxf.FXML
private var ageTextField: jfxsc.TextField = _
override def initialize(url: URL, resourceBundle: ResourceBundle): Unit =
{
}
#jfxf.FXML
protected def ok(): Unit =
{
// TODO validation
val newPerson = new Person(-1, foreNameTextField.getText, lastNameTextField.getText, ageTextField.getText.toInt)
// Save to DB
// Close whole form
}
}
When I'm done with filling in a new customer's detail I click on another button (that calls ok()) and save it to a database.
What I want to do now is close the form and replace it with the detail-form.
Something like calling a protected method in the main-controller like:
protected def clearDetails(): Unit =
{
contentPane.getChildren.clear
contentPane.getChildren.add(savedOldDetails)
}
won't work of course. (Will throw a runtime-exception because there is no contentpane in the sub/form-controller (even if I make it protected)
In Qt (C++) I'd use signals/slots and connect them accordingly.
Seems like in JavaFX there is nothing the like. How am I supposed to share such information?
Do I need to create a "super-controller" for the contentPane?

(I don't know Scala, so I'll write this in Java, hope that is ok).
You can define an observable property in the Form controller, and observe it when you load the form from the main controller:
public class Form implements Initializable {
private final ObjectProperty<Person> person = new SimpleObjectProperty<>(null);
public ObjectProperty<Person> personProperty() {
return person ;
}
public final Person getPerson() {
return personProperty().get();
}
public final void setPerson(Person person) {
personProperty().set(person);
}
// ... code you had before...
#FXML
protected void ok() {
Person person = new Person(-1, foreNameTextField.getText(), lastNameTextField.getText(), ageTextField.getText());
// save to DB...
personProperty().set(person);
}
}
Now in your main controller, load the form as follows:
#FXML
protected void newButtonPressed() {
contentPane.getChildren().clear();
FXMLLoader loader = new FXMLLoader(getClass().getResource("customers/form.fxml"));
Parent form = loader.load();
Form formController = loader.getController();
formController.personProperty().addListener((obs, oldPerson, newPerson) {
if (newPerson != null) {
// clear form, etc, e.g.
clearDetails();
}
});
contentPane.getChildren().add(form);
}

Related

C#-WinForm - How to close parent form when child is closed?

This is my issue:
I have 3 forms:
Form fParent
Form fChild
Form OpenForm
I want that When I click a button on Form fChild, it shows Form OpenFormand and hides Form fParentand Form fChild.
How can I do that?
Please help me.
Create new object of parent form and close/Hide it. Call Hide() method if you want to hide parent form, or Close() method if you want to close parent form.
The best way for you to do this is create a singleton class, because if you keep on doing this,
Form1 f1 = new Form1();
f1.Show();
this.Hide();
you keep on instantiating a new form and not going back to the previous form
here's how you do singleton design pattern:
public static class SingletonForm
{
private static Form1 _f1;
private static Form2 _f2;
public static Form1 _Form1
{
get
{
if(_f1 == null)
{
_f1 = new Form1();
}
return _f1;
}
}
public static Form2 _Form2
{
get
{
if(_f2 == null)
{
_f2 = new Form2();
}
return _f2;
}
}
}
in your button click in form just call:
SingletonForm._Form1.Hide();
SingletonForm._Form2.ShowDialog();
Assembly frmAssembly = Assembly.LoadFile(Application.ExecutablePath);
foreach (Type type in frmAssembly.GetTypes())
{
if (type.BaseType == typeof(Form))
{
if (type.Name == formname))
{
Form frmshow = (Form)frmAssembly.CreateInstance(type.ToString());
foreach (Form form in this.MdiChildren)
{
form.Close(); // form close or hide
}
frmshow.Show();
}
}
}

Wicket - changing panels through a dropdown

I have a dropdown component added on a page. the purpose of this dropdown is to change the type of input form that is rendered. for example, different forms have different required fields, editable fields, etc.
public final class Test extends WebPage
{
CustomPanel currentPanel = new MeRequest("repeater",FormType.MIN);
public Test(PageParameters parameters)
{
add(currentPanel);
DropDownChoice ddc = new DropDownChoice("panel", new PropertyModel(this, "selected"), panels, choiceRenderer);
ddc.add(new AjaxFormComponentUpdatingBehavior("onchange") {
protected void onUpdate(AjaxRequestTarget target) {
System.out.println("changed");
currentPanel = new MeRequest("repeater",FormType.PRO);
target.add(currentPanel);
}
});
add(ddc);
}
i've tried various options with limited results. the only real success has been updating the model, but what i really want to do is change how the components behave.
any thoughts on what i'm missing?
1) If you want to replace one panel with another you may just do the following.
First of all, you should output the markup id of the original panel:
currentPanel.setOutputMarkupId(true);
And then in the ajax event handler write something like that:
protected void onUpdate(AjaxRequestTarget target) {
CustomPanel newPanel = new MeRequest("repeater", FormType.PRO);
currentPanel.replaceWith(newPanel);
currentPanel = newPanel;
currentPanel.setOutputMarkupId(true);
target.addComponent(currentPanel);
}
In this case with every change of dropdown choice you add new panel to the page and you remove old panel from the page.
2) But I would proposed a slightly different approach to your problem. You should move the construction logic of your panel to the onBeforeRender() method:
public class MeRequest extends Panel {
private FormType formType;
public MeRequest(String id, FormType formType) {
super(id);
this.formType = formType;
// don't forget to output the markup id of the panel
setOutputMarkupId(true);
// constructor without construction logic
}
protected void onBeforeRender() {
// create form and form components based on value of form type
switch (formType) {
case MIN:
// ...
break;
case PRO:
// ...
break;
}
// add form and form components to panel
addOrReplace(form);
form.add(field1);
form.add(field2);
// ...
super.onBeforeRender();
}
public void setFormType(FormType formType) {
this.formType = formType;
}
}
Then you'll be able to only change type of the panel in the ajax event:
protected void onUpdate(AjaxRequestTarget target) {
currentPanel.setFormType(FormType.PRO);
target.addComponent(currentPanel);
}
Thus we rebuilt the original panel without recreating it.

How to achieve a dynamic controller and action method in ASP.NET MVC?

In Asp.net MVC the url structure goes like
http://example.com/{controller}/{action}/{id}
For each "controller", say http://example.com/blog, there is a BlogController.
But my {controller} portion of the url is not decided pre-hand, but it is dynamically determined at run time, how do I create a "dynamic controller" that maps anything to the same controller which then based on the value and determines what to do?
Same thing with {action}, if the {action} portion of my url is also dynamic, is there a way to program this scenario?
Absolutely! You'll need to override the DefaultControllerFactory to find a custom controller if one doesn't exist. Then you'll need to write an IActionInvoker to handle dynamic action names.
Your controller factory will look something like:
public class DynamicControllerFactory : DefaultControllerFactory
{
private readonly IServiceLocator _Locator;
public DynamicControllerFactory(IServiceLocator locator)
{
_Locator = locator;
}
protected override Type GetControllerType(string controllerName)
{
var controllerType = base.GetControllerType(controllerName);
// if a controller wasn't found with a matching name, return our dynamic controller
return controllerType ?? typeof (DynamicController);
}
protected override IController GetControllerInstance(Type controllerType)
{
var controller = base.GetControllerInstance(controllerType) as Controller;
var actionInvoker = _Locator.GetInstance<IActionInvoker>();
if (actionInvoker != null)
{
controller.ActionInvoker = actionInvoker;
}
return controller;
}
}
Then your action invoker would be like:
public class DynamicActionInvoker : ControllerActionInvoker
{
private readonly IServiceLocator _Locator;
public DynamicActionInvoker(IServiceLocator locator)
{
_Locator = locator;
}
protected override ActionDescriptor FindAction(ControllerContext controllerContext,
ControllerDescriptor controllerDescriptor, string actionName)
{
// try to match an existing action name first
var action = base.FindAction(controllerContext, controllerDescriptor, actionName);
if (action != null)
{
return action;
}
// #ray247 The remainder of this you'd probably write on your own...
var actionFinders = _Locator.GetAllInstances<IFindAction>();
if (actionFinders == null)
{
return null;
}
return actionFinders
.Select(f => f.FindAction(controllerContext, controllerDescriptor, actionName))
.Where(d => d != null)
.FirstOrDefault();
}
}
You can see a lot more of this code here. It's an old first draft attempt by myself and a coworker at writing a fully dynamic MVC pipeline. You're free to use it as a reference and copy what you want.
Edit
I figured I should include some background about what that code does. We were trying to dynamically build the MVC layer around a domain model. So if your domain contained a Product class, you could navigate to products\alls to see a list of all products. If you wanted to add a product, you'd navigate to product\add. You could go to product\edit\1 to edit a product. We even tried things like allowing you to edit properties on an entity. So product\editprice\1?value=42 would set the price property of product #1 to 42. (My paths might be a little off, I can't recall the exact syntax anymore.) Hope this helps!
After a little more reflection, there may be a bit simpler way for you to handle the dynamic action names than my other answer. You'll still need to override the default controller factory. I think you could define your route like:
routes.MapRoute("Dynamic", "{controller}/{command}/{id}", new { action = "ProcessCommand" });
Then on your default/dynamic controller you'd have
public ActionResult ProcessCommand(string command, int id)
{
switch(command)
{
// whatever.
}
}
You need to write your own IControllerFactory (or perhaps derive from DefaultControllerFactory) and then register it with ControllerBuilder.
Iam working with it in .Core but i'll share it's MVC version for all, after that i will share the core version
case OwnerType.DynamicPage:
var dp = mediator.Handle(new Domain.DynamicPages.DynamicPageDtoQuery { ShopId = ShopId, SeoId = seoSearchDto.Id }.AsSingle());
if (dp != null)
{
return GetDynamicPage(dp.Id);
}
break;
// some codes
private ActionResult GetDynamicPage(int id)
{
var routeObj = new
{
action = "Detail",
controller = "DynamicPage",
id = id
};
var bController = DependencyResolver.Current.GetService<DynamicPageController>();
SetControllerContext(bController, routeObj);
return bController.Detail(id);
}
// and
private void SetControllerContext(ControllerBase controller, object routeObj)
{
RouteValueDictionary routeValues = new RouteValueDictionary(routeObj);
var vpd = RouteTable.Routes["Default"].GetVirtualPath(this.ControllerContext.RequestContext, routeValues);
RouteData routeData = new RouteData();
foreach (KeyValuePair<string, object> kvp in routeValues)
{
routeData.Values.Add(kvp.Key, kvp.Value);
}
foreach (KeyValuePair<string, object> kvp in vpd.DataTokens)
{
routeData.DataTokens.Add(kvp.Key, kvp.Value);
}
routeData.Route = vpd.Route;
if (routeData.RouteHandler == null)
routeData.RouteHandler = new MvcRouteHandler();
controller.ControllerContext = new ControllerContext(this.ControllerContext.HttpContext, routeData, controller);
}

Stripes : RedirectResolution; How can I redirect to specific action event?

I have an action bean in my stripes application. The default handler/method will display a list of data, a list of all my MarketResearch objects
On my JSP, I can click on one to view its details, this takes me to a different JSP with a pre-populated form based on the particular MarketResearch object that you selected.
I have another method on my action bean which is mapped to the save submit button, this takes in what is on the amended form, and persists it. After this has taken place, I want it to redirect back to the form, rather than to the listing (default handler) action, is this possible?
My action is as follows :
public class MarketResearchAction extends BaseAction
{
#SpringBean
ClientService clientService;
private static final String VIEW = "/jsp/marketResearch.jsp";
private Client client;
private Client clientBeforeChanges;
public Client getClient()
{
return client;
}
public void setClient(Client client)
{
this.client = client;
}
#DefaultHandler
public Resolution viewAll()
{
return new ForwardResolution(VIEW);
}
public Resolution viewClientMarketResearch()
{
if (client.getSector().equals("Education"))
{
return new ForwardResolution("/jsp/marketResearchEducation.jsp");
} else if (client.getSector().equals("Local Government"))
{
return new ForwardResolution("/jsp/marketResearchLocalGovernment.jsp");
} else if (client.getSector().equals("Housing Association"))
{
return new ForwardResolution("/jsp/marketResearchHousing.jsp");
}
return new ForwardResolution("/jsp/viewClientMarketResearch.jsp");
}
public Resolution save()
{
clientBeforeChanges = clientService.getClientById(client.getId());
clientService.persistClient(client);
getContext().getMessages().add(new SimpleMessage("{0} updated", client.getName()));
return new RedirectResolution("/MarketResearch.action").flash(this);
}
public Client getClientBeforeChanges()
{
return clientBeforeChanges;
}
public void setClientBeforeChanges(Client clientBeforeChanges)
{
this.clientBeforeChanges = clientBeforeChanges;
}
public ClientService getClientService()
{
return clientService;
}
public void setClientService(ClientService clientService)
{
this.clientService = clientService;
}
}
Is it possible? Or am I approaching the situation from a bad angle and should re-factor?
Thanks
Yes. You could return a RedirectResolution to the form jsp. If you're having difficulty with the parameters, if you have them in the save() method, you could do like so:
return new RedirectResolution("/theJsp.jsp")
.addParameter("one", one)
.addParameter("two", two)
.addParameter("three", three)
.flash(this);
If you don't have the params that were passed to the form, you'll have to keep them going somehow. You could pass the MarketResearch object through the form so you'd have it there.
<stripes:hidden name="marketResearch" value="${ActionBean.marketResearch}"/>
And add the requisite instance variable/getter/setter on your MarketResearchActionBean.

How to avoid View specific code in my ViewModel

My application has a menu option to allow the creation of a new account. The menu option's command is bound to a command (NewAccountCommand) in my ViewModel. When the user clicks the option to create a new account, the app displays a "New Account" dialog where the user can enter such data as Name, Address, etc... and then clicks "Ok" to close the dialog and create the new account.
I know my code in the ViewModel is not correct because it creates the "New Account" dialog and calls ShowDialog(). Here is a snippet from the VM:
var modelResult = newAccountDialog.ShowDialog();
if (modelResult == true)
{
//Create the new account
}
how do i avoid creating and showing the dialog from within my VM so I can unit test the VM?
I like the approach explained in this codeproject article:
http://www.codeproject.com/KB/WPF/XAMLDialog.aspx
It basically creates a WPF Dialog control that can be embedded in the visual tree of another window or usercontrol.
It then uses a style trigger that causes the dialog to open up whenever there is content in the dialog.
so in you xaml all you have to do is this(where DialogViewModel is a property in you ViewModel):
<MyControls:Dialog Content = {Binding DialogViewModel}/>
and in you ViewModel you just have to do the following:
DialogViewModel = new MyDialogViewModel();
so in unit testing all you have to do is:
MyViewModel model = new MyViewModel();
model.DialogViewModel = new MyDialogViewModel();
model.DialogViewModel.InputProperty = "Here's my input";
//Assert whatever you want...
I personally create a ICommand property in my ViewModel that sets the DialogViewModel property, so that the user can push a button to get the dialog to open up.
So my ViewModel never calls a dialog it just instantiates a property. The view interprets that and display a dialog box. The beauty behind this is that if you decide to change your view at all and maybe not display a dialog, your ViewModel does not have to change one bit. It pushes all the User interaction code where it should be...in the view. And creating a wpf control allows me to re-use it whenever I need to...
There are many ways to do this, this is one I found to be good for me. :)
In scenarios like this, I typically use events. The model can raise an event to ask for information and anybody can respond to it. The view would listen for the event and display the dialog.
public class MyModel
{
public void DoSomething()
{
var e = new SomeQuestionEventArgs();
OnSomeQuestion(e);
if (e.Handled)
mTheAnswer = e.TheAnswer;
}
private string mTheAnswer;
public string TheAnswer
{
get { return mTheAnswer; }
}
public delegate void SomeQuestionHandler(object sender, SomeQuestionEventArgs e);
public event SomeQuestionHandler SomeQuestion;
protected virtual void OnSomeQuestion(SomeQuestionEventArgs e)
{
if (SomeQuestion == null) return;
SomeQuestion(this, e);
}
}
public class SomeQuestionEventArgs
: EventArgs
{
private bool mHandled = false;
public bool Handled
{
get { return mHandled; }
set { mHandled = value; }
}
private string mTheAnswer;
public string TheAnswer
{
get { return mTheAnswer; }
set { mTheAnswer = value; }
}
}
public class MyView
{
private MyModel mModel;
public MyModel Model
{
get { return mModel; }
set
{
if (mModel != null)
mModel.SomeQuestion -= new MyModel.SomeQuestionHandler(mModel_SomeQuestion);
mModel = value;
if (mModel != null)
mModel.SomeQuestion += new MyModel.SomeQuestionHandler(mModel_SomeQuestion);
}
}
void mModel_SomeQuestion(object sender, SomeQuestionEventArgs e)
{
var dlg = new MyDlg();
if (dlg.ShowDialog() != DialogResult.OK) return;
e.Handled = true;
e.TheAnswer = dlg.TheAnswer;
}
}
The WPF Application Framework (WAF) shows a concrete example how to accomplish this.
The ViewModel sample application shows an Email Client in which you can open the “Email Account Settings” dialog. It uses dependency injection (MEF) and so you are still able to unit test the ViewModel.
Hope this helps.
jbe
There are different approaches to this. One common approach is to use some form of dependency injection to inject a dialog service, and use the service.
This allows any implementation of that service (ie: a different view) to be plugged in at runtime, and does give you some decoupling from the ViewModel to View.