• Annonces

    • Olivier Devriese

      Nouveauté du forum, les blogs !   30/04/2016

      Envie de créer votre blog FileMaker ? Ca ne peut pas être plus simple qu'avec cette nouvelle section du site FM Source qui est désormais plus qu'un simple forum mais un vrai centre de ressource. Vous pouvez aussi facilement l'alimenter en le liant avec le flux RSS d'un autre blog que vous possédez déjà.
  • billets
    82
  • commentaires
    3
  • vues
    1 853

Billets dans ce blog

Soliant Consulting

A great skill a Salesforce administrator can have in his or her back pocket is basic knowledge of the Apex programming language. This awareness empowers administrators to better understand the behavior of an application. As a result, administrators can explain underlying logic to users as well as diagnose issues without having to go to a Salesforce developer for help.

To help Salesforce administrators explore learning Apex, I recommend writing a Hello World program. In this post, I will walk you through it. In the process, you will learn a few simple but useful programming concepts: variables, primitives, conditionals and methods.

Using the Developer Console

Before we can start coding our Hello World program, we need to learn how to run it. There are several ways to execute Apex in the Salesforce platform. The simplest one is using the Developer Console to execute code in an anonymous block. Let’s start by opening the console.

  1. Make sure your profile has the "Author Apex" permission enabled.
  2. Click on the gear icon, then select Developer Console. If you are in classic mode, click on your name on the top right, then select Developer Console.
  3. Once the Developer Console loads, click debug on the menu and select Open Execute Anonymous Window. You can also use the shortcut CTRL+E.
Enter Apex Code

Figure 1 - Execute Anonymous Window

  1. Type the following line of code:
       System.debug('Hello world!');
  1. Check the Open Log option.
  2. Click Execute.
  3. Once the Execution Log loads, check the Debug Only checkbox.
  4. You should see your Hello world message. Congratulations, you just wrote your first Apex program!
Execution Log

Figure 2 - Execution Log

Variables

For our next step, let’s try something a little more interesting. We will use a variable and assign "Hello World!" to it. Then, we will output the value of that variable to the debug log.

  1. Open the Execute Anonymous Window from the debug menu or using the shortcut, CTRL+E.
  2. Modify your code to look like this:
       String message = 'Hello world!';
       System.debug(message);
  1. Click Execute.
  2. Once the Execution Log loads, check the Debug Only checkbox.

Notice the output is the same as the previous time we ran the code. Line 1 has two purposes —  one is declaring the variable, which tells Apex we want to create a new variable of type String. The second purpose is to set its value to "Hello world!".

An interesting property of variables is that they are mutable. That means we can modify their content after they are created. Let’s try it. Update the code again and add one more line.

String message = 'Hello world!';
message = 'Hi there!';
System.debug(message);

Run the code again following the same steps as before. This time you will see "Hi there!", but "Hello World!" will not be displayed. Let’s take a look at what just happened. In line 1, we created a variable called message and assigned a value of "Hello World!" to it. Then, in line two, we assigned a new value to it, "Hi there". The new value took the place of the old one, overwriting it.

You can also perform operations on variables. In this case, since we are dealing with Strings, we can do things like concatenation (stitching two or more strings together) or changing the casing to all caps.

String greeting = 'Hello';
String who = 'world';
who = who.toUpperCase();
String message = greeting + ' ' + who + '!';
System.debug(message);

When you run this code, you will see it generates a similar output as the first example. That is because we are concatenating a variable called greeting which contains the word "Hello", a space, the who variable containing "WORLD", and the exclamation point symbol. Those four things together form the phrase "Hello WORLD!".
Are you wondering why the word "world" is all in capitals? That is because of line 3 where we call the toUpperCase() function which gives us back the same string but with all its letters capitalized.

Primitive types

So far, we have only used variables of type String in our examples. String variables allow you to store a collection or characters, numbers or symbols but there are other types that we can use for different purposes. For example, Integer can be used for storing whole numbers, or Double for numbers with decimals. Apex provides 12 primitive types but you can also create custom types by writing your own classes.

List of Apex Primitives

Blob Binary Data
Boolean The values true or false
Date A calendar date
Datetime A calendar date and time
Decimal A number that includes a decimal point
Double A double precision floating point number
ID A 15 or 18-digit Salesforce ID
Integer A whole number -- between -2,147,483,648 and 2,147,483,647
Long A large whole number -- between -263 and 263-1
Object A generic type that can store a value of any other type
String Any set of characters -- surrounded by single quotes
Time A time

Conditional statements

In order to have your code make decisions and behave differently based on the value of a variable (or user input), you need to use a conditional statement. The most common type of conditional is the if-else statement, which is very similar to the IF() function in Salesforce formulas. Just like its formula counterpart, it has three parts: the logical test, the true block, and (optionally) the false block. If the logical test evaluates to true, the true block is executed, otherwise the false/else block is. A code block is one or more lines of code wrapped with curly brackets.

Boolean formal = true;
String who = 'world';
String greeting;
if (formal == true) {
  greeting = 'Hello';
} else {
  greeting = 'Hi';
}
String message = greeting + ' ' + who;
System.debug(message);

When you run this code, you will see that it outputs "Hello world". In this case, the logical test was checking if the variable formal was equal to true. Since that is the case, it executed the first code block (line 5) which sets the variable greeting equals to "Hello". If it had not been true, it would have executed the false/else block (line 7) which would have set the greeting variable to "Hi". You can try running it again, but setting the variable formal equals to false on line 1.

Writing Methods

As a Salesforce administrator, you are probably already familiar with the concept of methods which are very similar to functions in validation rules and formula fields. We have also already used two methods in our examples. One was System.debug(), and the other one was String.toUpperCase(). You already know how to invoke them. Now let’s take a closer look.

Methods represent a block of code that can be invoked (or called) from somewhere else. They are useful in many ways but have two main purposes: First, you can use methods to avoid repetition by writing your code once and calling it multiple times as needed. Second, methods make code cleaner and easier to read by allowing sections to be abstracted out.

Let’s modify our Hello Word program to move the greeting selection logic inside a method, which we will call getGreeting().

Boolean formal = true;
String who = 'world';
String greeting = getGreeting(formal);
String message = greeting + ' ' + who;
System.debug(message);

private String getGreeting(Boolean formal) {
    if (formal == true) {
      return 'Hello';
    } else {
      return 'Hi';
    }
}

Now, if you want to quickly glance at the code, you should be able to tell at a high level what it does just by looking at the first 5 lines. If you are interested in the specifics of how the greeting is selected, you can look at lines 8 through 12 inside the method. This will make it easier for someone else to read your code or even for yourself, if in the future you want to refresh your memory on what the logic does.

This latest variation of the code will output "Hello world" just like previous versions but the order in which the lines get executed is a little different. First lines 1 and 2 will run. Once we get to line 3, in order to get the value to store in greeting, lines 6, 7, and 8 will get executed and getGreeting() will return "Hello". Then execution continues on line 4, where "Hello world" will be stored in message. Lastly, line 5 will be executed which will output the value of message. At that point, the code is finished executing and terminates.

Further Reading

Congratulations, you have reached the end of this tutorial. You should now be able to read and understand the gist of the code in your org, even if you don’t know exactly what each line does. You should also be able to make simple changes to existing code with the supervision of a more experienced developer.

If you enjoyed writing your first program and would like to learn more, you can take a look at these great resources:

The post Hello, World! The Basics of Apex Programming appeared first on Soliant Consulting.


Afficher la totalité du billet

Soliant Consulting

Today I'm sharing a quick tutorial on how to add Lightning Components to Visualforce pages and then take an event from your Lightning Component and have your Visualforce page handle it. This assumes you already have basic knowledge of Visualforce pages and the ability to create a basic Lightning Component. Before you start, make sure you've defined stylings for your Lightning Design System.

In order to construct this, we need a few elements:

  1. An app container (Visualforce page)
  2. A Lightning Component with a related controller and helper class
  3. An Apex Controller
  4. A Lightning Event

This example uses Casper Harmer's code for a Lookup Component. Some functionality was removed, and the event was added:

SVG Component

<aura:component >
 <aura:attribute name="class" type="String" description="CSS classname for the SVG element" />
 <aura:attribute name="xlinkHref" type="String" description="SLDS icon path. Ex: /assets/icons/utility-sprite/svg/symbols.svg#download" />
 <aura:attribute name="ariaHidden" type="String" default="true" description="aria-hidden true or false. defaults to true" />
</aura:component>

Lookup Component

<aura:component controller="SampleController" access="global" >
	<ltng:require styles="{!$Resource.SLDS213 + '/assets/styles/salesforce-lightning-design-system.css'}" />

	<!-- Component Init Handler -->
	<aura:handler name="init" value="{!this}" action="{!c.init}"/>

	<!-- Attributes -->
	<aura:attribute name="parentRecordId" type="Id" description="Record Id of the Host record (ie if this was a lookup on opp, the opp recid)" access="global"/>
	<aura:attribute name="lookupAPIName" type="String" description="Name of the lookup field ie Primary_Contact__c" access="global"/>
	<aura:attribute name="sObjectAPIName" type="String" required="true" description="The API name of the SObject to search" access="global"/>
	<aura:attribute name="label" type="String" required="true" description="The label to assign to the lookup, eg: Account" access="global"/>
	<aura:attribute name="pluralLabel" type="String" required="true" description="The plural label to assign to the lookup, eg: Accounts" access="global"/>
	<aura:attribute name="recordId" type="Id" description="The current record Id to display" access="global"/>
	<aura:attribute name="listIconSVGPath" type="String" default="/resource/SLDS213/assets/icons/custom-sprite/svg/symbols.svg#custom11" description="The static resource path to the svg icon to use." access="global"/>
	<aura:attribute name="listIconClass" type="String" default="slds-icon-custom-11" description="The SLDS class to use for the icon." access="global"/>
	<aura:attribute name="searchString" type="String" description="The search string to find." access="global"/>
	<aura:attribute name="required" type="Boolean" description="Set to true if this lookup is required" access="global"/>
	<aura:attribute name="filter" type="String" required="false" description="SOSL filter string ie AccountId = '0014B000003Sz5s'" access="global"/>

	<aura:attribute name="callback" type="String" description="Call this to communcate results to parent" access="global" />

	<!-- PRIVATE ATTRS -->
	<aura:attribute name="matches" type="SampleController.Result[]" description="The resulting matches returned by the Apex controller." />

	<aura:registerEvent name="updateLookup" type="c:LookupEvent" />

	<div class="slds">
		<div aura:id="lookup-div" class="slds-lookup" data-select="single" data-scope="single" data-typeahead="true">
		<!-- This is the Input form markup -->
		<div class="slds-form-element">
			<label class="slds-form-element__label" for="lookup">{!v.label}</label>
			<div class="slds-form-element__control slds-input-has-icon slds-input-has-icon--right">
		    	<c:PR_SVG class="slds-input__icon" xlinkHref="/resource/SLDS213/assets/icons/utility-sprite/svg/symbols.svg#search" />
			    <!-- This markup is for when an item is currently selected -->
			    <div aura:id="lookup-pill" class="slds-pill-container slds-hide">
		            <span class="slds-pill slds-pill--bare">
		                <span class="slds-pill__label">
		                    <c:PR_SVG class="{!'slds-icon ' + v.listIconClass + ' slds-icon--small'}" xlinkHref="{!v.listIconSVGPath}" />{!v.searchString}
		                </span>
		                <button class="slds-button slds-button--icon-bare" onclick="{!c.clear}">
		                    <c:PR_SVG class="slds-button__icon" xlinkHref="/resource/SLDS213/assets/icons/utility-sprite/svg/symbols.svg#close" />
		                    <span class="slds-assistive-text">Remove</span>
		                </button>
		            </span>
		        </div>
		        <!-- This markup is for when searching for a string -->
		        <ui:inputText aura:id="lookup" value="{!v.searchString}" class="slds-input" updateOn="keyup" keyup="{!c.search}" blur="{!c.handleBlur}"/>
		    </div>
		</div>
			<!-- This is the lookup list markup. Initially it's hidden -->
			<div aura:id="lookuplist" class="" role="listbox">
			    <div class="slds-lookup__item">
			        <button class="slds-button">
			            <c:PR_SVG class="slds-icon slds-icon-text-default slds-icon--small" xlinkHref="/resource/SLDS213/assets/icons/utility-sprite/svg/symbols.svg#search" />
			            &quot;{!v.searchString}&quot; in {!v.pluralLabel}
			        </button>
			    </div>
			    <ul aura:id="lookuplist-items" class="slds-lookup__list">
			        <aura:iteration items="{!v.matches}" var="match">
			            <li class="slds-lookup__item">
			                <a id="{!globalId + '_id_' + match.SObjectId}" role="option" onclick="{!c.select }">
			                    <c:PR_SVG class="{!'slds-icon ' + v.listIconClass + ' slds-icon--small'}" xlinkHref="{!v.listIconSVGPath}" />{!match.SObjectLabel}
			                </a>
			            </li>
			        </aura:iteration>
			    </ul>
			</div>
		</div>
	</div>
