Forums

How to hide/show the content of area views in CFv7 ?

shiluba 28 Jan, 2022
Hi,
With CFv6 I can hide/display what's inside the body of any Area view.
For now, I can not do it in CFv7. Nothing happens when I click on the "body" label. Did I miss something ?
I have forms containing many views that I am trying to migrate to CFv7. But they would be too difficult to edit if all their views are always displayed.
Regards,
JGLamber 30 Jul, 2022
Indeed this is a big problem if you have many views. I have the same problem and It is very difficult to work with a lot of fields. Any answer ?
shiluba 31 Jul, 2022
No answer on my side unfortunately. Now in CF7 only a "Page" or a "Page group" can be Minimized/Maximized. There is still no way to hide the content of areas the way it worked with CF6, neither the Views (hide/show the "Body" of Area views, conditions of "Switcher areas", etc.), nor the Actions (hide/show the content of most action types : Conditions, Event Switchers, Display layout, Found/Not found / Fail, etc.).
Plus, in CF7, the Views and Actions Editors are always displayed whereas CF6 used to keep them in separate tabs. Even the "Basic Articles List" demo is already quite long.
As long as everything is always displayed, I can not use CF7 for recreating my big CF6/CC6 applications. It just too confusing scrolling that long pages.
Now that there is less than one year left for using Joomla 3 safely and still no CF6/CF7 importer, there is not much time left for completely recreating huge projects.
The only option I see to continue using CF for these applications is that CF-CC6 becomes Joomla 4 compatible.
Hide/show in CF7, CF6/CF7 importer, CE6 for J4 ... hopefully one of these solutions will be available soon.
webbusteruk 18 Aug, 2022
If you're comfortable with doing javascript/jQuery and your backend template allows it, it could easily be done using your own scripts.
shiluba 19 Aug, 2022
Hi webbusteruk. Thank you for your response.
I might be comfortable enough with JavaScript/JQuery for hiding the content of all these Areas & Actions automatically and displaying them only after I click their "body" label, just by using the backend template scripts. But I do not see how plain JS/JQuery could remember the set of boxes that must be displayed/hidden so that we do not have to re-open them each time the form editing page is loaded.
That's essential because having all the Fields & Actions hidden at each page loading would be counter-productive.

I do not know how CF6 manages to remember the sets of hidden/displayed fields. It must be AJAX with some DB saving because CF6 remembers them even after closing the browser. I would not know how to do that from a backend template override.

Another JS/JQuery may be useful too : collapsing the Views block when the Actions block is used, and vice-versa.
webbusteruk 19 Aug, 2022
I suppose for convenience sake we could hide them all until we open them.

One method we could use is to save the settings to a local cookie on your device, so it remembers which one is opened? This should be doable with JavaScript/jQuery.

I could try to conjure up some script if needed (free time permitting).
shiluba 19 Aug, 2022
Indeed a cookie might do the trick. I could try and help coding the script (free time permitting too).
I'll keep you updated if I start coding something.
shiluba 20 Aug, 2022
From a user.css file, using this simple style ...
.chronoforms .drop_disabled { max-height:150px; overflow: hidden;}
... makes the dimmed Actions/Views blocks use less room.
webbusteruk 22 Aug, 2022
jQuery:
$(".com_chronoforms7 .ui.segment.unit_area .ui.label.ribbon").click(function() {
$(this).parent(".ui.segment.unit_area").toggleClass("open");
});
CSS:
.ui.segment.unit_area {
height:30px;
min-height:30px !important;
overflow:hidden;
}

.ui.segment.unit_area.open {
height:inherit;
overflow:inherit;
}

.ui.segment.unit_area .ui.label.ribbon {
cursor: pointer;
}

Working on the cookie next. Basically each "body" (which has class unit_area) has a unique attribute "data-value", which we can use to differentiate between the different "body". Using this we can store the open/close state for each "body" in the cookie.
shiluba 22 Aug, 2022
@webbusteruk, that's neat. Thank you very much.
shiluba 23 Aug, 2022
1 Likes
We're moving forward ! Here's how I DB load & save the open/hidden areas.
jQuery:
/* updated code here below */

PHP (I use it as my 1st system plugin. Maybe it can work elsewhere too.)
/* updated code here below */

