Inputs not attached to Object Models?

  • 1
  • Question
  • Updated 5 years ago
  • Answered
I'm working on an API integration with a system where we want to send orders from within my clients ORG. There are a number of variable configuration pieces that I retrieve directly from a webservice. These configurations are not stored in SF, thus I'm finding it awkward to use skuid to display & select these.

For example, after getting the shipping address for the order, I make a callout to get available shipping methods. These are then displayed to the user for selection.

Currently, I'm doing this either by injecting the inputs with javascript (using a component). Functionally this works fine, but the injected components obviously are not styled with the rest of the page.

I was just wondering if there are any best practices for doing this type of thing, or if I will just have to use CSS to make everything fit.

(this is my last question today, I promise ;)
Photo of Charlie Jonas

Charlie Jonas

  • 670 Points 500 badge 2x thumb

Posted 5 years ago

  • 1
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
(By the way, I'm not an automated documentation-writing troll, despite popular clamor and evidence to the contrary.)

Okay, this is also a good question, and while the solutions are not "ideal", we use them both often and have no qualms about recommending them.

SOLUTION 1:
Define your inputs as real fields in Salesforce. This one's obvious, but I'm putting it here just for posterity and to reassure people that you don't have to do Solution 2 to make this work.

SOLUTION 2:
Use a Custom Field Renderer. These things are magical, and for JavaScript junkies, they take Skuid to a whole different level.

Basically, drag in an old actual field, e.g. the Id field, on your regular Model, into your Field Editor or Table, and then change its Label to your desired label, and set Field Renderer to "Custom (Run Snippet)", and then enter "FakeInput" as the Snippet name:



Then add the following JavaScript Resource of type "Inline (Snippet)":




var field = arguments[0],
value = '', // We could set a default value here, if we want
$ = skuid.$;

var fieldId = 'MyFakeField__c';

// Define custom metadata for our fake field,
// so that Skuid knows how to run it.
var metadata = {

id: fieldId,
displaytype: 'PICKLIST',
accessible: true,
createable: true,
editable: true,
// If set to true,
// then the default option should be chosen
required: false,
picklistEntries: [
{ value: 'Option1', label: 'Option 1', active: true, defaultValue: true },
{ value: 'Option2', label: 'Option 2', active: true, defaultValue: false },
{ value: 'Option3', label: 'Option 3', active: true, defaultValue: false }
]
};

// Add our fake metadata to our fields map
if (!field.model.fieldsMap[fieldId]) {
field.model.fields.push(metadata);
field.model.fieldsMap[fieldId] = metadata;
}

field.metadata = metadata;
field.mode = 'edit';

// Run the standard renderer
skuid.ui.fieldRenderers[metadata.displaytype][field.mode](field,value);



Let me explain what's going on here.

Custom Field renderers get handed a skuid.ui.Field object representing the Input Field on the page. skuid.ui.fieldRenderers is documented in the skuid.ui object docs and allows you to run a Skuid Ui Renderer for any DisplayType, e.g. PICKLIST, CURRENCY, REFERENCE, TEXTAREA, etc., for a particular Mode, e.g. read, readonly, edit, on a particular skuid.ui.Field object, with a given starting value.

So all we're doing here that's "weird" is that we're defining the Field's Metadata here in JavaScript rather than retrieving it from Salesforce. So here we're doing a Picklist Field, adding 3 Options to it, making it not-required, etc.

So here's what it looks like:

Photo of Charlie Jonas

Charlie Jonas

  • 670 Points 500 badge 2x thumb
Once again, great answer!

Solution 2 is exactly what I'm looking for; Adding the fields is not an option as we are working with the Task object (limited to 100 custom fields, i think). While it does seem like a little bit of a hack its definitely a better solution than what I'm currently doing.

3 for 3 on resolving all of my projects blocking issues; someone buy this man a beer.
Photo of Charlie Jonas

Charlie Jonas

  • 670 Points 500 badge 2x thumb
Actually I do have one more tinsy question...

What is the best way to retrieve the value of this field?

I added a change handler to the editor:


field.editor.handleChange = function(e) {
e.changes['OBJECTID'].OrigionalField__c
}


but referencing the field value is not quite as clean as I had hoped...

I can do this to allow it to be more agnostic to the dummy object I'm using:


e.changes[Object.keys(e.changes)[0]].OrigionalField__c;


Also seems strange that even though I change id in the metadata, the change event still references the original field? Wondering if it is also changing the underlying model as well. If I actually update field.id it resolves this.

I can definitely get by like this but I just wanted to make sure there isn't a better way I should be doing this.
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
Assuming that we've added the Field to the Model's Metadata, which we did in the example above by modifying field.model.fields and field.model.fieldsMap, we should be able to retrieve the value of the Field using the standard model.getFieldValue(row,field,[noEscape]) syntax, e.g.

var model = field.model,
row = field.row;

var fakeFieldValue = model.getFieldValue(row,'FakeField__c');
Photo of Charlie Jonas

Charlie Jonas

  • 670 Points 500 badge 2x thumb
Zach,

I've run into a bit of a block. I need to programmatically set these fields.

The following code works fine for "real" fields, but it doesn't have any affect on the ones I've custom rendered.

The only change to the code above is that I'm setting the field.Id so that the "if statement" at the lowest scope will evaluate render on the custom field.
(otherwise it has the Id of the original "dummy" field)



function updateField(model, id, value){
var $ = skuid.$;
var fModel = skuid.model.getModel(model);
var row = fModel.getFirstRow();
fModel.updateRow(row,id,value);

$.each(fModel.registeredLists,function(){
$.each(this.renderedItems,function(){
$.each(this.fields,function(){
if(this.id === id)
this.render();
});
});
});
}

Photo of Charlie Jonas

Charlie Jonas

  • 670 Points 500 badge 2x thumb
ahhhhh NVM I see. Because I'm using a custom renderer my snippet gets calls whenever I call this.render();. I was setting value = ''; when instead I needed to query the model for the value to use.

It only took me about 20 minutes of stepping through the highly efficient (and hard to read) render() function to figure this out, haha