</aura:component>

Lookup JS Controller

/**
 * (c) Tony Scott. This code is provided as is and without warranty of any kind.
 * Adapted for use in a VF page, removed need for two components, removed events - Caspar Harmer
 *
 * This work by Tony Scott is licensed under a Creative Commons Attribution 3.0 Unported License.
 * http://creativecommons.org/licenses/by/3.0/deed.en_US
 */
({
    /**
     * Search an SObject for a match
     */
    search : function(cmp, event, helper) {
        helper.doSearch(cmp);
    },

    /**
     * Select an SObject from a list
     */
    select: function(cmp, event, helper) {

        helper.handleSelection(cmp, event);
    },

    /**
     * Clear the currently selected SObject
     */
    clear: function(cmp, event, helper) {

        helper.clearSelection(cmp);
    },

    /**
     * If the input is requred, check if there is a value on blur
     * and mark the input as error if no value
     */
     handleBlur: function (cmp, event, helper) {

         helper.handleBlur(cmp);
     },

    init : function(cmp, event, helper){
      try{
        //first load the current value of the lookup field
        helper.init(cmp);
        helper.loadFirstValue(cmp);

        }catch(ex){
          console.log(ex);
        }
      }
})

Lookup JS Helper

/**
 * (c) Tony Scott. This code is provided as is and without warranty of any kind.
 * Adapted for use in a VF page, removed need for two components, removed events - Caspar Harmer
 *
 * This work by Tony Scott is licensed under a Creative Commons Attribution 3.0 Unported License.
 * http://creativecommons.org/licenses/by/3.0/deed.en_US
 */
({
    //lookup already initialized
    initStatus : {},
    init : function (cmp){
      var required = cmp.get('v.required');
      if (required){
        var cmpTarget = cmp.find('lookup-form-element');
        $A.util.addClass(cmpTarget, 'slds-is-required');
      }
    },

    /**
     * Perform the SObject search via an Apex Controller
     */
    doSearch : function(cmp) {
        // Get the search string, input element and the selection container
        var searchString = cmp.get('v.searchString');
        var inputElement = cmp.find('lookup');
        var lookupList = cmp.find('lookuplist');

        // Clear any errors and destroy the old lookup items container
        inputElement.set('v.errors', null);

        // We need at least 2 characters for an effective search
        console.log('searchString = ' + searchString);
        if (typeof searchString === 'undefined' || searchString.length &lt; 2)
        {
            // Hide the lookuplist
            //$A.util.addClass(lookupList, 'slds-hide');
            return;
        }

        // Show the lookuplist
        console.log('lookupList = ' + lookupList);
        $A.util.removeClass(lookupList, 'slds-hide');

        // Get the API Name
        var sObjectAPIName = cmp.get('v.sObjectAPIName');
        // Get the filter value, if any
        var filter = cmp.get('v.filter');

        // Create an Apex action
        var action = cmp.get('c.lookup');

        // Mark the action as abortable, this is to prevent multiple events from the keyup executing
        action.setAbortable();

        // Set the parameters
        action.setParams({ "searchString" : searchString, "sObjectAPIName" : sObjectAPIName, "filter" : filter});

        // Define the callback
        action.setCallback(this, function(response) {
            var state = response.getState();
            console.log("State: " + state);

            // Callback succeeded
            if (cmp.isValid() &amp;&amp; state === "SUCCESS")
            {
                // Get the search matches
                var matches = response.getReturnValue();
                console.log("matches: " + matches);

                // If we have no matches, return nothing
                if (matches.length == 0)
                {
                    //cmp.set('v.matches', null);
                    return;
                }

                // Store the results
                cmp.set('v.matches', matches);
            }
            else if (state === "ERROR") // Handle any error by reporting it
            {
                var errors = response.getError();

                if (errors)
                {
                    if (errors[0] &amp;&amp; errors[0].message)
                    {
                        this.displayToast('Error', errors[0].message);
                    }
                }
                else
                {
                    this.displayToast('Error', 'Unknown error.');
                }
            }
        });

        // Enqueue the action
        $A.enqueueAction(action);
    },

    /**
     * Handle the Selection of an Item
     */
    handleSelection : function(cmp, event) {
        // Resolve the Object Id from the events Element Id (this will be the &lt;a&gt; tag)
        var objectId = this.resolveId(event.currentTarget.id);
		// Set the Id bound to the View
		cmp.set('v.recordId', objectId);

        // The Object label is the inner text)
        var objectLabel = event.currentTarget.innerText;

		// Update the Searchstring with the Label
		cmp.set("v.searchString", objectLabel);

        // Log the Object Id and Label to the console
        console.log('objectId=' + objectId);
        console.log('objectLabel=' + objectLabel);

        //This is important.  Notice how i get the event.
        var updateEvent = $A.get("e.c:LookupEvent");
		updateEvent.setParams({"lookupVal": objectId, "lookupLabel": objectLabel});
		updateEvent.fire();
    },

    /**
     * Clear the Selection
     */
    clearSelection : function(cmp) {
        // Clear the Searchstring
        cmp.set("v.searchString", '');
				cmp.set('v.recordId', null);

        var func = cmp.get('v.callback');
        console.log(func);
        if (func){
          func({id:'',name:''});
        }

        // Hide the Lookup pill
        var lookupPill = cmp.find("lookup-pill");
        //$A.util.addClass(lookupPill, 'slds-hide');

        // Show the Input Element
        var inputElement = cmp.find('lookup');
        $A.util.removeClass(inputElement, 'slds-hide');

        // Lookup Div has no selection
        var inputElement = cmp.find('lookup-div');
        $A.util.removeClass(inputElement, 'slds-has-selection');

        // If required, add error css
        var required = cmp.get('v.required');
        if (required){
          var cmpTarget = cmp.find('lookup-form-element');
          $A.util.removeClass(cmpTarget, 'slds-has-error');
        }
    },

    handleBlur: function(cmp) {
       var required = cmp.get('v.required');
       if (required){
         var cmpTarget = cmp.find('lookup-form-element');
         $A.util.addClass(cmpTarget, 'slds-has-error');
       }
   },

    /**
     * Resolve the Object Id from the Element Id by splitting the id at the _
     */
    resolveId : function(elmId)
    {
        var i = elmId.lastIndexOf('_');
        return elmId.substr(i+1);
    },

    /**
     * Display a message
     */
    displayToast : function (title, message)
    {
        var toast = $A.get("e.force:showToast");

        // For lightning1 show the toast
        if (toast)
        {
            //fire the toast event in Salesforce1
            toast.setParams({
                "title": title,
                "message": message
            });

            toast.fire();
        }
        else // otherwise throw an alert
        {
            alert(title + ': ' + message);
        }
    },

    loadFirstValue : function(cmp){

        var action = cmp.get('c.getCurrentValue');
        var self = this;
        action.setParams({
            'type' : cmp.get('v.sObjectAPIName'),
            'value' : cmp.get('v.recordId'),
        });

        action.setCallback(this, function(a) {
            if(a.error &amp;&amp; a.error.length){
                return $A.error('Unexpected error: '+a.error[0].message);
            }
            var result = a.getReturnValue();
            cmp.set("v.searchString", result);

            if (null!=result){
              // Show the Lookup pill
              var lookupPill = cmp.find("lookup-pill");
              $A.util.removeClass(lookupPill, 'slds-hide');

              // Lookup Div has selection
              var inputElement = cmp.find('lookup-div');
              $A.util.addClass(inputElement, 'slds-has-selection');
            }
        });
        $A.enqueueAction(action);
    }
})

Apex Controller