shiluba 24 Aug, 2022
QUERY
Give your "...toggle_area" table the following PRIMARY KEY (cfid,count,areakey);

(Sorry, with this editor, I can't find a way to post the whole query code, or a zip file, or even a simple screenshot.)

There is still an issue with the nested containers. I'll see if I can fix that later.
Cheers
webbusteruk 25 Aug, 2022
Thanks shiluba, I've stolen part of your code. Here's an alternative by storing it in a cookie instead (so different users will have their own saved "settings").
​[pre]function createCookie(name,value,days) {
if (days) {
var date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
var expires = "; expires="+date.toGMTString();
}[br] else var expires = "";[br] document.cookie = name+"="+value+expires+"; path=/";[br]}[br][br]function readCookie(name) {[br] var nameEQ = name + "=";[br] var ca = document.cookie.split(';');[br] for(var i=0;i < ca.length;i++) {[br] var c = ca[i];[br] while (c.charAt(0)==' ') c = c.substring(1,c.length);[br] if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);[br] }[br] return null;[br]}[/pre]
webbusteruk 25 Aug, 2022
const urlParams = new URLSearchParams(window.location.search);
const cfid = urlParams.get('id');
const option = urlParams.get('option');
const act = urlParams.get('act');
if (option !== 'com_chronoforms7' || act !== 'edit'){ /* not loaded outside of CF7 editing */
return false;
} else {
var cookieName = "toggle-open" + cfid;
$(".com_chronoforms7 .ui.segment.unit_area .ui.label.ribbon").click(function() {
$(this).parent(".ui.segment.unit_area").toggleClass("open");
var cfcookie = [];
$(".ui.segment.unit_area.open").each(function(index,item){
var id = $(item).data('count');
cfcookie.push(id);
});
createCookie(cookieName,cfcookie,1)
});
if (opened) {
var opened = readCookie(cookieName).split(',');
for(var j=0;j < opened.length;j++) {
$(".ui.segment.unit_area[data-count="+ opened[j] +"]").addClass("open");
}
}
}
It's not the best/cleanest code, but it works for now.
shiluba 26 Aug, 2022
Thank you @webbusteruk,
Indeed your JQuery can be useful to different users willing to have their own settings. In case someone wants to mix the advantages of the 2 approaches, it's doable by adding a user_id column to the DB table.

Your JQuery uses the "data-count" attribute for identifying each "unit_area". In the CF code, the parent div (the one with class="ui segment etc.") has a "data-count" too, with the same value. (I added the class "unit_container" to this parent div so as to target it more easily, both for the Views and for the Actions.)
But some unit_container types can have more than one "unit_area" sharing the same "data-count" (e.g. in the "Basic Articles List" demo, the "Table list" has several unit_areas having the same data-count value : "Article.ID", "Article.title", etc.). In that case, using the "unit_area" attribute is not enough for identifying each unit_area. When you try and show only some, it displays them all.
Fortunately the "data-areakey" attribute can be used to differentiate these "unit_areas" (data-areakey="0 , 1, 2, 3, ..." ). By default, a "unit_area" that's placed into a basic unit_container has data-areakey="0".

Same thing in the "Actions editor". A unit_container like "Save Data" or "Read Data" has several areas sharing its "data-count" value. Thus, using this attribute is not enough for distinguishing unit_areas such as "Success/Fail", "Found/Not Found", etc.
Here again, we can target them separately thanks to their "data-areakey" which is unique within the unit_container.

For now, what my JQuery can not do is to target the "unit_areas" that are place into nested unit_containers. Fixing that issue will take more time. Anyone's help will be appreciated !🙂
Regards,
webbusteruk 26 Aug, 2022
Hi shiluba

I see what you mean, I wasn't capturing those areas as well (only the ones with "body" in the Views Repo). I'll try to have a look.

I see a problem in that if we place a new unit_area into the repo, the jQuery doesn't apply to it (until the page is reloaded). This might be more tricky than I assume ..

If my assumption is right, Max used jQuery UI Draggable for the drag and drop. In that case, it might be possible to listen for the "drop" events and apply the open/close jQuery to the new unit_area.
shiluba 26 Aug, 2022
Here is an update of the the AJAX version which uses the user id and the loading order of the dom elements. When following this proper order, the open/close (1/0) status of the child areas no longer inherit from the 1/0 status of the parent areas.

