I'm creating a Stored Process that iterates multiple times through a data set and shows different subsets of it via Proc Print. I'd like to have those Proc Prints be able to expanded and hidden. (They could be quite large and the users won't want to scroll through them all the time.) I've got HTML code that does the hiding/expanding but including it via file _webout in my Stored Process is resulting in the "maximum level of nesting of macro functions exceeded" error.
So my method for displaying a mixture of HTML and SAS ODS results has a hard cap of 10 HTML blocks. I'm therefore wondering if there is a better way of doing this?
Below is SAS code of what I'm trying to do using the SASHELP Baseball library (as I can't put my actual code on here):
data _null_;
file _webout;
put ' <HTML>';
put ' <HEAD><TITLE>Expandable Blocks Test 1</TITLE></HEAD>';
put ' <BODY><H1>Expandable Blocks Test 1</H1>';
put ' <p>Beginning of test</P>';
put ' </BODY></HTML>';
put ' <style>';
put ' .accordion { background-color: #eee; color: #444; cursor: pointer; padding: 18px; width: 100%; border: none; text-align: center; outline: none; font-size: 18px; transition: 0.4s; }';
put ' .active, .accordion:hover { background-color: #ccc; }';
put ' .panel { padding: 0 18px; display: none; background-color: white; overflow: hidden;}';
put ' </style>';
run;
%macro ListPlayers(team);
Title1 "<H1>This <i>is </i> &team !</H1>";
%STPEND;
data _null_;
file _webout;
put '<button class="accordion">Team List</button><div class="panel">';
run;
%STPBEGIN;
proc print data=sashelp.Baseball;
where team="&team";
run;
%STPEND;
data _null_;
file _webout;
put '</div>';
run;
%STPBEGIN;
%mend;
%macro LoopTeams;
proc sql;
Select distinct(Team)
into :TeamName1-
From sashelp.baseball
Order by Team Asc;
quit;
%local i;
%do i=1 %to &sqlobs;
%ListPlayers(&&TeamName&i);
%end;
%mend;
%STPBEGIN;
%LoopTeams;
%STPEND;
;*';*";*/;quit;
data _null_;
file _webout;
put' <script>';
put' var acc = document.getElementsByClassName("accordion");';
put' var i;';
put' for (i = 0; i < acc.length; i++) {';
put' acc[i].addEventListener("click", function() {';
put' this.classList.toggle("active");';
put' var panel = this.nextElementSibling;';
put' if (panel.style.display === "block") {';
put' panel.style.display = "none";';
put' } else {';
put' panel.style.display = "block";';
put' }';
put' });';
put' }';
put' </script>';
run;
If anyone can help that would be great. Thanks.
Yes indeed - the 'better way' would be to seperate style and content (frontend and backend) by putting your HTML / Javascript directly on the SAS Web Server (eg htdocs) and calling SAS Stored Processes via AJAX to retrieve just the data you need, when you need it.
This approach is outlined here: https://www.rawsas.com/building-web-apps-with-sas/
Registration is open! SAS is returning to Vegas for an AI and analytics experience like no other! Whether you're an executive, manager, end user or SAS partner, SAS Innovate is designed for everyone on your team. Register for just $495 by 12/31/2023.
If you are interested in speaking, there is still time to submit a session idea. More details are posted on the website.
Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.