Creating custom components

  • 1
  • Question
  • Updated 4 years ago
  • Answered

This looks really cool, any time frame for when? Also I understand that SalesForce Lightning components will work in the Skuid builder, but will Skuid components work in the Lightning App Builder? Sorry if I'm getting too far ahead but this stuff is exciting!
Photo of Moshe Karmel

Moshe Karmel, Champion

  • 8,646 Points 5k badge 2x thumb

Posted 4 years ago

  • 1
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,004 Points 20k badge 2x thumb
Hi Moshe,

Regarding Skuid and Lightning Components, Ken wrote a blog post which should help to describe how they work together. The short answer is that yes you will be able to create Lightning Components that "wrap" Skuid, that would work in Lightning App Builder.

http://www.skuidify.com/blog/skuid-plus-lightning-questions-answered

Regarding Custom Components, we are working towards rolling out a more depth tutorial on creating "custom builders" for your components --- right now the best docs on this are here and here.
Photo of Moshe Karmel

Moshe Karmel, Champion

  • 8,646 Points 5k badge 2x thumb
Thanks Zach, one thing that's been confusing me is the Static Resources vs. Inline(Component) snippets. In the map component tutorial I understand that the <ModuleName>BuildersJS file will get picked up by skuid, and show up in the component tab on the edit page. But what's the deal with the MapsJS file? Why can't the 2 Static Resources be combined into one? Also I get that you need one snippet to define the builder (the way the component looks on the edit page) and another to define the actual behavior of the component (the way the component acts on the page). Can I add some of my existing JS logic to the runtime behavior of my component? For example I have a certain process which I use extensively which is a text box where users enter Utility Account numbers. Every time I want to have UA number entry on a page, I need a field editor with a text box, a button to call my snippet, and my snippet which calls an apex webservice. It would be awesome to be able to stuff all of my logic, snippets, field editors etc. into a component which could just be dragged into the page! Can you give me some information on how I could accomplish this? Thanks.
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,004 Points 20k badge 2x thumb
Moshe, you can absolutely consolidate all of your logic into one reusable Component.

Inline(Component) Resources and Static Resources are both accomplishing the same thing --- loading JavaScript in your page. The benefit of storing your JavaScript in a Static Resource, e.g. MapsJS, is that you don't have to redefine this JavaScript in multiple pages. Write once, run and use multiple places. If you need to make a change, then you only have to make that change in one place. 

There are 2 reasons that the Builder JS file and the Runtime JS file should not be combined. First, they each reference different Skuid API's --- and the Builder API's are only loaded when you're in the page builder. Second, this code is not needed at runtime --- you don't want to load in unnecessary code.

As you've discerned, there's (at least) 2 types of files needed:

-Runtime code (at least JavaScript, maybe also CSS)
-Builder code (at least JavaScript, maybe also CSS)

The runtime code needs to be loaded on each page where a Component is used --- the trick here is that with reusable components, you might not know which pages your Component is going to be used on! There are a couple ways to deal with this:

1. Whenever you drag/drop your custom component into a page, make sure to also load the Static Resource containing that component's code into the page (by adding a JavaScript Resource).
2. Use the Module paradigm --- as this tutorial describes, there is some special logic around putting pages in a Module, and giving your runtime JavaScript Static Resource the name <ModuleName>JS that will cause Skuid to search for this file at runtime if your Page is in that Module, and load in the <ModuleName>JS StaticResource.
3. Create a Component Pack that contains your Component. We have not documented how to create your own Component Packs yet, but we are working on this, and it is our preferred method going forward.
Photo of Moshe Karmel

Moshe Karmel, Champion

  • 8,646 Points 5k badge 2x thumb
Thanks for the response Zach, I get the new builder part, and how you set up the "objProps". This looks really straightforward, to get the users required input for the component:

1) define user needed data points for your component to work, by adding Object Properties
2) reference those variables in the "registerBuilder"/runtime by getting them from the "xmlConfig" object

My question is can I create a button that resides in my component, which runs a JS snippet when clicked. I would like to be able to add all of that into my component. What I would like to know is:

a) How can I add a button to my component?
b) Should that button be defined in the builder/edit page portion or the registerBuilder/runtime portion?
c) How can I set up a snippet to be run when that button is clicked (onClick, registerSnippet, etc.)?
Photo of Moshe Karmel

Moshe Karmel, Champion

  • 8,646 Points 5k badge 2x thumb
