ServiceWise Developers’ Blog – Angular Directive with Salesforce Field Sets

In this article we will demonstrate a simple yet effective solution that combines a dynamic field set and Angular directive. This solution can be used to easily present automatically any SObject, view and edit it.

The use case we’ll present this time is of a client who wanted a dynamic Visual Force page where they can choose the fields that will be shown on the screen, as well as have the ability to edit and save the SObject.

This solution requires an import of Angular 1.3.0 and jquery libraries.

  1. We created a field set under the SObject and chose all the relevant fields.
    1. In the apex controller we created a wrapper class named “FieldsMapping” that will represent each field in the field set:
  2. In the controller, we created a List named <FieldsMapping>, and for each field in the field set we created an instance of this FieldsMapping wrapper. So, now we have the List<FieldsMapping> fields in the controller.

Now we will demonstrate the steps on the client’s side:

  1. We created a directive in angular, named “‘dynamichtml'”:
  2. In the code above, for each type, we created a dynamic template, that was linked by the < dynamichtml > html tag.
  3. From the apex controller we get an instance of the FieldsMapping wrapper class. We check its type in the switch statement, according to the field type we created in the template for the ng-directive.
  4. In the html code itself, we added an ng-repeat loop that iterates the FieldsMapping list and adds the directive for each field in the list.

In order to add an edit mode, we created a function that checks the field type and updates the SObject:

After implementing the code, this is what the end user saw:

We hope this post taught you something new, and showed you how creative and innovative you can get with code. See you next time on our blog, with another unprecedented solution from the ServiceWise R&D team!

Let us know in the comments if there’s a specific subject you’d like us to write about, and if you found this post helpful.

If you’d like to hear more about our solutions and services, don’t hesitate to contact us.

Salesforce – חידושים בגרסת אביב 2018

גרסת אביב 2018 של סיילספורס כבר באוויר! כמו תמיד, הגרסא החדשה הביאה איתה עדכונים רבים. המומחים של ServiceWise בחרו להרחיב על אודות שלושה מהחידושים המעניינים והמועילים המוצעים בגרסאות החדשות.

ServiceWise Developers’ Blog – Dependent Picklist in Lightning Component

Welcome to our new Developers Blog! Our R&D team here at ServiceWise is dedicated to finding unorthodox and groundbreaking coding solutions for customers. So, we thought – why not share these solutions with the world?

For our first blog post, we chose to present to you a solution for a dependent Picklist in Lightning component. The purpose of this solution was to create a dependent Picklist and present it in a custom UI in Lightning, according to the customer’s request.

Why did we need to create a custom solution? The customer had two picklists – Country (controlling list) and Region (dependent). For each picklist entry of the controlling list, Salesforce saves a property name – “valid for” – which is a string in Base64 encoding. It looks like that – gAAA. In order to transfer this string to bytes, we had to go through the following process:
Base64 -> binary -> bitwise operation -> bytes

Example:

Pke.validFor :gAAA
Displayed as bits: 100000 00000000000000000
Displayed as bytes: 1000000000000000000000000000000

Functions to manipulate the Base64 string (the main function is base64ToBits)

// base64Chars hold the base 64 chars
	public static final String base64Chars = '' +
                        'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
			'abcdefghijklmnopqrstuvwxyz' +
			'0123456789+/'
	  // Convert decimal to binary representation 
         //Remainder (0 or 1).
	// these, in reverse order, are the binary.
	
      public static String decimalToBinary(Integer val) {
		String bits = '';
		while (val > 0) {
			Integer remainder = Math.mod(val, 2);
			val = Integer.valueOf(Math.floor(val / 2));
			bits = String.valueOf(remainder) + bits;
		}
		return bits;
	}//decimalToBinary
	// Convert a base64 token into a binary/bits representation
	// e.g. 'gAAA' => '1000000	00000 0000 00000'
	public static String base64ToBits(String validFor) {
		if (String.isEmpty(validFor)) return '';

		String validForBits = '';

		for (Integer i = 0; i < validFor.length(); i++) {
			String thisChar = validFor.mid(i, 1);
			Integer val = base64Chars.indexOf(thisChar);
			String bits = decimalToBinary(val).leftPad(6, '0');
			validForBits += bits;
		}

		return validForBits;
	}//base64ToBits

In order to find the controlling picklist entry that corresponds with an entry in the dependent picklists, we go over the bits of the “valid for” property of the dependent entry picklist from left to right. Each bit corresponds with an option in the index of the bit. (1 = it corresponds with the dependent picklist entry[i], 0 = it doesn’t correspond with it). However, Salesforce doesn’t reveal the “valid for” information in the APEX classes, but in the API calls. So, to overcome this problem we created a wrapper class:

