Wednesday, 11 June 2014

Snippets Code in AX 2012

Code Snippets in AX 2012

I wanted to post a little follow-up on an article presented by my fellow blogger Brandon George. In his postXppSource Exposed: Inserting Code Snippets he explains to his colleague the mysterious source of the code snippets you can insert by typing keywords and pressing TAB in the Dynamics AX 2012 editor. Although the XppSource class in the AOT does contain the code for those snippets, it is not where they originate.

In this post I will show you how to add your own code snippets. Since they are X++ code, you can even pop up a dialog to ask for parameters.

First of all, what are we talking about? Well, in the Dynamics AX 2012 editor, you can type certain keywords that trigger code snippets. For example, open a code editor window and type "whileSelect" (without quotes, and a capital S! this is case sensitive).


after the whileSelect statement, press the TAB key. You will see the code snippet pop in.


There is the XppSource class in the AOT, which does contain the code for these snippets. However, the real trigger is the EditorScripts class, which has existed for quite a few versions in AX, but was previously only available by right-click in the code editor, and going to the "Scripts" menu. Note that the menus you get to see are somewhat intelligent, and you will get different templates if you have an editor window open for a job versus a method on a class. For example, if you add a new method on a class, you delete all the default code, and type "parm" and press TAB.


This will open a dialog to enter some parameters to create a new parm method.



There are templates for parm, main, etc. These are however not available when you are editing a Job, for example.

The EditorScripts class works by naming your methods after the "Scripts" menu path from the code editor. For example, the scripts menu has sub-menus named "addIns", "sendTo" and "template". You will notice there are a bunch of methods in the class that are prefixed with "addIns_", "sendTo_" and "template_". This is exactly how the scripts menu is built, it parses the method names and creates submenus. As you have probably guessed by now, by adding new methods with the "template_" prefix, you can add your own code snippets. Note that you can create new sub-menus as you want.

So let's create a new code snippet. We'll add a template method for a "find" method on a table. Now, the EditorScripts class knows a lot about the code window you have open. It knows the object you are editing code on, for example. So thinking about a find method... The EditorScript should know pretty much everything there is to know. If it knows what table we are editing on, it can find out what the primary key fields are. If it knows what the table is and what the primary key fields are, it can automatically create the code for the find method.
So, to follow the Dynamics AX practice of adding the code to the XppSource, we will start by adding a new method for "findMethod".
Now, this code is fairly elaborate and sort of takes away of the point of this article about code snippets, but I figured it's such a useful template to have that I would post the whole thing. I'm sure there's room for improvement, but this gets the job done, feel free to leave your comments and questions below, let me know if you are using this!.
Source findMethod(TableName _tableName, boolean _useReplacementKey = false)
{
    DictTable dictTable = new DictTable(tableName2id(_tableName));
    DictIndex dictIndex = new DictIndex(tableName2id(_tableName),
        (_useReplacementKey && dictTable.replacementKey() != 0) ? dictTable.replacementKey() : dictTable.primaryIndex());
    DictField dictField;
    int fieldIndex;

    if (dictIndex == null)
        throw error(strFmt("Couldn't find primary index for table %1", _tableName));

    // method signature returning the table
    source += strFmt('public static %1 find(', _tableName);

    // add the primary key fields as the parameters to the find method
    for (fieldIndex=1; fieldIndex<=dictIndex.numberOfFields(); fieldIndex++)
    {
        dictField = new DictField(dictTable.id(), dictIndex.field(fieldIndex));
        source += strFmt('%1 _%2, ', extendedTypeId2name(dictField.typeId()), dictField.name());
    }
    source += strFmt('boolean _update = false)\n');

    indentLevel = 0;
    this.beginBlock();

    // Declare the table
    source += this.indent() + strFmt('%1 %1;\n', _tableName);
    source += '\n';

    // Set update yes/no
    source += this.indent() + strFmt('%1.selectForUpdate(_update);\n', _tableName);

    // select firstonly
    source += this.indent() + strFmt('select firstOnly %1 where', _tableName);
    // add the primary key fields in the where clause
    for (fieldIndex=1; fieldIndex<=dictIndex.numberOfFields(); fieldIndex++)
    {
        dictField = new DictField(dictTable.id(), dictIndex.field(fieldIndex));
        source += '\n';
        source += this.indent() + strFmt('    %1%2.%3 == _%3', ((fieldIndex>1) ? '&& ' : ''), _tableName, dictField.name());
    }
    source += ';\n';

    source += '\n';

    // return the buffer
    source += this.indent() + strFmt('return %1;\n', _tableName);

    this.endBlock();

    return source;
}