Zach can you give me a hint as to how I can add buttons and snippets into the runtime behavior of a component? Thanks
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,004 Points 20k badge 2x thumb
Moshe, here is an example that uses the approach of dynamically creating Skuid components from XML. So the idea is that you start with the XML for the Skuid components that you want to reuse in a component, then you can wrap this up with a few configurable attributes that will make it easy to reuse everything across multiple pages.

So in my example, I'm using the Page Title component to create 2 buttons, and I have a configuration property to show one of the buttons.

Here's the Runtime JavaScript. Notice that I put my Snippet definitions and other common code inside this file:




(function( skuid ){
var utils = skuid.utils,
$ = skuid.$;
// Register Snippets
skuid.snippet.registerSnippet('DoSomethingCrazy',function(params){
console.log(params);
alert('Do Something Crazy!');
});
skuid.snippet.registerSnippet('Button2Craziness',function(params){
console.log(params);
alert('Button 2 got clicked, craziness!');
});
//
// Register our Component Types
//
// BUTTON BAR
skuid.componentType.register('reusablewidgets__buttonbar',function(element,xmlDef,component){
var modelName = xmlDef.attr('model');
var showButton2 = xmlDef.attr('showbutton2')==='true';
var buttonBarXML = 
'<pagetitle model="'+modelName+'">'
  + '<maintitle>'
      + '<template></template>'
  + '</maintitle>'
  + '<subtitle>'
      + '<template></template>'
  + '</subtitle>'
  + '<actions>'
      + '<action type="custom" snippet="DoSomethingCrazy" label="Do Something Crazy" icon="ui-silk-tux"/>'
      + (showButton2 
      ? '<action type="custom" snippet="Button2Craziness" label="Button 2 craziness" icon="ui-silk-tux"/>' 
      : '')
  + '</actions>'
+ '</pagetitle>';

skuid.component.factory({
element: element,
xmlDefinition: buttonBarXML
});
});
})( skuid );



Here's the Builder JavaScript:

(function(skuid) {
var $ = skuid.$,
builder = skuid.builder,
bc = builder.core,
utils = skuid.utils;

bc.registerBuilder(new bc.Builder({
id : 'reusablewidgets__buttonbar',
name : 'Reusable Button Bar',
icon : 'ui-silk-tux',
description : 'Common buttons that we use',
hideFromComponentsList : false,
componentRenderer : function(component) {
component.header.text('Reusable Button Bar');
},
propertiesRenderer : function(propertiesObj,component) {
propertiesObj.setHeaderText('Button Bar Properties');
var propsList = [
{
id : 'model',
type : 'model',
label : 'Model'
},
{
id : 'showbutton2',
type : 'boolean',
defaultValue: true,
label : 'Show Button 2'
}
];
propertiesObj.body.append(
          skuid.builder.buildPropsEditor(component.state,propsList)
      );
},
defaultStateGenerator : function() {
return skuid.utils.makeXMLDoc(
            '<reusablewidgets__buttonbar showbutton2="true"/>'
        );
}
}));

})(skuid);






And here's a preview of it in action:

Photo of Moshe Karmel

Moshe Karmel, Champion

  • 8,646 Points 5k badge 2x thumb
Thanks for taking the time to put this together, this is exactly what I needed!
Photo of Moshe Karmel

Moshe Karmel, Champion

  • 8,646 Points 5k badge 2x thumb
Zach I tried copying and pasting this code in to my static resources and I am getting the following:
Photo of Moshe Karmel

Moshe Karmel, Champion

  • 8,646 Points 5k badge 2x thumb
I'm not sure what was wrong with your example, but I used it as a guide and... I built my first working custom skuid component!
Photo of Rob Hatch

Rob Hatch, Official Rep

  • 44,006 Points 20k badge 2x thumb
Well done Moshe.   Well done! 
Photo of Moshe Karmel

Moshe Karmel, Champion

  • 8,646 Points 5k badge 2x thumb
Quick follow up question do you guys have an easy way to stringify XML? My custom components are getting bigger and bigger and I would like to be able to easily make <tabset/> into '<tabset/>' + etc.
Photo of Ben Hubbard

Ben Hubbard, Employee

  • 12,470 Points 10k badge 2x thumb
Hi Moshe, I've looked for something that can convert a regular XML string in to a pretty Javascript formatted string, and haven't had any luck.
Photo of Moshe Karmel

Moshe Karmel, Champion

  • 8,646 Points 5k badge 2x thumb
Hey Ben I just put something together in (beginner) Python, it's not so pretty but it works :).

import sysimport os
import io
FILENAME = 'test.xml'
file = open(FILENAME,'r+')
for line in file:
    line = line.strip('\n\t ')
    file.write('
' + line + ' \n+ ')
file.close()
(Edited)