public class PicklistEntryWrapper {
		public String active {get; set;}
		public String defaultValue {get; set;}
		public String label {get; set;}
		public String value {get; set;}
		public String validFor {get; set;}
	}

How did we do that?
First of all, note that all the functions that take care of the bitwise operation are handled in a class named “Bitset” (including the wrapper class).
Full Bitset class code:

Full Bitset class code  
public class Bitset  {

	public class PicklistEntryWrapper {
		public String active {get; set;}
		public String defaultValue {get; set;}
		public String label {get; set;}
		public String value {get; set;}
		public String validFor {get; set;}
	}
	// base64Chars holde the base 64 chars
	public static final String base64Chars = '' +
			'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
			'abcdefghijklmnopqrstuvwxyz' +
			'0123456789+/';

	//convert picklist entery to wreper
	public static List<PicklistEntryWrapper> wrapPicklistEntries(List<Schema.PicklistEntry> PLEs) {
		return (List<PicklistEntryWrapper>)
			JSON.deserialize(JSON.serialize(PLEs), List<PicklistEntryWrapper>.class);
	}//wrapPicklistEntries

	// Convert decimal to binary representation (alas, Apex has no native method 🙁
	//    eg. 4 => '100', 19 => '10011', etc.
	// Method: Divide by 2 repeatedly until 0. At each step note the remainder (0 or 1).
	// These, in reverse order, are the binary.
	public static String decimalToBinary(Integer val) {
		String bits = '';
		while (val > 0) {
			Integer remainder = Math.mod(val, 2);
			val = Integer.valueOf(Math.floor(val / 2));
			bits = String.valueOf(remainder) + bits;
		}
		return bits;
	}//decimalToBinary

	// Convert a base64 token into a binary/bits representation
	// e.g. 'gAAA' => '1000000	00000 0000 00000'
	public static String base64ToBits(String validFor) {
		if (String.isEmpty(validFor)) return '';

		String validForBits = '';

		for (Integer i = 0; i < validFor.length(); i++) {
			String thisChar = validFor.mid(i, 1);
			Integer val = base64Chars.indexOf(thisChar);
			String bits = decimalToBinary(val).leftPad(6, '0');
			validForBits += bits;
		}

		return validForBits;
	}//base64ToBits

}//Bitset