JQuery[pre]const urlParams = new URLSearchParams(window.location.search);
const cfid = urlParams.get('id');
const option = urlParams.get('option');
const act = urlParams.get('act');[br]if (option !== 'com_chronoforms7' || act !== 'edit'){return false;} /* Don't load this js file outside of CF7 editing */[br]$(".unit_area").each( function() { /* help identifying the unit_containers */[br] $(this).closest(".ui.segment.viewsList").addClass("unit_container");[br] $(this).closest(".ui.segment.functionsList").addClass("unit_container");[br]});[br]$(".com_chronoforms7 .ui.segment.unit_area .ui.label.ribbon").click(function() {[br] $(this).parent(".ui.segment.unit_area").toggleClass("open");[br] db_save();[br]});[br][br]function db_save (){[br] const unit_areas = {};[br] var uid = $("#uid").val();[br] var segs = $('.unit_container').map(function () { /* Get an array of the segments containing unit_areas */[br] return [$(this).attr("data-count")];[br] }).toArray();[br] $.each( segs, function( skey, data_count ) { /* For each segment, add a subobject, loop through its unit_areas */[br] unit_areas[skey]={};[br] unit_areas[skey][data_count]={};[br] $(".unit_container[data-count='"+data_count+"'] .unit_area[data-count='"+data_count+"']").each( function() { [br] if ($(this).hasClass("open")){var isopen=1;}else{var isopen=0;}[br] unit_areas[skey][data_count][$(this).attr("data-areakey")]=isopen;[br] });[br] });[br] var json_unit_areas = JSON.stringify(unit_areas); /* Save JSON array to DB */[br] $.ajax({ [br] type: "POST", [br] url: "../index.php?run=chrono_plus&tmpl=component&cfid="+cfid, [br] data: {unit_areas: json_unit_areas, uid: uid }[br] });[br] }[/pre]
MYSQL[br][br]PRIMARY KEY (`uid`,`cfid`,`count`,`areakey`)[br]
shiluba 26 Aug, 2022
PHP
Same structure as above :
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
$input = Factory::getApplication()->input;
$db = JFactory::getDbo();
$user = Factory::getUser();
/* When a form editing page is loaded */
if ($input->get('option', '', 'string') == 'com_chronoforms7' && $input->get('act', '', 'string') == 'edit'){
$cfid=$input->get('id', '', 'string');
$query = "SELECT * FROM #__chronog3_toggle_area WHERE uid = $user->id AND cfid = $cfid ORDER BY sort ASC";
$db->setQuery($query);
$areas = $db->loadAssocList();
$js='jQuery(document).ready(function($) {
$("#header").append(\'\');';
foreach ($areas as $key => $area){
if($area['open']==1){$js.='$(".unit_container[data-count='.$area['count'].'] .unit_area[data-areakey='.$area['areakey'].']").addClass("open"); ';}
else{$js.='$(".unit_container[data-count='.$area['count'].'] .unit_area[data-areakey='.$area['areakey'].']").removeClass("open"); ';}
}
$js.='});';
$document = JFactory::getDocument();
$document->addScriptDeclaration($js);
}
in the append section, please put a hidden input with id = " uid " and value = $ user - > i d
For obvious security reasons, the editor does not want it here.
Then ...
/* When an area gets hidden/closed */
if ($input->get('run', '', 'string') == 'chrono_plus'){
$cfid=$input->get('cfid', '', 'string');
$uid = $input->post->get("uid", '', 'string');
$unit_areas = json_decode($input->post->get("unit_areas", '', 'string'),true);
foreach ($unit_areas as $skey => $areas ){
foreach ($areas as $count => $area ){
foreach ($area as $areakey => $open ){
$query = "INSERT INTO #__chronog3_toggle_area (uid,sort,cfid,count,areakey,open) VALUES ($uid,$skey,$cfid,$count,$areakey,$open) ON DUPLICATE KEY UPDATE open = $open, sort = $skey";
$db->setQuery($query)->execute();
}
}
}
jexit();
}
Sorry for the series of nested foreach constructs. If someone sees a neater way to do it, please share it !
You need to login to be able to post a reply.