Sometimes, when I need to do more complicated stuff than change one value in datasource, I would like some method on caller. For example I have a form A with overview. Form A has method setName() (I define). I open a related detail (form B). I change something and I want to call setName on caller.
Nowdays I am doing it in following way
element.args().caller().setName();
but I am looking for more idiomatic way. So what is proper way of calling method on caller in AX 2012 R3?
It sounds like you need to change your development methodology if you referencing that many caller-form methods. You would mostly do that for calling doRefresh or calling updateDesign, which are both typically created methods on forms. Beyond updating the design and refeshing, you should use a class form handler.
If you must do a caller-callback, you can validate by doing:
if (formHasMethod(element.args().caller(), identifierstr(updateDesign)))
{
element.args().caller().updateDesign();
}
You can pass your class as the caller. Here is a simple sample set of code:
\Forms\Form1\Designs\Design\[Group:Group]\Button:Button\Methods\clicked:
void clicked()
{
FormRun formRun;
Args args = new Args(formstr(Form2));
TestClass testLocal = new TestClass();
testLocal.parmTestVar('ZZZ');
args.caller(testLocal);
formRun = classfactory.formRunClass(args);
formRun.init();
formRun.run();
formRun.wait(true);
}
\Forms\Form2\Methods\init:
public void init()
{
TestClass testClass;
super();
testClass = element.args().caller() as testClass;
info(strFmt("%1", testClass.parmTestVar()));
}
Look at \Forms\SalesTable and \Classes\SalesTableForm or \Classes\SysCompare\startCompareOfContextProvider and the init method
identifierStr is not depreciated. It's a normal intrinsic function, but you will get a best practice warning if you use the identifierStr function. This is because no existence checking is carried out for identifierStr. Try to use a more specific intrinsic function if one is available. See http://msdn.microsoft.com/en-us/library/aa626893.aspx
You could take a look at Calling methods on a caller form, but identifierStr is deprecated in AX 2012. As far as I know there is no way to check for form methods at compile time.
But I would suggest avoiding methods on forms. Place them in a form handler class instead, you can then use the methodStr function to check for a method.
Related
I am building a simple journal form based on the form pattern DetailsTransaction. In this pattern it has the standard two view layout, the header/*journalTable grid and a lines/*journalTrans grid.
However, when I click the New button two create a new header/journal, it automatically invokes the taskSwitchToDetailsView task and switches to the lines. I wish to block this from happening, but I am unsure on how to do it. Is there a way to block this task from being invoked?
Have you experimented with the viewEditModeHelper() and other form event handlers?
I don't have an environment in front of me now, but here's a little snip that might give you an idea where to look. I know it's not exactly what you're looking for but the same style is what I would think.
[FormEventHandler(formStr(LogisticsPostalAddress), FormEventType::Initialized)]
public static void MyForm_OnInitialized(xFormRun sender, FormEventArgs e)
{
// Subscribe event handlers
FormRun formRun = sender as FormRun;
formRun.viewEditModeHelper().EditModeSwitched += eventhandler(MyEventHandler.ViewEditModeSwitched);
}
There is a lot of complexity around the OOTB journals, and if I needed a robust journal implementation I would have created classes that derived from JournalFormController and JournalFormTable/JournalFormTrans which provide number sequence generation, blocking/locking, validation, and much more very useful and powerful functionality to a journal form + table structure.
However, I don't need any of that. So to solve my specific problem I added this to the create method of the *journalTable datasource's create method (which the super call changes the context of the form to the lines by calling task(#taskSwitchToDetailsView). To counter this, I simply call task(#taskSwitchToGridView) immediately after the super.
[DataSource]
class CustomJournalTable
{
public void create(boolean _append = false)
{
#Task
super(_append);
element.task(#taskSwitchToGridView);
}
}
I have a UserController that has a Destroy function. It is a rather complex function because it demands to destroy all user's data. I have another action, from the Admin panel that deletes all data from a specific set of users.
Since I don't want to replicate the code from the UserController, I would like to call the Destroy function from UserController for each User to destroy its data.
How should I proceed?
Thanks in advance.
Why not move this functionality to a common class method which can be accessed from both the controllers as needed ?
public class UserManager
{
public void Destroy(List<int> userIdsToDestroy)
{
foreach(var userId in userIdsToDestroy)
{
//Execute code to destroy
}
}
}
and from your action methods, you can call it like
var mgr = new UserManager();
var badUsers = new List<int> { 1,2,3};
mgr.Destroy(badUsers);
Update the badUsers variable value as needed based on from where you are calling it.
Shared functionality like this would ideally be in a business layer, and both controllers would call that code. If it's a little app, you could just create a separate folder structure for shared code. Larger projects would have a business layer dll.
Why not make the Destroy() method as a Non-Action method then like
[Non-Action]
public void Destroy(User user)
{
// code goes here
}
You can as well make this Destroy() function as part of your business layer logic instead of handling this in controller. In that case, you call it from anywhere.
If you want it to be #controller, you can as well consider usig [ChildActionOnly] action filter attribute.
I use a unit of work pattern a lot in my flex projects. I'll have a class that might call a web service, put the data in a sqlite db, refresh a model with the data then raise an event.
I usually call these inline and pass in some singleton classes:
protected function CareerSynced():void
{
var process:ProcessWorkouts = new ProcessWorkouts(_dataModel, _trainerModel, _databaseCache, _database.Conn);
process.addEventListener("AllWorkoutsProcessed", AllWorkoutsProcessed);
process.UpdateAllUnprocessed();
}
I'll then get the response like this:
private function AllWorkoutsProcessed(event:DataReceivedEvent):void
{
//do something here
}
My question is, am I adding that event listener correctly? I think I might be causing a memory leak, but I'm not sure. I've also thought about using a weak reference. I'm confused about when to use them. Would this be one of those cases?
Should it be like this?
process.addEventListener("AllWorkoutsProcessed", AllWorkoutsProcessed,false, 0, true);
I would either go with the weak reference or just remove the listener:
private function AllWorkoutsProcessed(event:DataReceivedEvent):void
{
event.target.removeEventListener("AllWorksoutsProcessed",AllWorkoutsProcessed);
}
I could list out my reasons but I'll just point you to this.
The ValidationManager has a public Dictionary for storing UI components that implement the IValidatable interface.
I am testing a command class that needs an instance of ValidationManager and I want it to fail the validations. So I override the ValidationManager's "validateItem()" method like so:
var validationManagerRepos:ValidationManager = ValidationManager(mockRepository.createStub(ValidationManager));
var validationItem:IValidatable = IValidatable(mockRepository.createStub(IValidatable));
var validatableItems:Dictionary = new Dictionary();
validatableItems[validationItem] = false;
SetupResult.forCall(validationManagerRepos.validateItem(validationItem)).returnValue(false);
My problem is in the execute method of the command. It checks to see if the validationItem is both a DisplayObject (isVisble) and IValidatable. Any slick way to stub a typed object AND an interface? Or do I just need to create an instance of some existing object that already satisfies both?
for (var iVal:Object in validationManager.validatableItems)
{
if (isVisible(DisplayObject(iVal)))
{
passed = validationManager.validateItem(IValidatable(iVal));
eventDispatcher.dispatchEvent(new ValidationEvent(ValidationEvent.VALIDATE_COMPLETED, IValidatable(iVal), passed));
if (!passed)
{
allPassed = false;
}
}
}
I'm fairly sure you can't do both within asMock. It's a limitation of the Flash Player because of lack of polymorphism.
I believe what you'll have to do is create a testing object that does both (extend DisplayObject and implement IValidatable) and create a mock object of that.
The concept of a "multimock" is certainly possible, but floxy (the framework that asmock uses to generate dynamic proxies) doesn't support it. I previously considered adding support for it, but it would be difficult to expose via the various Mock metadata and there's be other issues to worry about (like method name clashes).
I agree with J_A_X's recommendation of creating a custom class and then mocking that.
I am using a Cairngorm MVC architecture for my current project.
I have several commands which use the same type of function that returns a value. I would like to have this function in one place, and reuse it, rather than duplicate the code in each command. What is the best way to do this?
Create a static class or static method in one of your Cairngorm classes.
class MyStatic
{
public static function myFunction(value:String):String
{
return "Returning " + value;
}
}
Then where you want to use your function:
import MyStatic;
var str:String = MyStatic.myFunction("test");
Another option is to create a top level function (a la "trace"). Check out this post I wrote here.
You have lots of options here -- publicly defined functions in your model or controller, such as:
var mySharedFunction:Function = function():void
{
trace("foo");
}
... static methods on new or existing classes, etc. Best practice probably depends on what the function needs to do, though. Can you elaborate?
Create an abstract base class for your commands and add your function in the protected scope. If you need to reuse it anywhere else, refactor it into a public static method on a utility class.