Let’s break it down to steps:

  1. We’ve got two fields – dependent and non-dependent. Here’s an example with the Country and Region fields.
    Schema.DescribeFieldResult fieldResultCountry = User.Countrycode.getDescribe();//get country code 
    List<Schema.PicklistEntry> contrEntries = fieldResultCountry.getPicklistValues();//parse country code to list
    
  2. We created a map with a key type “string” => The map holds the controlling picklist entry value (in our example – Country), and the value the “string” list type => that will hold the dependent picklist entry values (in our example – Region). Eventually we’ll get a map with the key “Country” and value list of regions.
    Map<String,List<String>> objResults = new Map<String,List<String>>();
    
  3. We created a picklist from the wrapper class that we made and inserted into it the entries from the dependent picklist, after we serialized and de-serialized them.
    //convert picklist entry to wrapper
    public static List<PicklistEntryWrapper> wrapPicklistEntries(List<Schema.PicklistEntry> PLEs) {
    return (List<PicklistEntryWrapper>)JSON.deserialize(JSON.serialize(PLEs), List<PicklistEntryWrapper>.class);
    }//wrapPicklistEntries
    
    List<Bitset.PicklistEntryWrapper> depEntries = Bitset.wrapPicklistEntries(fieldResultState.getPicklistValues());
    
  4. We went over all of the picklist entries in the controlling picklist and inserted their values to a string list – “controlling values”. We inserted each value as a key to the map we created in step 2 and gave it the value “new list”. Now we’ll have a map with Country as keys and empty lists as value, and a list that holds the Country values.
    list<String> controllingValues = new List<String>();
    		
    		for (Schema.PicklistEntry ple : contrEntries) {
    			String valueStr = ple.getValue();
    			objResults.put(valueStr, new List<String>());
    			controllingValues.add(valueStr);
    	}
    
  5. We went over the dependent entry picklist in the wrapper list from step 2. We kept the value of each entry. In the same time, each “valid for” field in the different values is being converted into bits.
  6. We went over the bits in the string we’ve created. If the bit = 1 we added the value to the dependent list as a controlling value, to the map from step 2.
    for (Bitset.PicklistEntryWrapper plew : depEntries) {
    	String value = plew.value;
    	String validForBits = Bitset.base64ToBits(plew.validFor);
    	for (Integer i = 0; i < validForBits.length(); i++) {
    // For each bit, in order: if it's a 1, add this label to the dependent list for the corresponding controlling value
    		   String bit = validForBits.mid(i, 1);
    	   if (bit == '1') {
    			     objResults.get(controllingValues.get(i)).add(value);
    	   }
    	 }
           }</li>
    
    
  7. We ended up with a map that holds the Country field and a list of Regions!
  8. Full code of Bitset:
    public class Bitset  {
    
    	public class PicklistEntryWrapper {
    		public String active {get; set;}
    		public String defaultValue {get; set;}
    		public String label {get; set;}
    		public String value {get; set;}
    		public String validFor {get; set;}
    	}
    	// base64Chars holde the base 64 chars
    	public static final String base64Chars = '' +
    			'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
    			'abcdefghijklmnopqrstuvwxyz' +
    			'0123456789+/';
    
    	//convert picklist entery to wreper
    	public static List<PicklistEntryWrapper> wrapPicklistEntries(List<Schema.PicklistEntry> PLEs) {
    		return (List<PicklistEntryWrapper>)
    			JSON.deserialize(JSON.serialize(PLEs), List<PicklistEntryWrapper>.class);
    	}//wrapPicklistEntries
    
    	// Convert decimal to binary representation (alas, Apex has no native method 🙁
    	//    eg. 4 => '100', 19 => '10011', etc.
    	// Method: Divide by 2 repeatedly until 0. At each step note the remainder (0 or 1).
    	// These, in reverse order, are the binary.
    	public static String decimalToBinary(Integer val) {
    		String bits = '';
    		while (val > 0) {
    			Integer remainder = Math.mod(val, 2);
    			val = Integer.valueOf(Math.floor(val / 2));
    			bits = String.valueOf(remainder) + bits;
    		}
    		return bits;
    	}//decimalToBinary
    
    	// Convert a base64 token into a binary/bits representation
    	// e.g. 'gAAA' => '1000000	00000 0000 00000'
    	public static String base64ToBits(String validFor) {
    		if (String.isEmpty(validFor)) return '';
    
    		String validForBits = '';
    
    		for (Integer i = 0; i < validFor.length(); i++) {
    			String thisChar = validFor.mid(i, 1);
    			Integer val = base64Chars.indexOf(thisChar);
    			String bits = decimalToBinary(val).leftPad(6, '0');
    			validForBits += bits;
    		}
    
    		return validForBits;
    	}//base64ToBits
    
    }//Bitset
    

    Full code of the function that we used in the Lightning component (we will pass that function the desired country code to get the list of depended regions)

    @AuraEnabled global static List<String> getRegonPiklistValuse(String country){
    
    Map<String,List<String>> objResults = new Map<String,List<String>>();
    /* get all country codes */
    List<String>countryCodes = new List<String>();//list that holds the country code
    
    Schema.DescribeFieldResult fieldResultCountry = User.Countrycode.getDescribe();//get country code 
    List<Schema.PicklistEntry> contrEntries = fieldResultCountry.getPicklistValues();//parse country code to list
    
    		
    		/*get all state codes */
    List<String>stateCodes = new List<String>();//list that holds the state code
    Schema.DescribeFieldResult fieldResultState = User.statecode.getDescribe();//get state code 
    List<Bitset.PicklistEntryWrapper> depEntries = Bitset.wrapPicklistEntries(fieldResultState.getPicklistValues());
    
    List<String> controllingValues = new List<String>();
    		
    for (Schema.PicklistEntry ple : contrEntries) {
    	String valueStr = ple.getValue();
    	objResults.put(valueStr, new List<String>());
    	controllingValues.add(valueStr);
    }
    
    for (Bitset.PicklistEntryWrapper plew : depEntries) {
    	String value = plew.value;
    	String validForBits = Bitset.base64ToBits(plew.validFor);
    	for (Integer i = 0; i < validForBits.length(); i++) {
    // For each bit, in order: if it's a 1, add this label to the dependent     list for the corresponding controlling value
    		String bit = validForBits.mid(i, 1);
    		if (bit == '1') {
    								 objResults.get(controllingValues.get(i)).add(value);
    		}
    	}
    }
    return objResults.get(country);
    	 
    }//countryStateCodeStract
    
    

    We hope this post taught you something new, and showed you how creative and innovative you can get with code! See you next time on our blog, with another unprecedented solution from the ServiceWise R&D team!

    Let us know in the comments if there's a specific subject you'd like us to write about, and if you found this post helpful.

    If you'd like to hear more about our solutions and services, don't hesitate to contact us.