public with sharing class SampleController
{
	@AuraEnabled
	public static String getCurrentValue(String type, String value){
		if(String.isBlank(type))
		{
			System.debug('type is null');
			return null;
		}

		ID lookupId = null;
		try
		{
			lookupId = (ID)value;
		}catch(Exception e){
			System.debug('Exception = ' + e.getMessage());
			return null;
		}

		if(String.isBlank(lookupId))
		{
			System.debug('lookup is null');
			return null;
		}

		SObjectType objType = Schema.getGlobalDescribe().get(type);
		if(objType == null){
			System.debug('objType is null');
			return null;
		}

		String nameField = getSobjectNameField(objType);
		String query = 'Select Id, ' + nameField + ' From ' + type + ' Where Id = \'' + lookupId + '\'';
		System.debug('### Query: '+query);
		List&lt;SObject&gt; oList = Database.query(query);
		if(oList.size()==0)
		{
			System.debug('objlist empty');
			return null;
		}
		return (String) oList[0].get(nameField);
	}

	/*
	   * Returns the "Name" field for a given SObject (e.g. Case has CaseNumber, Account has Name)
	*/
	private static String getSobjectNameField(SobjectType sobjType)
	{
		//describes lookup obj and gets its name field
		String nameField = 'Name';
		Schema.DescribeSObjectResult dfrLkp = sobjType.getDescribe();
		for(schema.SObjectField sotype : dfrLkp.fields.getMap().values()){
		  Schema.DescribeFieldResult fieldDescObj = sotype.getDescribe();
		  if(fieldDescObj.isNameField() ){
		    nameField = fieldDescObj.getName();
		      break;
		  }
		}
		return nameField;
	}
	/**
	 * Aura enabled method to search a specified SObject for a specific string
	 */
	@AuraEnabled
	public static Result[] lookup(String searchString, String sObjectAPIName)
	{
		// Sanitze the input
		String sanitizedSearchString = String.escapeSingleQuotes(searchString);
		String sanitizedSObjectAPIName = String.escapeSingleQuotes(sObjectAPIName);

		List&lt;Result&gt; results = new List&lt;Result&gt;();

		// Build our SOSL query
		String searchQuery = 'FIND \'' + sanitizedSearchString + '*\' IN ALL FIELDS RETURNING ' + sanitizedSObjectAPIName + '(id,name) Limit 50';

		// Execute the Query
		List&lt;List&lt;SObject&gt;&gt; searchList = search.query(searchQuery);
		System.debug('searchList = ' + searchList);
		System.debug('searchQuery = ' + searchQuery);

		// Create a list of matches to return
		for (SObject so : searchList[0])
		{
		    results.add(new Result((String)so.get('Name'), so.Id));
		}

		System.debug('results = ' + results);
		return results;
	}

	/**
	 * Inner class to wrap up an SObject Label and its Id
	 */
	public class Result
	{
		@AuraEnabled public String SObjectLabel {get; set;}
		@AuraEnabled public Id SObjectId {get; set;}

		public Result(String sObjectLabel, Id sObjectId)
		{
		    this.SObjectLabel = sObjectLabel;
		    this.SObjectId = sObjectId;
		}
	}
}

Most of this code is from Caspar's post, but there are a few differences. The first major difference is in the helper's "handleSelection" method, where it instead activates the event for the selection and associates values to the event's properties:

var updateEvent = $A.get("e.c:LookupEvent");
updateEvent.setParams({"lookupVal": objectId, "lookupLabel": objectLabel});
updateEvent.fire();

This is important because instead of passing the callback to the Visualforce page instead we will have it waiting for an event to fire. The Lookup component also contained a reference to an event:

<aura:registerEvent name="updateLookup" type="c:LookupEvent" />

The event code is very simple. Note: the type needs to be APPLICATION instead of COMPONENT:

<aura:event type="APPLICATION" description="Event template">
	<aura:attribute name="lookupVal" type="String" description="Response from calls" access="global" />
	<aura:attribute name="lookupLabel" type="String" description="Response from calls" access="global" />
</aura:event>

At this point, you will have an active component that will run an event when a selection is made. The missing link so far is the Visualforce page:


<apex:page applyBodyTag="false" standardController="Contact" docType="html-5.0" showHeader="true" sidebar="false" standardStylesheets="false">
	<html xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg">
		<head>
		  <apex:includeScript value="/lightning/lightning.out.js" />
		  <apex:stylesheet value="{!URLFOR($Resource.SLDS213, 'assets/styles/salesforce-lightning-design-system-vf.css')}"/>
		</head>
		<body >
		  <div class="slds">
		    <div class="slds-form-element slds-m-top--xx-small">
		      <div class="slds-m-right--x-small" id="account_lookup"></div>
		    </div>
		  </div>
		</body>
		<script>
			var visualForceFunction = function(event)
			{
				var myEventData1 = event.getParam("lookupVal");
				var label = event.getParam("lookupLabel");
				console.log('response data = ' + myEventData1 + ' : ' + label);
			};

			// and...
			$Lightning.use("c:expensesAppVF", function()
			{
				$Lightning.createComponent
				(
					"c:Lookup",
					{
						
						recordId: "{!contact.AccountId}",
						label: "Account",
						pluralLabel: "Accounts",
						sObjectAPIName: "Account"
					},
					"account_lookup",
					function()
					{
					    $A.eventService.addHandler({ "event": "c:LookupEvent", "handler" : visualForceFunction});
					}
				);
			});
		</script>
	</html>
</apex:page>

Instead of receiving a callback, this version adds a handler to wait for the event to fire when an Account is selected. This lookup could work for any object that you want as long as you change the API name of the objects.  You can replace the console.log in the event handler and instead have it do some real functionality, such as actually saving the values returned from the Lightning Component on the Salesforce record.

The post Handle Lightning Events from a Visualforce Page appeared first on Soliant Consulting.


Afficher la totalité du billet

Soliant Consulting

FileMaker 16 - Context-Independent Card WindowsWhile you’re in the middle of doing your work, you might want to add something to do your to do list, but you don’t want to lose your spot in your FileMaker solution. With Card Windows, you can open a new window (within the current window) with a new layout that may or may not be related to the main window/layout.

So even though you’re working on an invoice or looking at customer data, a Card Window can allow you to view your to do list, add a new task, close the to do list, and you’re right back where you were. Learn more about this feature by downloading the demo file.

Complete the form to receive the demo file.


Trouble with this form? Click here.

The post FileMaker 16: Context-Independent Card Windows Demo appeared first on Soliant Consulting.


Afficher la totalité du billet

Soliant Consulting

Card Windows for NavigationOne of the most exciting features included with the FileMaker 16 release is Card Windows. We're used to seeing a lightbox on a web page; now you can do the same in FileMaker.

In her video, Martha Zink shows how to use a Card Window for navigation. Download the demo to see how it's done.

Complete the form to receive the demo file.


Trouble with this form? Click here.

The post FileMaker 16: Using Card Windows for Navigation Demo appeared first on Soliant Consulting.


Afficher la totalité du billet

Soliant Consulting
Finished shoes from the engineering challenge

Finished shoes from the engineering challenge (click to enlarge)

In 2014, Soliant began a yearly tradition. On the fourth Monday of each April our Chicago office hosts, “Take Your Kids to Work Day.” Employees bring their children to the office to bond with them and to expand their kid's career horizons. In addition to this event being a fantastic learning experience for the kids, Soliant sees this as a great opportunity to have team engagement in the office.

We have a committee that helps plan the event in advance and to ensure the children not only shadow their parents but also engage in skill-building exercises. Our goal is to expose the kids to new things so each year, so we are always coming up with different activities. Parents and family members also participate in the planning process; they help us brainstorm the activity options with information about their child interests.

Kick-Off Meeting with our New Team

Orientation meeting for the day's activities

Orientation meeting (click to enlarge)

This year, we had a day full of activities planned. We kicked off the day with breakfast and orientation. The first activity planned for the day was to review the positions at Soliant. We explained the different departments and what each person is responsible for in the organization.

To make it more fun and illustrate Soliant's roles, we gave each child a passport and a map. They had to follow the map to find stations around the office to learn more about our team members’ roles and responsibilities. Once they learned about each position, they received a passport stamp.

Passports and map of stations in the office

Passports and map of stations in the office

Engineering Challenge


Next, we gave a presentation about the engineering design process. We explained the series of steps that one must repeat to develop or improve a product, process or system. We split the teams into age groups for a simple coding exercise. Our goal was to make this experience as interactive as possible so no matter what age, they would be able to engage and learn.

Soon enough, it was time for the lunch break. Since the weather was nice and sunny, lunch took place at our rooftop. Pizza seemed like a win-win menu choice for adults and kids; besides, we wanted to keep the little ones fueled for the afternoon activities.


In the afternoon, we had more activities focused on engineering; the kids had to design and create a pair of shoes that could successfully undergo several challenges. The shoes would be tested for comfort and efficiency, and go through several challenges: jumping, walking and stair climbing. Each challenge had a point system, and the goal was to achieve the most points.

Wrapping up Another Awesome Take Your Kids to Work Day


Before the day ended, we asked the children to fill out a time sheet. It was a good exercise to recap the day. The day ended with an evaluation from the kids; we always want to encourage feedback. This year we had tons of fun with the kids and added another successful "Take Your Kids to Work Day." We hope the hands-on experiences gave new skills to the kids and helped them better understand Mom and Dad’s workday. We’re already looking forward to next year!

The post Take Your Kids to Work Day appeared first on Soliant Consulting.


Afficher la totalité du billet

Soliant Consulting

FileMaker 16 introduces newly available options to the "Insert from URL" script step that utilize the populate cURL library. If you are not familiar with cURL, it has long been a standard for command line interaction with all manner of internet protocols and applications. The introduction of these options heralds a host of new options for FileMaker developers looking to interact and integrate with all kinds of web services.

In this post, we will focus on one particularly useful feature: the ability to upload a file stored in a container field directly from the field. That means you do not need to write the data out to the file system before referencing it with your script.

It makes good sense. The location of the data, the contents of the container field, is known, so you should be able to reference it directly without having to save it somewhere locally first. Now we have a way to do just that, even if it is not apparent at first. Admittedly, the syntax was not intuitive for me when I first tried to get this working.

The Need For This Functionality

There are times when you need to upload a file to a web page, and in fact, most web scripting languages have built-in functions to handle file uploads. After all, the HTML form input type "file" has been around forever to allow exactly this sort of functionality. We know we can handle file uploaded on the web server side of things. We will assume that the file is being transferred as binary data and not Base64 encoded. Some web services may require it and there may be limits on the amount of Base64 text you can transfer.

What we do with the file once it is uploaded is irrelevant to this blog post. Our focus is on how to make that request from a FileMaker 16 script. We will assume that the web scripting side of things works as expected.

What we do with the file once it is uploaded is irrelevant to this blog post. Our focus is on how to make that request from a FileMaker 16 script. We will assume that the web scripting side of things works as expected.

Enter cURL

Luckily for us, the cURL functionality needed for this is included with FileMaker 16. External scripting or third party plugins are no longer required. Since "file" is a valid input type for HTML forms, the cURL library includes support for submitting this data as part of an HTTP request, most commonly transferred with the POST method. This means that the payload of the request is not sent as part of the URL string, like you would have with a GET request. Consequently, an arbitrarily large request can be made without running into URL string limits.

cURL syntax with FileMaker Variables

Let's review the relevant steps. First, we set a variable to the value of our container field.

Set Var: ["$myFile" ; Table::UploadField_r ]

How can we use this variable in our cURL expression? The "-F" flag, which is short for "form", allows cURL to emulate a filled in form in which the user has pressed the submit button.

We can use this flag with the new "Insert from URL" cURL options like so:

-F "file=@$myFile"

Note that when we include the variable in the cURL options, it appears inside the quotation marks. This is the correct syntax; placing it outside the quotes will not work.

This is analogous to how a file name is specified inside of quotation marks in an html form element:

<input type="file" name="myFile" />

Similarly, on the server you can access the data the same way any uploaded files are received. For example, with PHP, you can use the $_FILE variable to handle and save the file on the server.

Side Effects

A side effect of this syntax is that, while it allows you to pass the binary data directly from a container, the file name that appears on the receiving end will be the name of the variable you use. In the above example, the file name would be “myFile” which may not be what you want.

A possible solution will be to pass the file name as a separate parameter and then rename the file on the server. However if you are not in control of the web scripting, that won't be an option.

