create model and component from javascript

Ok. My little skype click-to-call and logging app is coming along nicely. I’d like to move the model and components into the javascript so that I can easily add this functionality to any page.

Can I do this? I mean, can a page which already has models and components get additional models and components from my javascript?

oh oh … Can javascript alter the XML of the page prior to loading? ie. can I set the field rendering to my custom snippet from javascript?

That would be slick. One reference to one static resource to automatically set all phone fields to use the snipper, model and popup component.

K. One other thing. Can a popup component be generated completely from javascript?

Another thing, what happens if a page include page has the same static resource referenced? Will it crash? Will it create two of everything? Can I add code to the javascript to detect whether or not the loaded page (parent and page include) has the model and component?

It depends on what your Static Resource contains and does. Yes you can add code to the JavaScript to detect if a particular Model / Component exists, you could add something like this:

// When the page / page include is loaded…
skuid.$(document.body).one(‘pageload’,function(){
    // If we do not have a certain Model yet, create it
    if (!skuid.$M(‘SomeModel’)) {
         // Create the Model dynamically
    }
    // If we do not have a certain Component yet, create it dynamically
    if (!skuid.$C(‘SomeComponentUniqueId’)) {
         // Create the Component dynamically
    }
});

Here, ‘SomeModel’ is the Id of a particular Model, and ‘SomeComponentUniqueId’ is the Unique Id of a particular Component.

Yes it would be possible to override a particular Field Renderer globally on page load, like this:

(function(skuid){

   // Get references to our original field renderers
   var fieldRenderers = skuid.ui.fieldRenderers; 
   var PHONE = skuid.ui.fieldRenderers.PHONE;
   var originalPhoneRead = PHONE.read;
   var originalPhoneReadonly = PHONE.readonly;
   var originalPhoneEdit = PHONE.edit;

   // My custom Phone field renderer
   var customPhoneFieldRenderer = function() {
       var field = arguments[0],
             value = skuid.utils.decodeHTML(arguments[1]);

      // rest of custom renderer…
   };

   // Override all of Skuid’s standard PHONE field renderers to call our custom renderer
   PHONE.read = PHONE.readonly = PHONE.edit = function(){
       customPhoneFieldRenderer.apply(this,arguments);
   };

})(skuid);

See this tutorial: https://community.skuid.com/t/trigger-popup-from-javascript

Can I call this skuid.utils.createPopupFromPopupXML from a custom field renderer snippet onclick?

Yep, no problem.

wait for it … wait for it … what was me crossing the threshold into the dark side.  ( bug eyed teethy smile )

You can do so much with javascript. AWESOME!

One file to rule all phone fields to auto set the link to skype with a custom call lop popup from any page the static resource is added. Won’t need to add XML, Component, inline javascript or set any of the field to custom render.

One last thing, I assume I can filter which fields will get this custom field renderer by not having the words “fax” or “facsimile” in the field label or custom label. Pointers on this would much much appreciated.

Pat, note the changes I made in Bold. These changes should get you on the right track — note the part where I check the Fields’ Id to see if it contains fax or facsimile:

(function(skuid){

   // Get references to our original field renderers
   var fieldRenderers = skuid.ui.fieldRenderers; 
   var PHONE = skuid.ui.fieldRenderers.PHONE;
   var originalPhone = {
        read: PHONE.read,
        readonly: PHONE.readonly,
        edit: PHONE.edit
   };

   // My custom Phone field renderer
   var customPhoneFieldRenderer = function() {
       var field = arguments[0],
             value = skuid.utils.decodeHTML(arguments[1]);

      // rest of custom renderer…
   };

   // Override all of Skuid’s standard PHONE field renderers to call our custom renderer
   PHONE.read = PHONE.readonly = PHONE.edit = function(field){
      // If our Field’s API Name contains “fax” or “facsimile”, then use the standard renderers
       if (skuid.utils.contains(field.id,‘fax’) || skuid.utils.contains(field.id,‘facsimile’)) {
          originalPhone[field.mode].apply(this,arguments);
       }
       // Otherwise use our custom renderer
       else {
          customPhoneFieldRenderer.apply(this,arguments);
       }
   };

})(skuid);

Ok. I think I’m close, but I don’t understand what’s not working. So close. Testing on the Contact object.