So, next we need to create the method on the EditorScripts class that will call this source method and add it to the editor, based on the Scripts menu or the keyword assigned. Logically this goes under the "methods" sub-menu. So we will call our method "template_method_find". Note that the last portion of this name will be the keyword triggering the code snippet in the editor, and that the keyword is CASE SENSITIVE. Our new method will look like this:
public void template_method_find(Editor editor)
{
    xppSource       xppSource = new xppSource();
    Source          template;
    str             path = editor.path();
    TreeNode        treeNode = path ? TreeNode::findNode(path) : null;
    TableName       tableName;
    #TreeNodeSysNodeType

    if (treeNode)
    {
        treeNode = treeNode.AOTparent();
        if (treeNode && treeNode.treeNodeType().id() == #NT_MEMBERFUNCLIST)
        {
            treeNode = treeNode.AOTparent();
            if (treeNode && treeNode.treeNodeType().id() == #NT_DBTABLE)
            {
                tableName = treeNode.treeNodeName();
            }
        }
    }

    if (!tableName)
    {
        warning("Find method applies to tables only");
        return;
    }

    template = xppSource.findMethod(tableName);

    editor.insertLines(template);
}
Note that the whole treeNode section checks if this method is on a table. Optionally, you could add some code that prompts a dialog to ask for the table name in case the method is not on a table. Feel free to do so.

I tried to find a table without a find method, but I couldn't quite find one right away (and that's a good thing). But of course we can just try this anywhere. Let's open the CustTable table in the AOT, and add a new method. Remove all the existing code and type "find" and hit TAB.



Yay! Observant readers have noticed that the method supports primary key versus alternate key (if you're not sure what this means, check my article on RecId and Alternate Keys!).

Source

No comments:

Post a Comment

Labels

#veryusefulcode (1) AIF (8) AOT Maps (1) Args (1) Ax 2009 Reports (2) AX 2012 navigation (1) Ax 2012 Interview Questions (1) AX 7 (2) AX Architecture (1) Ax Backup (1) AX Workflow (2) AX2012 (1) AX2012 R2 (1) Ax2012R3 (1) AX2012R3 Dynamics Connector Step by Step Installation and Configuration (1) AX2012R3 EP Step by Step Installation and Configuration EP R3 (1) AX2012R3 HelpServer Step by Step Installation and Configuration (1) AX2012R3 Rapid Start Connector Step by Step Installation and Configuration (1) AX2012R3 Report Server and Analysis Server Step by Step Installation and Configuration (1) AX7 (1) Best practices (1) Blocking user to enter (1) Collection Classes (1) Container (1) D365FO (3) Data Migration Frame Work ax 2012R3 (1) Deleting duplicate Record from Table – Ax2012 (1) Delivery due date notification workflow in Ax 2012 (1) Development Steps EP (1) Dimensions (1) DIXF (1) DMF in Ax 2012 R3 (1) Dynamics Ax 2012 Interview Questions (1) DYNAMICS AX 2012 INTERVIEW QUESTIONS PART 2 (1) DYNAMICS AX 7 (1) EDT relation Migration Tool (1) EP AX 2012 (1) Ep Lookup (1) Error (1) Event Handler (1) F5 (1) File Handling (4) Filter on AX2012 Listpage (1) filtering (2) financial dimensions in AX 2012 (3) form (1) images (1) Installation and Configration (4) Installation and Configuration (11) Installation of Management Reporter 2012 for AX 2012 (1) Interaction class in ax 2012 (1) Interview Question (1) Interview Questions For Ax 2012 (1) Invent DIm (1) Jobs (2) license (1) List page and form menuitem enable code (1) Methods (1) microsoft Dynamics AX 365FO (1) Number Sequence Generation – AX 2012 (5) Number Sequence2012 (1) OLTP-OLAP (1) Passing Args (1) Passing form caller and menu item caller in ax 2012 (1) Passing Multiple Records Args (1) Posting in Ax 2012 (1) POSTING PURCHASE ORDER (1) Query (1) Query Filter Form (2) Query Ranges in X++ (1) Question and Answer (1) Report (1) Reports Controller class (1) RLS in ax 2009 (1) SALES ORDER IMPORT/EXPORT FRAMEWORK BY DMF (1) Security (1) security roles (1) Security Sysqueryrangeutil (1) Sharepoint 2016 (1) SQL SERVER (1) SSRS (2) SSRS Reports Controller class (2) Table collections & Virtual company (1) Time (1) TIPS AND TRICKS (1) Web service AIF (3) Web Services on IIS (AIF) Step by Step Installation and Configuration (1) workflow ax2012 (1) Workflow installation (1) Workflow Method (3) X++ (1)