To make it easier for web services to consume, we can set the variable name to the file name of the container data. The trick here is to do this dynamically and to avoid illegal characters and reserved words. Note that variable names are subject to the same rules as field names.

A simplified version of this can be done with a few variables and using the Evaluate and Let functions to dynamically set a variable.

Set Variable [$myFileName ;
Substitute ( GetAsText ( Detail::Upload_File_r ) ; [" " ; "_"] ; ["(";""] ; [")";""] ;  ["+";""] ; ["-";""] ; ["=";""] ; ["≠";""] ; ["&";""] ; ["<";""] ; [">";""] ; [",";""] ; [";";""] ; ["[";""] ; ["]";""] ; [":";""] ; ["\"";""] ; ["$";""] ; ["/";""] ; ["*";""] ; ["^";""] ; ["}";""] ; ["{";""] )  ]
Set Variable [ $null ; Evaluate ( "Let ( $" & $myFileName & " = " & "Detail::Upload_File_r" & " ; \"\" )" ) ] 
Insert From URL [ Select ; With dialog: Off ; $your_result ; $your_url ; cURL options: Evaluate ( "\"-F file=@$" & $myFileName & "\"" ) ]

You may also note that the target of the result of the insert can now be a variable. This is a new change in FileMaker 16. You can now call this function without requiring a field to be placed on a layout to hold the result, as was the case before.

Testing

As mentioned above, you can write your own web script and host it on your web server. You can also use a service like Request Bin to inspect HTTP requests that are made. If we do that, we can see that the file gets submitted as part of the HTTP request and that it is sent as binary data.

Feel free to try it out yourself from the sample file.

The sample file also includes a sample script to download a file directly to a container field. Although the functionality shown is not an entirely new feature to FileMaker, the part that is new in 16 is the ability to verify SSL before downloading data. The sample file has this new feature enabled.

Of course, this just scratches the surface of what you can do with FileMaker 16 and the newly introduced cURL options. I look forward to seeing what can be done with it myself, and seeing what others can come up with.

References

The post FileMaker 16: cURL and Container Fields appeared first on Soliant Consulting.


Afficher la totalité du billet

Soliant Consulting

FileMaker 16 and cURL

FileMaker 16 introduces new options to the "Insert from URL" script step that utilize the populate cURL library. You can even write your own web script and host it on your web server or use a service like Request Bin to inspect HTTP requests.

Try this functionality out yourself. Download our sample file by filling out the form on this page.

This file includes a sample script to download a file directly to a container field and showcases the new ability to verify SSL before downloading data.

Complete the form to receive the demo file:


Trouble with this form? Click here.

The post FileMaker 16: cURL and Container Fields Demo appeared first on Soliant Consulting.


Afficher la totalité du billet

Soliant Consulting

In FileMaker Server 16, under Web Publishing you will find a brand-new entry called FileMaker Data API.

FileMaker Data API

Figure 1. FileMaker Data API under Web Publishing (Click to enlarge.)

Its description explains that web services can use it to access FileMaker data. The truth is - it’s even better than that. It’s open to any kind of integration, not just web services. Any application or system that can make an HTTP call and works with received JSON data in response can use FileMaker Data API. It’s a very powerful and welcome addition to the FileMaker product line. It essentially turns your FileMaker app into a web service.

How to Use FileMaker Data API 

You can reach the web service (your FileMaker app) through a URL endpoint that looks something like this: https://{{server}}/fmi/rest/api/record/the_UI/FRUIT_utility/1 (We’ll explain the structure of this URL later.)

Then use one of the HTTP methods (verbs) to indicate what your desired action is (GET to retrieve data, POST to create records, PUT to update records, DELETE to delete).

The next part of the request is one or more headers. The most common header here is the authentication token to prove that you are an authorized user.

And finally, a JSON-formatted body contains the data you want to send to your FileMaker app. The web service then replies with a response in the JSON format.

Online help for FileMaker Data API is available on the machine where FileMaker Server is installed, at the URL shown below. This detailed documentation provides good examples of what to send and what you receive in return.

https://{{server}}/fmi/rest/apidoc/

FileMaker Data API - Get Record

Figure 2. FileMaker Data API - Get Record (Click to enlarge.)

Example 1: Authentication

Let’s get practical. For this post, I've created a collection of Postman test requests. Postman is a free testing tool our development team uses quite often. It allows you to construct API calls and see what you get back from the web service.

The Postman test requests have been saved as a "Postman collection," which you can download below. You can import this Postman collection once you have the tool installed. Each test demonstrates the various actions that the FileMaker Data API allows.

FileMaker Data API - Postman Test

Figure 3. FileMaker Data API - Postman Test (Click to enlarge.)

FileMaker Data API - Test Files

Figure 4. FileMaker Data API - Test Files (Click to enlarge.)

Download the Demo Files

In addition to the Postman collection, we've created two FileMaker files we used for these tests: the UI and the Data. Both have been enabled for the new FileMaker Data API.

Test Requests in FileMaker Data API

Let’s look at three of the test requests and see how they work. First, you need to do is authenticate; i.e. log in.  You then receive a token allowing you to interact with the data. The token is valid for 15 minutes after your last request. It needs to be added as a header to each HTTP request.

The documentation shows that we need to use this endpoint (URL) syntax and that it requires the HTTP POST method.

FileMaker Data API - HTTPS Post Method

Figure 5. FileMaker Data API - HTTPS Post Method

All requests to the FileMaker Data API will use '/fmi/rest/api/".  Next the API calls on 'auth' for logins and logouts, ‘record’ for all data manipulations, ‘find’ for searches, and ‘global’ for setting global fields.

Wherever the API documentation mentions ':solution', use the name of the FileMaker file you want to target.  Use just the file name, no leading ':', no '.fmp12' extension.

Where the API documentation uses ':layout', just use the target layout name, sans the ':'. Using web compatible file and layout names makes your life much easier. Don’t forget to avoid spaces and special characters.

In our Postman request, we used this process, with the name of the FileMaker file ("the UI") that we want to work with.

FileMaker Data API - Postman Request

Figure 6. FileMaker Data API - Postman Request

In Postman, the "{{server}}" is a placeholder set in the Postman ‘environment’ section. I can easily change servers without having to update each individual request. I use a similar placeholder for the token once we have it.

FileMaker Data API - Manage Environment

Figure 7. FileMaker Data API - Manage Environment (Click to enlarge.)

That takes care of the endpoint. The documentation also tells us what headers we must specify for the request: 'Content-Type' is required and the 'X-FM-Data-Login-Type' is only needed if we want to use OAuth to log in.

FileMaker Data API - Content Type Specification

Figure 8. FileMaker Data API - Content Type Specification (Click to enlarge.)

We are going to use a normal FileMaker account to log in, so we’ll skip that optional header and just set the content type:

FileMaker Data API - Content Type

Figure 9. FileMaker Data API - Content Type (Click to enlarge.)

The last thing we need to do is to set the body. The documentation explains what needs to go in the body. It also provides an example of what the whole thing should look like:

FileMaker Data API - Body Parameters

Figure 10. FileMaker Data API - Body Parameters (Click to enlarge.)

FileMaker Data API - Postman Test - Get Layout

Figure 11. FileMaker Data API - Postman Test - Get Layout (Click to enlarge.)

Then use Postman to construct the piece of JSON to send over. In that JSON we add the username and the password to use for the login, but we also need to add the name of a layout.  The layout is a bit of an odd duck in this request; it can obviously be the layout that you will want to use for the actual data interaction but at a minimum, the provided user credentials must have access to that specified layout or the authentication will fail. Here’s our request in Postman:

FileMaker Data API - Postman Request

Figure 12. FileMaker Data API - Postman Request (Click to enlarge.)

We are now ready to send this request over to FileMaker. When we have everything right, we will get a JSON response that contains the token:

FileMaker Data API - Postman Request

Figure 13. FileMaker Data API - JSON Response (Click to enlarge.)

If the login fails, the return response would be:

FileMaker Data API - Login Fail Response

Figure 14. FileMaker Data API - Login Fail Response (Click to enlarge.)

After a successful login, you can copy the token to the {{token}} environment placeholder. This makes it available to all subsequent requests.

Example 2: Retrieve Data

Now that we have a valid token, let’s use it to retrieve some data. In fact, let’s ask for all 10,000 records in the FRUIT table. We will use the ‘FRUIT_utility’ layout for this as shown in the screenshot below. It has a nice mix of different field types on it:

FileMaker Data API - FRUIT Utility Layout

Figure 15. FileMaker Data API - FRUIT Utility Layout (Click to enlarge.)

As per the documentation, we need to create a GET request, specifying the FileMaker file (:solution) and the target layout (:layout). We need to specify the token in the header.

FileMaker Data API - Specify Token

Figure 16. FileMaker Data API - Specify Token (Click to enlarge.)

In Postman, it looks like what's shown below, where 'the_UI' is the file and 'FRUIT_utility' is the layout:

FileMaker Data API - Specify Token - Postman Test

Figure 17. FileMaker Data API - Specify Token - Postman Test (Click to enlarge.)

When we send the request, we receive a response with the FileMaker data in JSON format:

FileMaker Data API - Request Response

Figure 18. FileMaker Data API - Request Response (Click to enlarge.)

You might expect this to contain the data for all 10,000 records, but that is not the case. The Data API limits the returned found set to 100 by default.

Note that Postman also gives us feedback on the time it took for the Data API to respond and the size of the response: 78 milliseconds for 100 records.

FileMaker Data API - Response Status

Figure 19. FileMaker Data API - Response Status (Click to enlarge.)

If we really want all 10,000 records, we can modify the request to add the 'range' keyword and set it to the exact number of records. Or, if we are not sure of how many records there are, we can use a very large number.

FileMaker Data API - Record Numbers

Figure 20. FileMaker Data API - Record Numbers (Click to enlarge.)

As you can see, it now took just over a second and a half to get all 10,000 records. The total response size is just over 7MB.

One thing we should note about JSON data we receive from FileMaker: all data is returned as JSON text (everything is quoted), even numbers and dates. The system receiving the JSON will need to factor this in.

FileMaker Data API - JSON Data

Figure 21. FileMaker Data API - JSON Data (Click to enlarge.)

The test requests in the Postman collection will demonstrate some of the nuances in the JSON syntax for things like portal data, if you have an object name set for your portal, what repeating fields look like, and other variations.

Example 3: Modify Data

The last example shows the endpoint structure — headers and body to use when we want to edit a specific record. In this example, we want to change the value of the ‘fruit’ field on the record with record id 10007.

As per the documentation, we’ll need to use the PUT HTTP method, set two headers, and use a JSON body that includes the field that we want to update and the new value. The URL has to include the FileMaker file name (:solution), layout (:layout), and the record’s id (:recordId).

FileMaker Data API - PUT HTTP Method

Figure 22. FileMaker Data API - PUT HTTP Method (Click to enlarge.)