<skuidpage tabtooverride="Elite_Agent__c" showsidebar="true" showheader="true" unsavedchangeswarning=""> <models> <model id="Contacts" limit="20" query="true" createrowifnonefound="false" sobject="Contact" doclone="" type=""> <fields> <field id="Phone"/> <field id="HomePhone"/> <field id="MobilePhone"/> </fields> <conditions/> <actions/> </model> </models> <components> <skootable showconditions="true" showsavecancel="false" searchmethod="server" searchbox="true" showexportbuttons="false" pagesize="10" createrecords="false" model="Contacts" buttonposition="" mode="readonly"> <fields> <field id="Phone" valuehalign="" type=""/> <field id="HomePhone" valuehalign="" type=""/> <field id="MobilePhone" valuehalign="" type=""/> </fields> <rowactions/> <massactions usefirstitemasdefault="true"/> <views> <view type="standard"/> </views> </skootable> </components> <resources> <labels/> <css/> <javascript> <jsitem location="inline" name="clicktocall" cachelocation="false" url="">(function(skuid){ //Get references to our original field renderers var fieldRenderers = skuid.ui.fieldRenderers; var PHONE = skuid.ui.fieldRenderers.PHONE; var originalPhone = { read: PHONE.read, readonly: PHONE.readonly, edit: PHONE.edit }; //My custom Phone field renderer var customPhoneFieldRenderer = function() { var field = arguments[0] , value = arguments[1] , decodedValue = skuid.utils.decodeHTML(value) , renderer = skuid.ui.fieldRenderers[field.metadata.displaytype] , mode = field.mode , $ = skuid.$; // get a shorthand reference to jquery // we want to use the default skuid rendering since we are not doing // anything special to the information displayed in this field // need to call decode because the 'value' from arguments[1] is escaped // but the renderer needs the unescaped value as it will escape it internally renderer[mode](field, decodedValue); // find the first anchor - there should only be one but just being safe var anchor = $(field.element).find('a:first'); // if we found an anchor (we won't find one when in edit mode) if (anchor &amp;amp;&amp;amp; anchor.length) { // set the href attribute value $(anchor).attr('href', 'skype:' + decodedValue); // add event listener for click // NOTE - This doesn't handle cases like right click or keyboard // events so if those are needed, this needs to be adjusted. This will // only handle left-mouse click $(anchor).on('click', function () { var inputmodel = field.model , inputrow = field.row , whatid = inputmodel.getFieldValue(inputrow,'Id') , userid = skuid.utils.userInfo.userId; var popupXMLString = '&amp;lt;popup title="New Popup" width="90%"/&amp;gt;' + '&amp;lt;components/&amp;gt;' + '&amp;lt;panelset type="custom" scroll="" cssclass=""/&amp;gt;' +'&amp;lt;panels/&amp;gt;' +'&amp;lt;panel width="100%"/&amp;gt;' +'&amp;lt;components/&amp;gt;' +'&amp;lt;includepanel type="skuid" querystring="eaid=' + whatid + '&amp;amp;amp;waid=' + whatid + '&amp;amp;amp;ownerid=' + userid + '" pagename="CallLogTopPane" module=""/&amp;gt;'; var popupXML = skuid.utils.makeXMLDoc(popupXMLString); var popup = skuid.utils.createPopupFromPopupXML(popupXML); }); } }; // Override all of Skuid's standard PHONE field renderers to call our custom renderer PHONE.read = PHONE.readonly = PHONE.edit = function(field){ // If our Field's API Name contains "fax" or "facsimile", then use the standard renderers if (skuid.utils.contains(field.id,'fax') || skuid.utils.contains(field.id,'facsimile')) { originalPhone[field.mode].apply(this,arguments); } // Otherwise use our custom renderer else { customPhoneFieldRenderer.apply(this,arguments); } }; })(skuid); </jsitem> </javascript> </resources> </skuidpage>

What part is not working?

Did you try it? Did it work for you?

I tried checking it out, looks like you’re getting this error:

 Uncaught RangeError: Maximum call stack size exceeded

Yeah. I got that error, but … I have no idea whatsoever how to fix that. :S

According to stackoverflow: http://stackoverflow.com/questions/7658775/chrome-jquery-uncaught-rangeerror-maximum-call-stack-size… You might be creating too many handlers, you can fix this by changing this line:

$(anchor).on('click', function () {


To this:

$(anchor).one('click', function () {

no dice.

$(anchor).one('click', function () {

I did try commenting stuff out until I didn’t get an error. Commenting this out let my page render but without anything in fields.

/* renderer[mode](field, decodedValue); // find the first anchor - there should only be one but just being safe var anchor = $(field.element).find('a:first'); // if we found an anchor (we won't find one when in edit mode) if (anchor &amp;&amp; anchor.length) { // set the href attribute value $(anchor).attr('href', 'skype:' + decodedValue); // add event listener for click // NOTE - This doesn't handle cases like right click or keyboard // events so if those are needed, this needs to be adjusted. This will // only handle left-mouse click $(anchor).one('click', function () { var inputmodel = field.model , inputrow = field.row , whatid = inputmodel.getFieldValue(inputrow,'Id') , userid = skuid.utils.userInfo.userId; var popupXMLString = '<popup title="New Popup" width="90%"/>' + '<components/>' + '<panelset type="custom" scroll="" cssclass=""/>' +'<panels/>' +'<panel width="100%"/>' +'<components/>' +'<includepanel type="skuid" querystring="eaid=' + whatid + '&amp;amp;waid=' + whatid + '&amp;amp;ownerid=' + userid + '" pagename="CallLogTopPane" module=""/>'; var popupXML = skuid.utils.makeXMLDoc(popupXMLString); var popup = skuid.utils.createPopupFromPopupXML(popupXML); }); } */

OK try this as your full snippet, it worked for me.

(function(skuid){ var $ = skuid.$;
$(function(){
//Get references to our original field renderers
var fieldRenderers = skuid.ui.fieldRenderers;
var PHONE = skuid.ui.fieldRenderers.PHONE;
var originalPhone = {
read: PHONE.read,
readonly: PHONE.readonly,
edit: PHONE.edit
};

//My custom Phone field renderer
var customPhoneFieldRenderer = function() {
var field = arguments[0]

    , value = arguments[1]

    , decodedValue = skuid.utils.decodeHTML(value)

    , renderer = skuid.ui.fieldRenderers[field.metadata.displaytype]

    , mode = field.mode

    , $ = skuid.$; // get a shorthand reference to jquery

    // we want to use the default skuid rendering since we are not doing
    // anything special to the information displayed in this field
    // need to call decode because the 'value' from arguments[1] is escaped
    // but the renderer needs the unescaped value as it will escape it internally

    renderer[mode](field, decodedValue);

    // find the first anchor - there should only be one but just being safe
   
    var anchor = $(field.element).find('a:first');
    
    // if we found an anchor (we won't find one when in edit mode)
    

    if (anchor &amp;&amp; anchor.length) {
    
        // set the href attribute value
    
        $(anchor).attr('href', 'skype:' + decodedValue);
    
        // add event listener for click
        // NOTE - This doesn't handle cases like right click or keyboard
        // events so if those are needed, this needs to be adjusted.  This will
        // only handle left-mouse click
    
        $(anchor).one('click', function () {
    
            var inputmodel = field.model
            , inputrow = field.row
            , whatid = inputmodel.getFieldValue(inputrow,'Id')
            , userid = skuid.utils.userInfo.userId;
        
            var popupXMLString = 
                '<popup title="New Popup" width="90%"/>'
                + '<components/>'
                + '<panelset type="custom" scroll="" cssclass=""/>'
                +'<panels/>'
                +'<panel width="100%"/>'
                +'<components/>'
                +'<includepanel type="skuid" querystring="eaid=' + whatid + '&amp;amp;waid=' + whatid + '&amp;amp;ownerid=' + userid + '" pagename="CallLogTopPane" module=""/>';

            var popupXML = skuid.utils.makeXMLDoc(popupXMLString);
            
            var popup = skuid.utils.createPopupFromPopupXML(popupXML);
    
        });
    }

};

// Override all of Skuid's standard PHONE field renderers to call our custom renderer
PHONE.read = PHONE.readonly = PHONE.edit = function(field){
    // If our Field's API Name contains "fax" or "facsimile", then use the standard renderers
    if (skuid.utils.contains(field.id,'fax') || skuid.utils.contains(field.id,'facsimile')) {
        originalPhone[field.mode].apply(this,arguments);
    } 
    // Otherwise use our custom renderer
    else {
        customPhoneFieldRenderer.apply(this,arguments);
    }
};

});
})(skuid);

Commenting this alone lets my page render. Commenting anything else out makes no difference.

/* renderer[mode](field, decodedValue); */<br>