Build your first Support app - Part 3: Creating and inserting templates

Have more questions? Submit a request

30 Comments

  • Haroon Haider
    Comment actions Permalink

    Nice tutorial Charles. One observation though, I am confused as to what the logic is in the javascript to determine if the requester_data was not found, so then it can display the error_data.


    So basically even if you did not comment out the showInfo(); function call it would still display the error message, as far as I have tested.

    0
  • Charles Nadeau
    Comment actions Permalink

    Hi Haroon, 


    This tutorial only describes how to display the info and error templates. The next tutorial describes the logic that determines what template to display -- info or error? See https://help.zendesk.com/hc/en-us/articles/229489148.

    0
  • David
    Comment actions Permalink

    The showInfo() and showError() functions are not displaying in my App. I've deleted the code and even copied it exactly from the tutorial twice and it still isn't working. Here is my code. Only the App Title, Logo and Report Bug link are showing. 



    main.js

    function() {
    var client = ZAFClient.init();
    client.invoke('resize', { width: '100%', height: '20px' });
    showInfo();
    showError();
    };



    function showInfo() {
    var requester_data = {
    'name': 'Jane Doe',
    'tags': ['tag1', 'tag2'],
    'created_at': 'November 20, 2014',
    'last_login_at': 'June 27, 2016'
    };


    var source = $("#requester-template").html();
    var template = Handlebars.compile(source);
    var html = template(requester_data);
    $("#content").html(html);
    }


    function showError() {
    var error_data = {
    'status': 404,
    'statusText': 'Not found'
    };
    var source = $("#error-template").html();
    var template = Handlebars.compile(source);
    var html = template(error_data);
    $("#content").html(html);
    }







    main.css


    .data {
    color: #363;
    padding-left: 6px;
    }


    #content td {
    font-size: 12px;
    padding-top: 2px;
    }







    iframe.html

    <html>
    <head>
    <meta charset="utf-8">



    <link href="https://cdn.jsdelivr.net/bootstrap/2.3.2/css/bootstrap.min.css" rel="stylesheet">
    <link href="main.css" rel="stylesheet">
    </head>
    <body>
    <div id="content"></div>



    <footer>
    <a href="https://mysite.github.io/support" target="_blank">Report bugs</a>
    </footer>
    <script id="requester-template" type="text/x-handlebars-template">
    <table>
    <tr>
    <td>Customer:</td>
    <td class="data">{{name}}</td>
    </tr>
    {{#if tags}}
    <tr>
    <td>Tags:</td>
    <td class="data">{{#each tags}}{{this}} {{/each}}</td>
    </tr>
    {{/if}}
    <tr>
    <td>Added:</td>
    <td class="data">{{created_at}}</td>
    </tr>
    <tr>
    <td>Last signed in:</td>
    <td class="data">{{last_login_at}}</td>
    </tr>
    </table>
    </script>


    <script id="error-template" type="text/x-handlebars-template">
    <p>{{status}} - {{statusText}} error. Please report a bug at the link below.</p>
    </script>

    <script src="https://cdn.jsdelivr.net/handlebarsjs/4.0.8/handlebars.min.js"></script>
    <script src="https://cdn.jsdelivr.net/jquery/3.0.0/jquery.min.js"></script>
    <script type="text/javascript" src="https://assets.zendesk.com/apps/sdk/2.0/zaf_sdk.js"></script>
    <script type="text/javascript" src="main.js"></script>


    </body>
    </html>

    0
  • Charles Nadeau
    Comment actions Permalink

    Hey David,


    Code looks okay, though I notice you set the height of your app to 20px:


    client.invoke('resize', { width: '100%', height: '20px' });

    Try 120 or 160.


    Also make sure to selectively comment out the show functions to test each one separately. Example:


    $(function() {
    
    var client = ZAFClient.init();
    client.invoke('resize', { width: '100%', height: '120px' });
    // showInfo();
    showError();
    });

    Hope this helps.


     

    0
  • Krzy Hamer
    Comment actions Permalink

    Hi i want to use zendesk garden css.
    I get stylesheet directly from:
    https://assets.zendesk.com/apps/sdk-assets/css/2/zendesk_garden.css

    But not all css showcased on https://garden.zendesk.com/css-components/tags/ is included - for eg. tags are missing.

    This file is official and should have all the css classes available on zendesk garden docs page, right?

    0
  • Charles Nadeau
    Comment actions Permalink

    Hi Krzy,

    All the Zendesk Garden CSS classes (and now React components) are now available on JSDelivr and npm.

    See https://www.jsdelivr.com/?query=zendeskgarden. The jsDelivr files are npm packages that can also be installed from npm. See https://www.npmjs.com/search?q=zendeskgarden.

    I'll add the latest info to this article.

    Thanks.

    Update: Added it to the "Adding styles" section in the second part of the series - https://develop.zendesk.com/hc/en-us/articles/360001074808#topic_zsq_g3d_kw

     

    0
  • Madhav Attili
    Comment actions Permalink

    Hi All,

    Even I am facing this issue, the app isn't executing the ShowInfo() in my case its display();

    I have tried in incognito and even in multiple browsers

    here's  my code

     

    main.js

    <script>
      // Initialise the Zendesk JavaScript API client
      // https://developer.zendesk.com/apps/docs/apps-v2
      $(function() {
        var client = ZAFClient.init();
        client.invoke('resize', { width: '100%', height: '160px' });
        display();
      });

      function display() {

        var req_data = {
          'name' : 'Yadava1'
          'tags' : ['co-admin','support']
          'created_at' : 'Nov 26,2018'
          'last_login_at' : 'Just now'
        };

        var scr = $(#requester-template).html();
        var template = Handlebars.compile(scr);
        var html = template(req_data);
        $("#content").html(html);

      }

    </script>

     

    iframe.html

    <html>
    <head>
      <meta charset="utf-8">
      <!-- http://garden.zendesk.com -->
      <!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@zendeskgarden/css-bedrock@7.0" type="text/css"> -->



    <link href="https://cdn.jsdelivr.net/bootstrap/2.3.2/css/bootstrap.min.css" rel="stylesheet">
    <link href="main.css" rel="stylesheet">



    </head>
    <body>
      <div id= "content">

      </div>
      <footer>
      <a href="https://mysite.github.io/support" target="_blank">Report bugs</a>
    </footer>

    <script id="requester-template" type="text/x-handlebars-template">
        <table>
        <tr>
          <td>Customer:</td>
          <td class="data">{{name}}</td>
        </tr>
        {{#if tags}}
        <tr>
          <td>Tags:</td>
          <td class="data">{{#each tags}}{{this}} {{/each}}</td>
        </tr>
      {{/if}}
        <tr>
          <td>Added:</td>
          <td class  = "data">{{created_at}}</td>
        </tr>
        <tr>
          <td>Last signed in:</td>
          <td class = "data">{{last_login_at}}</td>
        </tr>
        </table>
    </script>
      <script src="https://cdn.jsdelivr.net/jquery/3.2.1/jquery.min.js"></script>


      <script src="https://cdn.jsdelivr.net/handlebarsjs/4.0.8/handlebars.min.js"></script>
    <script type="text/javascript" src="https://assets.zendesk.com/apps/sdk/2.0/zaf_sdk.js"></script>
      <script type="text/javascript" src="main.js"></script>



    </body>
    </html>

     

    main.css

     

    footer {
      font-size: 15px;
      padding-top: 20px;
    }
    .data{
      color: red;
      padding-left: 6px;
    }
    #content td {
        font-size: 12px;
        padding-top: 2px;
    }

     

    Let me know if I have some glitch in this

    0
  • Bryan - Community Manager
    Comment actions Permalink

    Hi Madhav. You have a number of syntax errors in main.js that need fixing:

    1. main.js is a JavaScript file, so even though you're including it via an HTML file, it shouldn't have HTML <script> tags

    2.  var scr = $(#requester-template).html(); is referencing an element, so #requester-template needs to be in quotes

    3. JavaScript objects need to have their attributes separated by commas, so look at the req_data object and see how it's defined

    The final test code you have should look something like this:

    $(function() {
      var client = ZAFClient.init();
      client.invoke('resize', {
        width: '100%',
        height: '160px'
      });
      display();
    });

    function display() {

      var req_data = {
        'name': 'Yadava1',
        'tags': ['co-admin', 'support'],
        'created_at': 'Nov 26,2018',
        'last_login_at': 'Just now'
      };

      var scr = $("#requester-template").html();
      var template = Handlebars.compile(scr);
      var html = template(req_data);
      $("#content").html(html);

    }

    Hope this helps!

    0
  • ATA
    Comment actions Permalink

    I am having a problem getting the javascript functions to work, and also for the stylesheet to be used.  I get the following in the Chrome console:

    Refused to apply style from 'http://localhost:4567/main.css' because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled.


    GET http://localhost:4567/main.js net::ERR_ABORTED 404 (Not Found)

    Both files are in the same folder as iframe.html, so why won't those URLs work?

    0
  • Pat
    Comment actions Permalink

    Hello, I'm following this tutorial. But I can't get it work.

    It shows only the footer. Error from web console says: TypeError: client.invoke is not a function

    iframe.html

    <html>
    <head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
    <link href="main.css" rel="stylesheet">
    <link rel="shortcut icon" href="">
    </head>
    <body>


    <div id="content"></div>


    <footer>
    <a href="https://mysite.github.io/support" target="_blank">Report bugs</a>
    </footer>
    <script id="requester-template" type="text/x-handlebars-template">
    <table>
    <tr>
    <td>Customer:</td>
    <td class="data">{{name}}</td>
    </tr>
    {{#if tags}}
    <tr>
    <td>Tags:</td>
    <td class="data">{{#each tags}}{{this}} {{/each}}</td>
    </tr>
    {{/if}}
    <tr>
    <td>Added:</td>
    <td class="data">{{created_at}}</td>
    </tr>
    <tr>
    <td>Last signed in:</td>
    <td class="data">{{last_login_at}}</td>
    </tr>
    </table>
    </script>


    <script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.1.0/handlebars.min.js"></script>
    <script src="https://cdn.jsdelivr.net/jquery/3.2.1/jquery.min.js"></script>
    <script type="text/javascript" src="https://assets.zendesk.com/apps/sdk/2.0/zaf_sdk.js"></script>
    <script type = "text/javascript" src="main.js"></script>
    </body>
    </html>

     

    main.js



    $(function() {
    var client = ZAFClient.init();
    client.invoke('resize', { width: '100%', height: '160px' });
    showInfo();
    });

    function showInfo() {

    var req_data = {
    'name': 'Yadava1',
    'tags': ['co-admin', 'support'],
    'created_at': 'Nov 26,2018',
    'last_login_at': 'Just now'
    };

    var scr = $("#requester-template").html();
    var template = Handlebars.compile(scr);
    var html = template(req_data);
    $("#content").html(html);

    }
    1
  • Yonatan Becker
    Comment actions Permalink

    Hi,

    I am also getting the issue where it only shows the footer,  is it possible to add the complete text for each file so that we can compare our own files to them?

    0
  • Amie B
    Comment actions Permalink

    Hi There,

    I'm a complete novice at this and it's my first attempt so please be kind. So far I've been able to follow the guides successfully, however I've now come to a sticking point because the guide isn't very clear.

    I've attached a screenshot of where i'm up too as per the highlight in the image.

    It makes no sense to me. "Add your new showInfo() function after the anonymous function" - does that mean I just paste this text under the self invoking function from the step 2 above as Pat has done in his code from 2 comments ago above?? or does it go in a different file? it's not very clear. 

    Can you please confirm?

     

     

    I would also like to request that you show the complete text for each file or have them avail as a download so we can compare our own files against them to find where we went wrong (if we did) etc. It would be super helpful. :)

    0
  • Charles Nadeau
    Comment actions Permalink

    Yes, it goes after the self-invoking function, which is anonymous because it's not named. I agree it's confusing so I replaced all instances of "anonymous function" with "self-invoking function" to be consistent.

    Making the app's completed source code available is on our list of things to do.

    0
  • Aunggoon Klongpithak
    Comment actions Permalink

    Hey I would like to start design the wireframe design using XD or Sketch can the team provide me the File for style guide?

    0
  • Tom Harper
    Comment actions Permalink

    Hello

    it seems the link to JQuery in the example iframe.html file:

    https://cdn.jsdelivr.net/jquery/3.2.1/jquery.min.js

    is no longer valid.

    I used this one instead, which works fine:

    https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js

     

    0
  • Devan
    Comment actions Permalink

    Hello Tom,

    Thanks for providing the updated link! :D

    0
  • Fernando
    Comment actions Permalink

    Truly awesome, step-by-step tutorial that should be updated. 

    0
  • Charles Nadeau
    Comment actions Permalink

    @Fernando, I updated the tutorial for 2019! That's the clean fresh smell you might have noticed here.

    1
  • Charles Nadeau
    Comment actions Permalink

    @Amie B The completed source code of the app in this tutorial is available for download in Part 1 of the tutorial. The download link is xr_app.zip.

    0
  • David Andrews
    Comment actions Permalink

    I've been trying to follow this demo. I am running Zat 3.4.0. I use the ZAT server command and append zat=true while viewing a ticket in Chrome & use incognito windows or clear caches. The only thing that renders is the new image. The 'Hello World' in the h2 element did not render either.

    Any suggestions

    Here's my iframe.html:

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" crossorigin="anonymous">
    <link href="main.css" rel="stylesheet">
    </head>
    <body>
    <script id="requester-template" type="text/x-handlebars-template">
    <table>
    <tr>
    <td>Customer:</td>
    <td class="data">{{name}}</td>
    </tr>
    {{#if tags}}
    <tr>
    <td>Tags:</td>
    <td class="data">{{#each tags}}{{this}} {{/each}}</td>
    </tr>
    {{/if}}
    <tr>
    <td>Added:</td>
    <td class="data">{{created_at}}</td>
    </tr>
    <tr>
    <td>Last signed in:</td>
    <td class="data">{{last_login_at}}</td>
    </tr>
    </table>
    </script>
    <script src="https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.min.js"></script>
    <script src="https://static.zdassets.com/zendesk_app_framework_sdk/2.0/zaf_sdk.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/handlebars@4.3.3/dist/handlebars.min.js"></script>
    <script src="main.js"></script>
    <div id="content"></div>
    <footer>
    <a href="https://mysite.github.io/support" target="_blank">Report bugs</a>
    </footer>
    </body>
    </html>

    Here is my main.js:

    $(function() {
    var client = ZAFClient.init();
    client.invoke('resize', { width: '100%', height: '120px' });
    showInfo();
    });

    function showInfo() {
    var requester_data = {
    'name': 'Jane Doe',
    'tags': ['tag1', 'tag2'],
    'created_at': 'November 20, 2014',
    'last_login_at': 'June 27, 2016'
    };

    var source = $("#requester-template").html();
    var template = Handlebars.compile(source);
    var html = template(requester_data);
    $('#content').html(html);
    };

    Here is my main.css:

    footer {
    font-size: 10px;
    padding-top: 20px;
    }
    .data {
    color: #363;
    padding-left: 6px;
    }
    #content td {
    font-size: 12px;
    padding-top: 2px;
    }

    0
  • Charles Nadeau
    Comment actions Permalink

    Hi David,

    You don't specifically mention it but did you enable "Load Unsafe Content" in Chrome?

    https://develop.zendesk.com/hc/en-us/articles/360001074808#topic_egv_qv4_lw

     

    0
  • David Andrews
    Comment actions Permalink

    Hi Charles,

    Thanks for the prompt reply. I do not believe that scripts are being blocked given that there is no shield. Let me know if I missed something . . . 

     

    0
  • Bryan - Community Manager
    Comment actions Permalink

    That's suspicious. Are you running 'zat server' from the command line in your project's root directory (the directory with the manifest.json file)?

    'zat server' is a local Sinatra based web server that serves up your app via localhost. This is only for testing and is helpful in avoiding having to upload the packaged app (using 'zat package') to your Zendesk instance every time you have a change that you want to test.

    0
  • David Andrews
    Comment actions Permalink

    Yep. Here's what the window looks like when opening. It recognized that I changed the images, but the initial h2 tag didn't display and nothing I added seems to display. 

    0
  • Bryan - Community Manager
    Comment actions Permalink

    This should work. Just to test, I copy/pasted the exact files you had above into a new app and it worked as expected.

    When you go to select "Load Unsafe Scripts" it should look like this:

    Once "Load Unsafe Scripts" is selected, you should see the app working and this in your Chrome/browser:

    If you don't see the above, submitting a private ticket may be the next best step, so we can look at your particular account and instance.

    0
  • David Andrews
    Comment actions Permalink

    I am aware of the warning and did not receive it. I just wanted to confirm that I didn't miss anything since I find Chrome to be a CPU hog. 

    Submitting a private ticket for a developer question through normal ticketing is a horrible experience and does not appear to be geared for developers.

    I am in the slack dev chat . . . Who would you recommend I reach out to there?

    0
  • Bryan - Community Manager
    Comment actions Permalink

    I'll start a ticket David and we can discuss through that.

    0
  • Roman Belousov
    Comment actions Permalink

    Hi, how do I return the value of a custom field using the Handlebar template documented here?

    0
  • Azhar
    Comment actions Permalink

    Hi,

    I have used you xr_apps code to create a test app in zendesk appending ticket info along with user info. I have modified the iframe.html and main.js for the purpose. I am able to display the customer informations on the side bar but the ticket information is not retrieved and displayed. Can you please look into my main.js and point out if I am making any mistake.

    main.js :

    $(function() {
    var client = ZAFClient.init();
    client.invoke('resize', { width: '100%', height: '120px' });

    client.get('ticket.requester.id').then(
    function(data) {
    var user_id = data['ticket.requester.id'];
    requestUserInfo(client, user_id);
    }
    );

    client.get('ticket.id').then(function(data) {
    var ticket_id = data['ticket.id'];
    ticketInfo(client,ticket_id);
    });

    });


    function requestUserInfo(client, id) {
    var settings = {
    url: '/api/v2/users/' + id + '.json',
    type:'GET',
    dataType: 'json',
    };
    client.request(settings).then(
    function(data) {
    showInfo(data);
    },
    function(response) {
    showError(response);
    }
    );
    }

    function ticketInfo(client, id) {
    var settings = {
    url: '/api/v2/tickets/' + id + '.json',
    type:'GET',
    dataType: 'json',
    };
    client.request(settings).then(
    function(data) {
    showTktInfo(data);
    },
    function(response) {
    showError(response);
    }
    );
    }


    function showInfo(data) {
    var requester_data = {
    'name': data.user.name,
    'tags': data.user.tags,
    'created_at': formatDate(data.user.created_at),
    'last_login_at': formatDate(data.user.last_login_at)
    };
    var source = $("#requester-template").html();
    var template = Handlebars.compile(source);
    var html = template(requester_data);
    $("#content").html(html);
    }

    function showTktInfo(data) {
    var ticket_data = {
    'ticket_id': data.ticket.id,
    'ticket_title': data.ticket.subject
    };
    var source = $("#requester-template").html();
    var template = Handlebars.compile(source);
    var html = template(ticket_data);
    $("#content").html(html);
    }


    function showError(response) {
    var error_data = {
    'status': response.status,
    'statusText': response.statusText
    };
    var source = $("#error-template").html();
    var template = Handlebars.compile(source);
    var html = template(error_data);
    $("#content").html(html);
    }


    function formatDate(date) {
    var cdate = new Date(date);
    var options = {
    year: "numeric",
    month: "short",
    day: "numeric"
    };
    date = cdate.toLocaleDateString("en-us", options);
    return date;
    }

     

     

    iframe.html : 

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" crossorigin="anonymous">
    <link href="main.css" rel="stylesheet">
    </head>
    <body>

    <div id="content"></div>


    <h2 class="u-semibold u-fs-xl" id="H2">Hello World !!</h2>
    <br></br>

    <button type="button" onclick='document.getElementById("H2").innerHTML = "Hello JavaScript!"'>Click to change Heading </button>

    <footer>
    <a href="https://mysite.github.io/support" target="_blank">Report bugs</a>
    </footer>

    <script id="requester-template" type="text/x-handlebars-template">
    <table>
    <tr>
    <td>Customer:</td>
    <td class="data">{{name}}</td>
    </tr>
    {{#if tags}}
    <tr>
    <td>Tags:</td>
    <td class="data">{{#each tags}}{{this}} {{/each}}</td>
    </tr>
    {{/if}}
    <tr>
    <td>Added:</td>
    <td class="data">{{created_at}}</td>
    </tr>
    <tr>
    <td>Last signed in:</td>
    <td class="data">{{last_login_at}}</td>
    </tr>
    <tr>
    <td>Ticket Id:</td>
    <td class="data">{{ticket_id}}</td>
    </tr>
    <tr>
    <td>Ticket Title:</td>
    <td class="data">{{ticket_title}}</td>
    </tr>

    </table>
    </script>

    <script id="error-template" type="text/x-handlebars-template">
    <p>{{status}} - {{statusText}} error. Please report a bug at the link below.</p>
    </script>

    <script src="https://cdn.jsdelivr.net/npm/handlebars@4.3.3/dist/handlebars.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.min.js"></script>
    <script src="https://static.zdassets.com/zendesk_app_framework_sdk/2.0/zaf_sdk.min.js"></script>
    <script src="main.js"></script>
    </body>
    </html>

    0
  • Bryan - Community Manager
    Comment actions Permalink

    Hello Azhar. This isn't the best forum to post and debug custom code. Questions should be focused on specific questions related to the article's posted code.

    That said, your code has two primary issues:

    1. The nature of JavaScript Promises is that they need to be "returned" to be chained together. This is how asynchronous programming is accomplished. Without the client calls being chained together, they may run out of order and step on expected results.

    The code should look something more like the below, where the JavaScript Promise/client calls are chained together by "returning" them and using "then":

    ...

    return client.get('ticket.requester.id').then((data) => {
    var user_id = data['ticket.requester.id'];
    return requestUserInfo(client, user_id).then((data) => {
    return client.get('ticket.id').then((data) => {
    var ticket_id = data['ticket.id'];
    ticketInfo(client, ticket_id);
    });
    })
    }
    );

    ...

    function requestUserInfo(client, id) {
    var settings = {
    url: '/api/v2/users/' + id + '.json',
    type: 'GET',
    dataType: 'json',
    };
    return client.request(settings).then(
    function(data) {
    showInfo(data);
    },
    function(response) {
    showError(response);
    }
    );
    }

    function ticketInfo(client, id) {
    var settings = {
    url: '/api/v2/tickets/' + id + '.json',
    type: 'GET',
    dataType: 'json',
    };
    return client.request(settings).then(
    function(data) {
    showTktInfo(data);
    },
    function(response) {
    showError(response);
    }
    );
    }

    2. That's still not enough to get your code working, however, as you're filling the template twice -- once in showInfo, then filling the template again in showTktInfo. You need to fill the template just once otherwise the call to showTktInfo will always be the last one run, overwriting what was displayed in showInfo.

    The custom code and logic needs to be debugged on your end, so to meet your requirements. Hopefully the above helps figure out what your next steps should be. In addition, check out this article. It talks about how JavaScript Promises work in the context of Zendesk ZAFClient calls:

    JavaScript Promises — a Zendesk Introduction

    0

Please sign in to leave a comment.

Powered by Zendesk