This raises a question: what do we use for the record id, and how do we know the value of the record id?  'recordId' is the internal id that FileMaker assigns to every record. It is not your own primary key or serial number. For the purpose of this demo, I have created a field in the FRUIT table that shows us the record id, so that we can actually see it on the layout:

FileMaker Data API - Record ID

Figure 23. FileMaker Data API - Record ID (Click to enlarge.)

Even if you do not have such a field, the record id is always part of the JSON that the Data API returns if you create a record or search for records. If you do a search through the Data API and then process that response, you will have the record id you need:

FileMaker Data API - Record Search

Figure 24. FileMaker Data API - Record Search (Click to enlarge.)

Back in Postman, our request looks like this with the URL (ending with the record id) and the headers:

FileMaker Data API - Postman Request

Figure 25. FileMaker Data API - Postman Request (Click to enlarge.)

In the body, we have some JSON that contains the name of the field that we want to update:

FileMaker Data API - Body JSON

Figure 26. FileMaker Data API - Body JSON (Click to enlarge.)

When the request is sent, the Data API response indicates all is well:

FileMaker Data API - Response

Figure 27. FileMaker Data API - Response (Click to enlarge.)

Or, if we messed up and specified a field name that does not exist, we will get this back:

FileMaker Data API - Error Response

Figure 28. FileMaker Data API - Error Response (Click to enlarge.)

These examples are basic. The JSON payload (body) gets more complex when you do searches and other requests, but I hope this introduction was helpful to get you going with the new FileMaker Data API.

How does FileMaker Data API stack up against other APIs?

We still have the traditional APIs at our disposal (ODBC/JDBC, XML and PHP). The most straightforward comparison is with the XML API. They both work off request URLs, they return data in a particular format, and they do not require any drivers or other setup, like ODBC does for example.

The Postman collection includes one XML test that also asks for all 10,000 records from the same table, through the same layout. In our testing, we have found the Data API to be consistently faster than the XML API. Below is a screenshot showing that it took over 3 seconds to retrieve the data through the XML API. The Data API took just over 1.6 seconds.

FileMaker Data API - Data Retrieval Time

Figure 29. FileMaker Data API - Data Retrieval Time (Click to enlarge.)

The Future with FileMaker Data API

On the FileMaker Server admin console, it states that the Data API is a trial that expires at the end of September next year (2018). Given that FileMaker Inc. releases a new version every year around May, we expect the Data API to be officially released with FileMaker 17 well before this trial expires.

We do expect that at some point the support for the XML and PHP APIs will be deprecated and that all development focus will go to this new FileMaker Data API. Keep an eye on the FileMaker roadmap for this.

Even when they eventually get marked as deprecated, XML and PHP APIs will clearly be around for a long time. There’s no need to rework any existing integrations you have that use those APIs. However, consider the new Data API for any new and upcoming work


We have one caveat: it is unclear at this point if the use of the new FileMaker Data API – once officially released – will require some sort of connection-pack licensing. We will keep our ear to the ground on this.

There are definitely some downsides to the Data API. It does not have the ability to execute scripts, a capability both XML and PHP APIs boast. It also requires using the internal record id of a record to identify the record.  That record id is not as set in stone as it needs to be to permanently point to the same record.  For example, if you delete the record from the FileMaker file and re-import the same record from a backup, the record id will be different. Your primary key will still be the same, but the internal record id will change. Any REST url pointing to this record through its original record id will break.

Our FileMaker Data API Recommendation:

Get familiar with this new feature. It is an exciting and promising addition to FileMaker developers’ integration arsenal. However, be careful when committing to it for production projects. The API will likely undergo tweaks before its official release. The licensing pricing model is unknown at this point too.

Have questions about FileMaker Data API capabilities? Let us know in a comment below.

The post FileMaker Server 16: FileMaker Data API appeared first on Soliant Consulting.


Afficher la totalité du billet

Soliant Consulting

FileMaker Server 16: FileMaker Data API DemoOne of our favorite features of FileMaker Server 16 is the brand-new FileMaker Data API. We've explained how it works in a blog post here.

Now you can explore the functionality yourself by downloading our demo files.

We've created two FileMaker files: the UI and the Data. Both have been enabled for the new FileMaker Data API. (This download includes the Postman collection, FileMaker UI, and the Data referenced in the preceding blog post.)

Complete the form to receive the demo files:


Trouble with this form? Click here.

The post FileMaker Server 16: FileMaker Data API Demo appeared first on Soliant Consulting.


Afficher la totalité du billet

Soliant Consulting

The FileMaker 16 platform includes FileMaker Server and introduces the ability to have multiple "worker" machines connected to a single FileMaker database server or "master" machine. This is mostly relevant to FileMaker WebDirect, which has greatly improved with the latest version.

This involves an understanding of the different services running as part of FileMaker Server, only one of which is the database server itself. There are also web publishing and scripting engines, among other services. Most important here is the Web Publishing Engine (WPE). That’s where WebDirect sessions are run from. FileMaker 16 introduces some changes to how WPE is managed in the FileMaker Admin Console.

Worker 1

FileMaker Server 16 represents the Web Publishing Engine a little differently. If you have enabled Web Publishing at all in the Deployment Wizard, you will now see the first "worker" machine attached. This represents a one-machine deployment. Previous versions allowed for a two-machine deployment you specified as part of the Deployment Wizard.

With FileMaker 16, the server machine is labeled as Worker 1 in your admin console. A two-machine deployment will show two worker machines, the first one being from the main server machine.

As the screenshot shows, there is no option to remove Worker 1, as it is part of the main server deployment.

WebDirect in FileMaker 16

Figure 1. Worker Machine 1 Options (click image to enlarge)

500 Maximum Concurrent Connections

The Web Publishing Engine in FileMaker Server 16 is now much more capable, judging from the new server specifications. Previous versions supported multiple WebDirect sessions on a single machine. Unfortunately, if you needed more than 7 or 8, a two-machine deployment was required. Now, a single machine deployment supports up to 100 concurrent WebDirect Sessions.

Of course, that means your server will need to be provisioned well enough to handle the load. Make sure to give your server enough resources to handle anticipated usage. This is a big deal, because a WebDirect session is roughly equivalent to a FileMaker Pro or Go client. Once a user logs in via WebDirect, a session must maintain all global field contents, local and global variables, and other information about the user session until the user has either logged out or their session times out, effectively disconnecting them.

The new supported number of maximum concurrent WebDirect connections is 500, with 100 users per worker machine in the deployment. That makes a total of five well-provisioned machines (the main server hosting worker 1, plus 4 additional worker machines) to handle 500 concurrent WebDirect users at the same time, accessing a hosted FileMaker solution.

Adding a Worker

The process for adding a worker machine is now different as well. As with earlier versions, you specify that the machine is a worker while installing FileMaker Server on that machine. In FileMaker 16, a deployment web page attaches the worker machine to the master machine. Each worker requires its own fully qualified domain name or IP address, including SSL certificate if appropriate.

Once the worker machines connect, the master machine routes WebDirect sessions to them to balance the load. You will see the redirected URL in the location of your browser. FileMaker will route requests to the connected worker machines as much as possible, keeping as much of the main server available for handling all other services running on that machine.

Once a user routes to a secondary worker machine, he stays connected to that worker. He will not re-route to another one for the remainder of his WebDirect session. This is true for any parameters included in the URL string. You can still reference the "homeurl" parameter to redirect to when the user logs out, or pass in additional parameters like you were able to in previous versions.

Balancing the Load

You should always have users log into the main machine, worker 1, and then let FileMaker Server route them to the worker with the smallest load.

FileMaker Server performs its load balancing based on available resources of the worker machines and of course, perhaps the most important metric of all, user count per machine. The worker hosting the fewest users will typically get the next incoming user routed to it.

Using WebDirect in Business Applications

Considering its continued improvements, WebDirect serves as a strong contender for your next custom web-enabled business application. There may not be a better alternative for quickly building a feature-filled, robust web application providing such extensive customization options.

If you’d like to learn more about how WebDirect can enhance your business, contact our team today. We’re happy to discuss potential solutions with you.

References

The post FileMaker Server 16: Multiple Worker Machines appeared first on Soliant Consulting.


Afficher la totalité du billet

Soliant Consulting

The newly released FileMaker 16 product line introduces interesting new security features and adds three new External Authentication providers through OAuth: Amazon, Google and Microsoft Azure.

Read all about them in two white papers co-authored with Steven H. Blackwell:

At this year's DevCon, I will present an in-depth look at those new OAuth providers and show you how to set them up and use them.

See you there!  In the meantime, contact us if you have any questions about these new features.

The post FileMaker 16 – OAuth External Authentication and more security features appeared first on Soliant Consulting.


Afficher la totalité du billet

Soliant Consulting

As with past releases of the product, FileMaker, Inc. invited Soliant's team to be a part of the initial testing of FileMaker 16. We've spent the past months working with this new version and we're happy to help familiarize you with its features.

FileMaker 16 Oveview

May 9, 2017 - FileMaker 16 is here, and its new features are game-changers for developers. For example, using its functionality will make custom applications more responsive and secure. You can now connect with other services, such as Weather.com, to integrate data from external data sources, but that's just the tip of the surface. In this video, we introduce the top three new functionalities: Card Windows, creating and parsing JSON, and the Layout Objects Inspector.

An Introduction to Card Windows

May 9, 2017 - The new Card Window feature in FileMaker 16 allows you to create overlays within the same window. The card can have its own context different from the background layout. This is going to be a game-changer in how FileMaker solutions are designed and architected.

The post FileMaker 16 Playlist appeared first on Soliant Consulting.


Afficher la totalité du billet

Soliant Consulting

FileMaker 16 is here, and its new features are game-changers for developers. For example, using its functionality will make custom applications more responsive and secure. You can now connect with other services, such as Weather.com, to integrate data from external data sources, but that's just the tip of the surface. In this video, we introduce the top three new functionalities: Card Windows, creating and parsing JSON, and the Layout Objects Inspector.

The post FileMaker 16 Overview appeared first on Soliant Consulting.


Afficher la totalité du billet

Soliant Consulting

Every one of us FileMaker developers comes from a different background. Some of us are graphic designers, web developers, coders, teachers, or mathematicians, and we expect FileMaker to work like other software. For example, if your focus is graphic design, every year when the new version is released, you probably hope for more design tools.

Well, wait no more. FileMaker, Inc. released FileMaker 16 today along with a ton of new features. In this article, we will concentrate on the new Layout Objects Inspector.

Locating and Identifying Objects

If you work like I do, you try to keep your layout as close to minimalistic as possible. Then your client says, “Can you add one more field?” on a weekly basis. At some point, you will end up with a “colorful collection of crayons” all over the place (and you will have no kids to clean that up). At home, you can use bins and put labels on the side so you can identify the contents. And the crayons are color-coded themselves, so you can easily pick whichever one you need.

At home, you can use bins and put labels on the side so you can identify the contents. And the crayons are color-coded themselves, so you can easily pick whichever one you need. In FileMaker, we also try to follow the same method by putting objects in the appropriate buckets and using layout tools such as tabs, sliders, and popovers. This contains the madness and allows data to be shown to the user on a need-to-know basis.

We can also act on our objects. We can:

  • Color objects
  • Go to an object (via script step)
  • Apply conditional formatting
  • Hide objects
  • Use the Get (Layout Object Attribute) function to learn about our objects (this is a very useful function)
  • Set Layout Object Animation
  • Use the LayoutObjectNames function to get all the named objects on the
  • Use triggers on them

No matter what tools FileMaker, Inc. gives us, we always want more. And with the yearly release cycle, we have a chance to get new tools every year.

New Tool in FileMaker 16: Layout Objects Inspector

One thing I always wished for is the ability to identify which object is behind something or hidden tab panels. Hidden tab panels used to be my favorite feature until you could actually hide elements. You had to note via yellow “stickies” on the layout if you had hidden objects, usually in the non-visible area.

Now you can easily find them.

We have a new panel called, Layout Objects Inspector as shown in Figure 1.

You can pop it open/hide it by hitting CMD + Option + T. It will also hide with Escape (just like the Inspector palette).

From the Layout Objects Inspector, you can do the following:

  • name objects (it uses regular object naming)
  • select multiple objects at once
  • view objects by type
  • group multiple selected objects, name the group (call it a layer if you wish); drag to rearrange them as layers
  • hide them / expose them (it grays them out if hidden)
  • hide all non-selected objects
  • show all hidden objects
  • find an object
  • modify object while being grouped
  • see if an item is from a related table (::)
  • add conditional formatting
  • add triggers


Layout Objects Inspector in FileMaker 16

Figure 1. Layout Objects Inspector (click to enlarge)

Notes:

  • Adding an item to the group creates a parent group with all objects in it.
  • Un-grouping a group will make the group name disappear.
  • Just because you can name every object doesn’t mean you should. Generally, in FileMaker we name objects we want to navigate to manually or in a script.
  • You can view but not move an object when exposed by clicking on it in the Layout Objects Inspector. You have to click on the actual item to be able to move it.

Below is another example of the Layout Objects Inspector - one compact (Figure 2) and one expanded (Figure 3) view.

Compact view of the Layout Objects Inspector in FileMaker 16

Figure 2. Compact view of the Layout Objects Inspector

Expanded view of the Layout Objects Inspector in FileMaker 16

Figure 3. Expanded view of the Layout Objects Inspector

In Figures 2 and 3, you can see groups. One of them I called layer (just because I can), which can be confusing if you ungroup it. As you can see, the Slide Control automatically created a group for itself, and each slide is an object in that group. I love this feature because, now I don’t have to expose the dots to go to the next slide panel.

The portal contains an edit box, and you can navigate to it from the Layout Object Inspector, as well.

I hope this new tool will save you time and help you get more organized in your FileMaker development. If you have any questions, feel free to write a comment or send us a note.

Happy FileMaker Developing!

The post New Layout Objects Inspector in FileMaker 16 appeared first on Soliant Consulting.


Afficher la totalité du billet

Soliant Consulting

SortValues and UniqueValues functions in FileMaker 16 demoLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Complete the form to receive the demo:

The post SortValues & UniqueValues in FileMaker 16 Demo appeared first on Soliant Consulting.


Afficher la totalité du billet

Soliant Consulting

FileMaker 16 Create JSON Demo

The release of FileMaker 16 includes an interesting new function used to create a JSON object called JSONSetElement.

You can see how it works here. Download the simple sample file to create one single JSON object with all relevant information, including key-value pairs, arrays, nested objects for the contact record, and more.

Complete the form to receive the demo:

The post Create JSON in FileMaker 16 Demo appeared first on Soliant Consulting.


Afficher la totalité du billet

Soliant Consulting

FileMaker 16 Card WindowsThe new Card Window feature in FileMaker 16 allows developers to create overlays within the same window. The card can have its own context different from the background layout. This is going to be a game-changer in how solutions are designed and architected.

Download our Card Window demo to see it in action.

Complete the form to receive the demo:

The post Card Windows in FileMaker 16 Demo appeared first on Soliant Consulting.


Afficher la totalité du billet

Soliant Consulting

As dependable as ever, FileMaker has delivered on its promise to release FileMaker 16 on schedule. As a Platinum Partner, the Soliant team got a sneak peek at its functionality and features, and we're really excited with what we've seen. The new features in FileMaker 16 are game-changers for developers. We can now make custom applications more responsive and more secure. We can connect with other services, such as Weather.com, to integrate data from external data sources.

And, of course, the more capabilities your developers have, the more powerful and robust your FileMaker solution becomes. Based on what we've seen in FileMaker 16, your application has many interesting enhancement opportunities ahead. FileMaker's roadmap gives a few hints, of course, but exploring the new version first-hand and in detail has been nothing short of amazing. We know the FileMaker community is going to love it.

With the rollout of the new version every year, the Soliant Consulting FileMaker team shares our thoughts on new features as well as tips for developers on how to best leverage them. Over the next few weeks, we'll be sharing our insights in the form of blog posts, Soliant.tv videos, and downloadable resources. In fact, you can secure your copy of the FileMaker 16 Executive Summary written by Wim Decorte, Senior Technical Architect.

FileMaker 16 Top Added Features

Here are, in our opinion, the top three new functionalities:

1. Card Windows

This new incredible feature, also known as overlays, allows developers to create a more app-like file by providing a new modal window of a completely different context in FileMaker. We think this new functionality will have an immediate impact on architecture and design for FileMaker solutions. Martha Zink, Bill Heizer, and I will be covering this feature in depth. Watch the video...

2. Layout Object Inspector Palette

Layout mode has gotten a big revamp to increase developers' productivity. Designing a good custom app requires many objects to be placed on a layout, and this object inspector gives us better access to each one of the objects, allowing developers to work with anything on the layout more efficiently. They can now quickly filter, find, hide, and manipulate various objects in Layout mode. Agnes Riley will dive into this new developer tool that will save us a lot of time. Read the blog post...

3. Added JSON Support

JSON functionality is a major addition within FileMaker 16. Anders Monson and others have many insights to share on this new feature. Developers can natively and easily parse JSON from web services. They can also use the format to perform many database tasks.

And more!

FileMaker 16 also delivers new iOS transition animations, script workspace improvements, security enhancements, and more.

Get a more in-depth look at these new features by downloading Wim Decourte's FileMaker 16 Executive Summary.

And make sure to keep an eye out for our blog posts released over the next few weeks, which will also be explaining how each new feature affects your FileMaker solution. Our FileMaker development team will also be sharing tips on how to leverage each new functionality to enhance your solution. You can find all of these posts here, so make sure to bookmark this page!

Moving Forward with FileMaker 16

All of us at Soliant are excited about how FileMaker is evolving to provide even more features for companies and their development teams. We hope you love FileMaker 16 as much as we do. If you have any questions on specific functionality are interested in learning more about how to implement its new features, feel free to contact us directly.

FileMaker 16 Resources

The post What You Need to Know About FileMaker 16: An Introduction to Its Best Features appeared first on Soliant Consulting.


Afficher la totalité du billet

Soliant Consulting

JSON (JavaScript Object Notation) is an open standard format to send data that consists of an object with name-value pairs. JSON has its origins with JavaScript, but as many other programming languages have adopted it as an alternate to XML, since JSON is far less verbose than XML. Many web services return data in JSON or XML, and sometimes both are offered as options. FileMaker 16 introduces native functions to create and extract data from JSON, making interactions with web services easier and more streamlined. This article focuses on how to create JSON objects from within FileMaker 16 with new, native functions.

JSON Structure

JSON objects begin and end with braces - { } and may contain arrays, other JSON objects, strings, numbers, null values, and Boolean values. Although JSON objects can contain one single element, the power of JSON is that it allows for nested arrays and objects. It’s important to note that the keys are case sensitive, so “firstname” and “FirstName” aren’t seen as identical. However, the order of the JSON keys, and any whitespace between text, does not affect how systems read the JSON object.

FileMaker 16 Function

The FileMaker 16 function used to create a JSON object is called JSONSetElement.


JSONSetElement ( json </em><em>; keyOrIndexOrPath ; value ; type </em><em>)

There are four required parameters.

  1. The "json" parameter references an existing JSON object ( e.g. script variable ), or it initializes a new JSON object.
  2. The "keyOrIndexOrPath" identifies the JSON element, which can take many forms. For example, if you are adding a key-value pair like "firstName" (key) and "John" (value) to the JSON, then "firstName" would be used for this parameter. In addition, this key can consist of other elements, all covered in examples below.
  3. The "value" parameter refers to the content of the element. This might be a string of text, such as "John" if the key is "firstName." However, it also could be an array containing multiple values, such as "[1,2,3]" or even another JSON object. This is covered in more details in the examples below.
  4. Finally, the type parameter identifies the key-value pair as either string, number, etc. The type is specified with a number, with 0-6 corresponding to raw, string, number, object, array, Boolean, null. (Instead of remembering which number corresponds to which datatype, creating a set of simple custom functions with a name such as json_string, json_number, etc. with just the corresponding number, might be a useful action if you have FileMaker Pro Advanced. There are at the moment hidden functions that work the same way, such as JSONString, JSONArray and so on, but as these are undocumented, there is a possibly these may be removed or renamed in the future).

Examples

The examples listed below are taken from a demo file. Some examples appear in records, so that you can test different values and see these create JSON in real time. Other examples are illustrated using scripting techniques. This screenshot illustrates the record example, which provides instant feedback.

json in filemaker 16

Figure 1. Record example (click to enlarge)

There are many ways to accomplish similar goals in FileMaker. Being able to step through a script a see how the JSON is created in smaller increments is something I see a good starting point for anyone new to this type of data format, especially when constructing complex objects.

creating objects json filemaker 16

Figure 2. Stepping through a script (click to enlarge)

Simple JSON elements

Setting a key-value pair with a string, number, Boolean, or null value is the simplest JSON one can create the following combinations For example:

  • JSONSetElement ( "" ; "firstName" ; "John" ; 1 ) à { "nameFirst":"John" }
  • JSONSetElement ( "" ; "age” ; 35 ; 2 ) à { "age": 35 }
  • JSONSetElement ( "" ; "active” ; false ; 5 ) à { "active": false }*
  • JSONSetElement ( "" ; "resident” ; “something or nothing” ; 6 ) à { "resident": null }**

*Boolean types don’t require quotes. If you use “false” this is considered a string.

**Null data types ignore any values in the third parameter.

Multiple sets

It’s also possible to pass multiple sets of “keyOrIndexOrPath ; value ; type” input in one function call, using the square brackets, much like the Substitute function.


JSONSetElement( "{}" ; 
  [ "firstName" ; "John" ; 1 ] ; 
  [ "lastName"; "Doe" ; 1 ]  
)

The following examples are illustrated on the Contacts layout in the demo file.

Arrays

A JSON array is a collection of values enclosed within square brackets and separated by commas. When present in a JSON object, arrays still have a key, such as “phoneNumbers”, but are extracted using the index, or order within the array. Arrays are zero based, so the first index value is 0, the second 1, and so on. For example, this simple array uses the key of “random” for both values in the array, but to get the first value you add the index – random[0].


{ "random" : [  27, 69 ] }

Creating an array, therefore, means you populate it by specifying the index number.

To declare an array in JSONSetElement you need the "[]" which the actual JSONSetElement refers to when adding data. Below is a loop in a script to populate an array. The $counter starts at zero as array indexes have zero as the first value. Each element in the array matches a numerical index, from 0 to n.


Set Variable [$json ; "[]" ]
Set Variable [$counter ; 0 ]
Loop
  Exit Loop If [$counter > 2 ]
  Set Variable [ $json; Value: JSONSetElement( $json ; $counter ; Int( Random * 100) ; 2 ) ]
  Set Variable[ $counter; Value: $counter + 1 ]
End Loop

Result:
[ 27, 69 ] 

This array is now ready to be added to a JSON object.


JSONSetElement( "" ; "random" ; $json ; 4 )

The "4" here is the data type, which corresponds to an array.

Result:


{ "random" : [ 27, 69 ] }

This simple array is now ready to be understood by any program that interprets JSON.

From an example with random numbers we move to actual data. In a contact record with phone numbers, the array might end up looking like this:


{ "phoneNumbers" :
  [
	{
		"number" : "907-345-0962",
		"type" : "Phone"
	},
	{
		"number" : "907-345-1215",
		"type" : "Fax"
	}
  ]
}

In this case the array elements are objects, since there are multiple items, like number and type. The same principle applies, although we would first contrast the number and type object, insert this into an array, and then create the JSON with the phoneNumbers object. For the data parameter, instead of using a number, I’m relying on a custom function that makes it more obvious as to the data type, such as JSON_string vs. JSON_object.


Set Variable [$json ; “[]” ]
Go to Object [ObjectName: “portalPhones” ]
Go to Portal Row [Select: On; First ]
Set Variable [$counter ; 0 ]
Loop
	Set Variable [ $json; Value: 
	     Let([
		~json = JSONSetElement ( "" ; 
		["type";Phones::Type; JSON_string ];
		["number"; Phones::PhoneNumber; JSON_string] 
		)
	     ];

	     JSONSetElement( $json ; $counter ; ~json ; JSON_object ) 
)]	
  	Set Variable[ $counter; Value: $counter + 1 ]
  	Go to Portal Row [ Select: On ; Next ; Exit after last: On ]
End Loop

Note the "~json" variable inside the Let statement uses square brackets to feed multiple sets of keyOrIndexOrPath, value, and type parameters in a single JSONSetElement function call.

Nested Objects

Like arrays, objects can store multiple values. Whereas arrays are grouped inside the square brackets like peas in a pod, objects are strung together, but still in a sense separate. Not to cloud an existing FileMaker concept, but objects are more like anchors with buoys strung along, or a fishing line with multiple hooks; they all relate back to the main key.

Objects can be nested within the main JSON object. The root, or main, object doesn’t have a name (or key), but all other objects are nested within the main object and are linked to a name (or key), such as “address” in these examples.

This process is far simpler than arrays, at least for something like the address. Instead of a loop, we construct the content of the object first, and then link this to the parent object.

# Build the content


Set Variable [ $json_address ; Value: JSONSetElement ( "{}" ; 
   [ "Street" ; Contacts::Street ; 1 ] ;
   [ "City" ; Contacts::City ; 1 ] ;
   [ "State" ; Contacts::State ; 1 ] ;
   [ "Zip" ; Contacts::ZIP ; 1 ] 
 )
]

# Create the parent with content as the value


Set Variable [ $JSON ; Value: JSONSetElement ( "{}" ; "address" ; $json_address ; JSON_object ) ]

Result:


{
     "address" : 
     {
	"City" : "Anchorage",
	"State" : "AK",
	"Street" : "14225 Hancock Dr",
	"Zip" : "99515"
     }
}

Gathering it all together

Now that you have key-value pairs, arrays, and nested objects for the contact record, creating one single JSON object with all this information is just a matter of putting together all the elements into one JSON object as can be seen in the demo file's "Create JSON Contact Info" script.

JSON in FileMaker 16, in Summary

There are both external and internal uses for JSON.

Knowing how to create JSON is a requirement for using many web services. Combined with cURL features in the Insert from URL script step, you can now send JSON built from FileMaker records.

Within FileMaker, JSON could be used to handle multiple script parameters, store users preferences in a single object instead of across multiple fields, gather changes in an audit log, and even duplicate records including data in portals. Imagine the possibilities.

Get the Demo File

The post Creating JSON in FileMaker 16 appeared first on Soliant Consulting.


Afficher la totalité du billet

Soliant Consulting

JSON (JavaScript Object Notation) is an open standard format to send data that consists of an object with name-value pairs. JSON derives from JavaScript, but as many other programming languages have adopted it as an alternative to XML, as JSON is far less verbose than XML. Many web services return data in JSON or XML, and sometimes both. FileMaker's Insert from URL script connects to web services and retrieves data such as XML and JSON. Until FileMaker 16, parsing JSON required functions that manipulated text to extract data.

FileMaker 16 introduces native functions to create and extract data from JSON, making interactions with web services easier and more streamlined. This article focuses on how to extract values from JSON.

JSON structure

JSON objects begin and end with braces - { } - containing sets of key-value pairs. Each key is followed by a colon and each key-value pair is separated by a comma.


{ key1 : value1 , key2 : value2 }

Arrays are collections of values. These begin and end with square brackets - [ ]. Values within arrays are separated by commas. The values inside arrays are indexed numerically, starting with zero (0). The array in the example below has an index consisting of 0, 1,  and 2.


[ value1 , value2 , value3 ]

Data types

Values can consist of strings, numbers, Boolean( true/false), or null, as well as objects and arrays. This means that a JSON object can contain data nested several layers deep within arrays and objects, creating a pathway to specific values by following a pathway of keys down to the value associated with the key in that path.

Whitespace

JSON ignores whitespace that appears between key-value pairs, which means tabs, spaces, and returns can be inserted to make the JSON more human readable.

A large block of JSON can be difficult for humans to read, especially when it comes to discerning arrays, objects, and paths. In my examples above I've rendered the JSON as "human-readable" with indents and spaced.

In order to extract data from JSON we need to know the path and key names, and being able to see the structure is crucial to seeing the pathway to the elements you want to extract. Although it's possible to build scripts that loop through JSON, lists the keys, then extracts the values based on those keys without looking at the JSON, I firmly believe you can't rely on trusting the data without eye-balling it to understand its structure. Web services construct JSON in interesting ways, from simple to complex.

To that end, the function called JSONFormatElements( JSON ) re-structures the JSON code to make it more readable. Note: This function also serves as a way to confirm that the JSON data you are working with is valid. Invalid JSON would return a “?” followed by some information about the error. For example, an object where the [ ] are not paired would return something like:


? * Line 1, Column 186
  Missing ',' or ']' in array declaration
If the { } braces are not paired, the error would look like this:
? * Line 1, Column 185
  Missing ',' or '}' in object declaration

Outside FileMaker, there are several websites that serve the same function. Some examples:

FileMaker 16 Function

The FileMaker 16 function used to extract data from JSON is called JSONGetElement.


JSONGetElement ( json ; keyOrIndexOrPath )

There are two required parameters. The first is the JSON itself, usually a variable or a field. The second is the more interesting one, as there are three possible options: Key, Index, or Path. The options will depend on the type of the element, such as string, array, number, object, etc.

Example

Let's begin with some actual JSON code. Below is a JSON object that store contact information: name, address, and a pair of phone numbers. Important: JSONGetElement is case sensitive, so firstName is different from firstname or FirstName.


{
  "firstName": "John",
  "lastName" : "doe",
  "age"      : 26,
  "address"  : {
    "streetAddress": "123 Main street",
    "city"         : "Anytown",
    "postalCode"   : "12345"
  },
  "phoneNumbers": [
    {
      "type"  : "iPhone",
      "number": "123-456-8888"
    },
    {
      "type"  : "home",
      "number": "123-557-8910"
    }
  ]
}

This JSON object contains a mixture of objects, arrays, and key-value pairs. For example, the "firstName" is a key, with "John" being the matched value. The value for the "address" key is an object denoted by the { } braces, containing three key-value pairs: streetAddress, city, postalcode. The value for the "phoneNumbers" key is an array denoted by the [ ] brackets, containing two sets of key-value pairs, where type  and number are the keys.

In order to extract certain elements, the keyOrIndexOrPath parameter will look and behave differently based on where that element exists inside the main JSON object.

To extract the firstName value, the function is simple:


JSONGetElement( JSON ; "firstName" )

This returns “John” as the value.

However, using this exact same function with streetAddress as the parameter returns an empty value, as this key is nested inside the "address" object. This extends the path we need to traverse, so in order to extract any of the values inside the "address" object we need to add this to the path. Each branch in the path is separated by a period. There’s no need to declare the root, or top, path even though it’s possible by simply prefacing the key, such as ".firstName"

So, in order to get the "streetAddress," we add "address" plus a period in front of this key.


JSONGetElement( JSON ; "address.streetAddress" )

If the address object had additional objects nested inside, the path would be constructed with the object names "separated.with.periods.etc" where the last portion of the string denotes the key for the element being extracted.

Arrays present different challenges. Arrays are denoted by [ ] brackets. There’s no key, and instead, we need to use the numerical index in our JSONGetElement somehow.

We’ll start by extracting out the array itself.

JSONGetElement( JSON ; "phoneNumbers" ) returns the entire array, and we see there JSON objects inside the array, unlike our simple example above with [ value1, value2, value3 ]:


[
	{
		"number" : "123-456-8888",
		"type" : "iPhone"
	},
	{
		"number" : "123-557-8910",
		"type" : "home"
	}
]

Since there are two groups of key-value pairs, to extract just the first one means we have to use the index of the array (remember the key, or index, or path from the parameter). JSON arrays are zero-based, so the first array has an index of zero (0).

JSONGetElement( JSON ; "phoneNumbers[0]" ) returns:


{"number":"123-456-8888","type":"iPhone"}

Now we have a set of name value pairs. To get the number we need to add its key to the path:


JSONGetElement( JSON ; "phoneNumbers[0].number" )

This would result in 123-456-8888. To get the second number you use [1] in the path instead of [0].

JSON objects for different people might contain a variable number of phone numbers. There's no way to simply extract a list of the "numbers" values from the "phoneNumbers" array. Instead, you would need to loop through the data using a script or recursive custom function.

There are two functions that let you count the number of values in an array: JSONListKeys and JSONListValues.

JSONListKeys( JSON ; "phoneNumbers" ) returns the keys, starting with 0. In the case of the example above it would be a return-delimited list of "0¶1" – note that, unlike other FileMaker list functions, a trailing carriage return will not be appended to the result.

JSONListValues( JSON ; "phoneNumbers" ) returns the actual objects with the key-value pairs:


{"number":"123-456-8888","type":"iPhone"}
{"number":"123-557-8910","type":"home"}

Since strings can contain carriage returns, one way to how many elements there are inside an array is to use ValueCount( JSONListKeys( JSON; “keyOrIndexOrPath” ). This returns two(2) in our example with the phone numbers. In a scripted loop you set a variable to the iteration, i.e. $i, and you extract your data as follows:


JSONGetElement( JSON ; "phoneNumbers[" &amp; $i &amp; "].number" )

Again, since indexes are zero based, the counter for the loop would start at zero, not one.

Parse JSON in FileMaker 16, in Summary

Knowing how to parse JSON first begins with some JSON object. If you’re currently pulling down data from a web service in JSON into a FileMaker 16 file, you can now take advantage of native functions. The way to start is to take the JSON object, look for arrays, nested objects, and key-value pairs. Identify the element you want to extract, determine the path, then test the expression to make sure you get the value that matches what’s in the JSON. Along with this post I’ve added a simple sample file where you can paste in JSON objects and use JSONGetElement on those objects to extract all the data types available in JSON.

Get the Demo File

The post Parsing JSON in FileMaker 16 appeared first on Soliant Consulting.


Afficher la totalité du billet

Soliant Consulting

Discover FileMaker 16

FileMaker 16 Executive Summary

As always, many of us at Soliant Consulting have been involved in early beta testing programs and have had time to explore FileMaker 16 extensively. We have prepared an executive summary to share our thoughts on features of interest and how to leverage them best.

This summary is a quick, top level review that touches on the new features of the platform and addresses the first questions one might have when considering an upgrade.

Please fill out the form to receive a copy of the FileMaker 16 Executive Summary.

Complete the form to receive the executive summary:


Trouble with this form? Click here.

The post FileMaker 16 Executive Summary appeared first on Soliant Consulting.


Afficher la totalité du billet

Soliant Consulting
Salesforce Lightning needs a few more components – here’s a new drag and drop Multiselect component

Ok, I know I’ve blogged about components a lot recently… but they are pretty fun to build, so… I made another one.

This one is like the two column select components of Salesforce Classic where you can drag items in an out of the “Selected” area. Except it’s better 😉

This component allows drag and drop in both directions, Shift-Selection of multiple items to push in either direction and easy up/down arrow rearranging of selected item order. I think you’ll like it.

For some of my other components, please look here:

This component relies heavily on the HTML 5 drag and drop API, which, while it has a few quirks, seems to work well. It’s useful to use it because it abstracts away some of the mouseX and mouseY location details that you’d normally have to worry about.

For more about this API look here.

The main structure of the component is really just two lists – one with the source items and one with the destination items.

Most of the functionality is centered around adding and removing items from these lists.
Since the Lightning ‘aura:iteration’ component can only take plain arrays, I opted to iterate on Arrays of objects, sorted by a ‘sort’ attribute.

I built up a low-level addition/removal/sort set of functions in the handler that may be useful in other apps.

List Handling Building Blocks


    //this inserts an item by either swapping or pushing up/down all other items
    insertItemAt : function (fromItem,toItem,items){
      var fromIndex = fromItem.sort;
      var toIndex = toItem.sort;
    
      if (fromIndex == toIndex){
        return items;
      }
      if (Math.abs(fromIndex-toIndex) == 1){  //just swap
        var temp = fromItem.sort;
        fromItem.sort = toItem.sort;
        toItem.sort = temp;
      }
      else if (fromIndex>toIndex){
        items.forEach( function(item){
          if (item.sort >= toIndex){
            item.sort++;
          }
        });
        fromItem.sort = toIndex;
      }
      else if (toIndex>fromIndex){
        items.forEach( function(item){
          if (item.sort <= toIndex && item.sort > fromIndex){
            item.sort--;
          }
        });
        fromItem.sort = toIndex;
      }
      return this.sortItems(items);
    },
    
    //this gets an item based on the supplied index
    getItem : function(indexVar,items) {
      var itemToReturn;
      items.forEach( function(item){
        if (item.sort == indexVar){
          itemToReturn = item;
        }
      });
      return itemToReturn;
    },
    
    //this function pushes an item to the source array, and removes it from the source
    moveItemTo : function(source,destination,item,addTo){
      item.type = addTo;
      item.style = '';
      //if we put back to the source, we'll grab this sort and reinstate it.
      if (addTo == 'destination'){
        item.sort = item.savedSort;
      }
      else {
        item.savedSort = item.sort;
      }
      source = this.removeItem(item.sort,source);
      destination.push(item);
    
      return item;
    },
    
    //this gets all items between a start and end index
    getItems : function(start,end,items) {
      var itemsToReturn = [];
      items.forEach( function(item){
        if (item.sort >= start && item.sort <= end){ itemsToReturn.push(item); } }); return itemsToReturn; }, //removes an item with the specified index removeItem : function(indexVar,items) { items.forEach(function(item, index) { if (item.sort == indexVar) { items.splice(index, 1); } }); return items; }, //performs a standard numeric array sort sortItems : function(items) { items.sort(function(a, b) { return a.sort > b.sort ? 1 : -1;
      });
      return items;
    },
    
    //after a sort, renumbers items (ie removes gaps in the sort) using the iterator index
    renumberItems : function(items) {
      items = this.sortItems(items);
      items.forEach(function(item, index) {
        item.sort = index;
      });
      return items;
    },
    
    //removes items from the source that are present in dest
    xorSourceItems : function(source,dest) {
      var itemsToReturn = [];
      source.forEach(function(sourceItem, sourceIndex) {
        var match = false;
        dest.forEach(function(destItem, destIndex) {
          if (destItem.value == sourceItem.value) {
            match = true;
          }
        });
        if (!match){
            itemsToReturn.push(sourceItem)
        }
      });
      return itemsToReturn;
    }

Design Rationale

By having the set of list handling utility methods above, Once they were (hopefully) bug free, I could use them to easily manipulate items in my lists. They are used by the higher functions extensively to perform all actions in the app.

I really wanted to truly abstract the two lists into two instances of the same component, but I thought that Locker Service would break the drag and drop API if I made it out of components. Hence, in the main app, I have to lists defined, source and destination.

This was… unsettling to me, as I like componentization, reusability and abstraction… so in the interests of abstraction, I tried to make the helper code as unaware as possible of the actual lists it was operating on.

Below you can see a generic `add` method that can be used for either the left or right hand list:
(as you can see, there is a little leakage of information (check for ‘source’ / ‘destination’) – I might be able to remove this in a later iteration).

Perhaps the best way of avoiding knowledge would be to pass control back to the controller and get the controller to handle these details.

An Example of Helper Abstraction

   init: function(component, event, helper) {

    handleAddItems : function(component,event, sourceName, destinationName, selectedListName, selectedItemName,addTo){
    
      var itemsHighlight = component.get(selectedListName);
      var itemHighlight = component.get(selectedItemName);
      
      //source list (nominally lhs)
      var source = component.get(sourceName);
      //destination list (nominally rhs)
      var destination = component.get(destinationName);
      
      //if we are selecting multiple items
      if (!itemsHighlight.length && itemHighlight){
        itemsHighlight.push(itemHighlight)
      }
      //something went wrong
      else if(!itemsHighlight.length && !itemHighlight){
        return;
      }
      var self = this;
      //one by one, move the items
      itemsHighlight.forEach(function(item){
        self.moveItemTo(source,destination,item,addTo);
      });
      
      //we never want to renumber the true source (lhs)
      if (addTo == 'source'){
        source = this.renumberItems(source);
      }
      else {
        destination = this.renumberItems(destination);
      }
      source = this.sortItems(source);
      destination = this.sortItems(destination);
      
      //write all values back
      component.set(sourceName,source);
      component.set(destinationName,destination);
      this.broadcastDataChange(component);
      
    },

In the controller, there are few “dummy” event handlers – these are just there to make the drag and drop API work, even though they are not used.

    
    handleOnDragOverDummy: function(component, event, helper) {
        event.preventDefault();
      },
      handleOnDragEnterDummy: function(component, event, helper) {
        event.preventDefault();
      },
      handleOnDragLeaveDummy: function(component, event, helper) {
        event.preventDefault();
      },

The source code is not officially available from LightningDesignSystem.com… but it’s pretty easy to scrape it.

Locker Service

One great thing about how this app works is that it’s all pretty much standard HTML.
Salesforce using LockerService now has secure versions of most elements and attributes, so you really can just apply business as usual.


You just have to remember to respect encapsulation and to use events and public component APIs (via publicly defined `method` attributes).
As long as you do this, Locker Service won’t get in your way.

I did run into one issue caused specifically by Lightning Locker Service: all properties for an Object must be specified the first time that Object is written to.

For example, I pass in some “init” lists to the Select component and I then add some new attributes – namely: sort, type and style.
I was force to utilize JSON parsing to separate the Objects from their references, because simply copying the object to the internal representation of that object prevented bindings from working.

    selectedItems.forEach( function(item,index){
      item.sort = index;
      item.type = 'destination';
      //need to define style here, although not used till later
      item.style = '';
    });

    //needed to make work with locker service otherwise bindings don't work 
    items = JSON.parse(JSON.stringify(items));
    selectedItems = JSON.parse(JSON.stringify(selectedItems));

To test, try with this app:

    
    <aura:application access="global" extends="force:slds" >
      <aura:attribute name="stagenames" type="Object[]" default="[ { 'label': 'In Credit Repair', 'value': 'In Credit Repair' }, { 'label': 'Annual Review', 'value': 'Annual Review' }, { 'label': 'Watching Prices', 'value': 'Watching Prices' }, { 'label': 'Initial Contact', 'value': 'Initial Contact' }, { 'label': 'Application', 'value': 'Application' }, { 'label': 'Waiting for Docs', 'value': 'Waiting for Docs' }, { 'label': 'Qualifying Docs Review', 'value': 'Qualifying Docs Review' }, { 'label': 'Additional Docs Requested', 'value': 'Additional Docs Requested' }, { 'label': 'Searching for Data', 'value': 'Searching for Data' }, { 'label': 'Submit to Admin', 'value': 'Submit to Admin' }, { 'label': 'Active', 'value': 'Active' }, { 'label': 'Contract Sent', 'value': 'Contract Sent' }, { 'label': 'Waiting on interested Party', 'value': 'Waiting on interested Party' } ]"/>
    
      <aura:attribute name="stagename" type="Object[]" default="[ { 'label': 'In Credit Repair', 'value': 'In Credit Repair' }, { 'label': 'Annual Review', 'value': 'Annual Review' }, { 'label': 'Watching Prices', 'value': 'Watching Prices' }]"/>
        
      
<div class="slds">

<div class="slds-box">
          <c:SPEAR_MultiColumnSelect fieldName="Opportunity Stage" allValues="{!v.stagenames}" selectedValues="{!v.stagename}" />
        </div>

     
      </div>

    </aura:application>


Here’s what it looks like:
pKVVGZEOOk.gif

Multi Column Select in Action


That’s all for now.

Enjoy!

The post Salesforce Lightning Drag and Drop Multiselect Component appeared first on Soliant Consulting.


Afficher la totalité du billet