Skip Navigation

[Resolved] Hide Parent View if Child View is Empty

This thread is resolved. Here is a description of the problem and solution.

Problem:
Client has a parent View which iterates over some posts and includes nested Views in its output. If none of the nested Views have any results, the parent View should not output anything for that iteration.

Solution:
Set up the parent View with CSS so that for each iteration nothing is displayed.

Then add JS to the nested Views in the wpv-items-found section but outside of the wpv-loop tags which sets the display property of the parent to 'block' so that the content becomes visible.

The parent View can generate a wrapper for each iteration with a unique class name, like so:

<div class="results-container-[wpv-post-id]">

The nested Views can then target that unique class name like so:

jQuery('.results-container-[wpv-post-id id="$parent-slug"]').css('display', 'block');
This support ticket is created 6 years, 8 months ago. There's a good chance that you are reading advice that it now obsolete.

This is the technical support forum for Toolset - a suite of plugins for developing WordPress sites without writing PHP.

Everyone can read this forum, but only Toolset clients can post in it. Toolset support works 6 days per week, 19 hours per day.

Sun Mon Tue Wed Thu Fri Sat
- 7:00 – 14:00 7:00 – 14:00 7:00 – 14:00 7:00 – 14:00 7:00 – 14:00 -
- 15:00 – 16:00 15:00 – 16:00 15:00 – 16:00 15:00 – 16:00 15:00 – 16:00 -

Supporter timezone: Europe/London (GMT+00:00)

This topic contains 8 replies, has 2 voices.

Last updated by BD 6 years, 8 months ago.

Assisted by: Nigel.

Author
Posts
#623319

BD

I have been at this for a while and I've read and tested every seemingly related support ticket, but I can't find an answer. I have a parent view and a child view. If the child view returns no content, the title of the parent view still appears. I would like the parent to be hidden if the child is empty.

The code for the views is as follows:

PARENT

[wpv-layout-start]
	[wpv-items-found]
	<!-- wpv-loop-start --> 
		<wpv-loop>
          <div id="monthcontainer">
<div id="monthdivider" class="small-12 columns">
<span><month>[wpv-post-title]</month></span>
</div><!--monthdivider-->
[wpv-view name="name-of-child-view" order="desc" orderby_second="post_date" order_second="desc"]
</div><!--monthcontainer-->[/wpv-conditional]
		</wpv-loop>
	<!-- wpv-loop-end -->
	[/wpv-items-found]
	[wpv-no-items-found]
	[/wpv-no-items-found]
[wpv-layout-end]

CHILD

[wpv-layout-start]
	[wpv-items-found]
	<!-- wpv-loop-start -->
		<wpv-loop>
<div class="overview">
<h4>Overview</h4>
[fullarticle]
</div><!--overview-->
		</wpv-loop>
	<!-- wpv-loop-end -->
</div><!--longreadfeature-->
	[/wpv-items-found]
	[wpv-no-items-found]
	[/wpv-no-items-found]
[wpv-layout-end]

I have tried adding a DIV (with ID) to the child that appears if no items are found and then testing several versions of a javascript that supposedly hides the parent div, but no versions of this seem to work.

Any help would be, as ever, much appreciated.

#623372

Nigel
Supporter

Languages: English (English ) Spanish (Español )

Timezone: Europe/London (GMT+00:00)

Hi Nick

I don't think you need to resort to JavaScript or any smoke and mirrors to achieve this, just put all of the content in the child View, which itself outputs nothing if there are no child results found.

Something like this:

-- Parent View --

[wpv-layout-start]
    [wpv-items-found]
    <!-- wpv-loop-start -->
        <wpv-loop>
        	[wpv-view name="name-of-child-view"]
        </wpv-loop>
    <!-- wpv-loop-end -->
    [/wpv-items-found]
    [wpv-no-items-found]
    [/wpv-no-items-found]
[wpv-layout-end]



-- Child View --

[wpv-layout-start]
    [wpv-items-found]
    <p>Content that should appear only once</p>
    <p>e.g. parent title: [wpv-post-title id="$parent-slug"]</p>
    <!-- wpv-loop-start -->
        <wpv-loop>
        	<p>Content that appears for each child post iteration</p>
        	<p>e.g. child title: [wpv-post-title]</p>
        </wpv-loop>
    <!-- wpv-loop-end -->
    [/wpv-items-found]
    [wpv-no-items-found]
    [/wpv-no-items-found]
[wpv-layout-end]
#623386

BD

Thanks Nigel - this would work fine except that the parent view will need to contain more than one child view (sorry, this wasn't made at all clear above) - this is why it's set up as a parent / child relationship. The parent has several custom post type children. So the parent would actually look more like this:

[wpv-layout-start]
    [wpv-items-found]
    <!-- wpv-loop-start --> 
        <wpv-loop>
          <div id="monthcontainer">
<div id="monthdivider" class="small-12 columns">
<span><month>[wpv-post-title]</month></span>
</div><!--monthdivider-->
[wpv-view name="name-of-child-view" order="desc" orderby_second="post_date" order_second="desc"]
[wpv-view name="name-of-second-child-view" order="desc" orderby_second="post_date" order_second="desc"]
</div><!--monthcontainer-->[/wpv-conditional]
        </wpv-loop>
    <!-- wpv-loop-end -->
    [/wpv-items-found]
    [wpv-no-items-found]
    [/wpv-no-items-found]
[wpv-layout-end]

So if the parent view contains no results from either child, it should be hidden.

Regards, Nick

#623590

Nigel
Supporter

Languages: English (English ) Spanish (Español )

Timezone: Europe/London (GMT+00:00)

Ahh. That rather changes things.

You want to only show a View if any of its nested Views contain at least one result.

The cleanest way would be to prevent anything being output in the first place if the condition was not met, but that would effectively require you to write a PHP function that recreated all of your child Views and tested the results, which is not simple and implies duplication of the database queries.

So a hacky solution then.

In your parent View for some container DIV that you include that wraps up all of the content give it a class that can be used to target it and add a CSS style rule that hides it, e.g. display: none;

So when the page loads no content will be displayed.

Then in each of your child Views within the wpv-items-found section but not within the wpv-loop tags (so it is added only once) include inline script tags with a small snippet of JS that updates the display style of the wrapper DIV to display: block;

So, if any of your child Views have a result that script will make the whole parent View content visible.

Can you do that, or do you need help?

#623624

BD

Hi Nigel, I like your thinking - we're getting there but there's still an issue (see below).

I have done the following:

1. Set the #monthcontainer (the overall wrapper) in the parent view to display:none; (this is within the loop) - the parent looks like this:

[wpv-layout-start]
	[wpv-items-found]
	<!-- wpv-loop-start --> 
		<wpv-loop>
          <div id="monthcontainer">
<div id="monthdivider" class="small-12 columns">
<span><month>[wpv-post-title]</month></span>
</div><!--monthdivider-->
[wpv-view name="child-view-01" order="desc" orderby_second="post_date" order_second="desc"]
[wpv-view name="child-view-02" order="desc" orderby_second="post_date" order_second="desc"]
</div><!--monthcontainer-->
		</wpv-loop>
	<!-- wpv-loop-end -->
	[/wpv-items-found]
	[wpv-no-items-found]
	[/wpv-no-items-found]
[wpv-layout-end]

2. Created a new shortcode to display the javascript in the output editor: [displaymonth]. Putting the javascript directly into the output editor didn't work. The shortcode is as follows:

function displaymonth_shortcode() {
   return "<script type='application/javascript'>document.getElementById('monthcontainer').style.display = 'block';</script>";
}
add_shortcode('displaymonth', 'displaymonth_shortcode');

3. Placed the shortcode below [wpv-items-found], but above <wpv-loop> in both child views. The top of the child views looks like this:

[wpv-layout-start]
	[wpv-items-found]
[displaymonth]
	<!-- wpv-loop-start -->
		<wpv-loop>

This worked in principle - except that now the parent only outputs one parent item (that contains child content) - all others are hidden, regardless of whether or not they contain child items. I also tried placing the javascript in the JS editor but got the same result.

It then occurred to me that the javascript was only targeting a single ID on the page, so I chabged the wrapper to a class and used the following javascript as the shortcode:

function displaymonth_shortcode() {
   return "<script>var x = document.getElementsByClassName('monthcontainerfreecontent');var i;for (i = 0; i < x.length; i++) {x[i].style.display = 'block';}</script>";
}
add_shortcode('displaymonth', 'displaymonth_shortcode');

However, although the javascript has been tested separately (and is functional), when inserted in the shortcode, no parent items appear at all. Possibly this is because it is only looking for classes within the child and not on the entire page?

#623634

Nigel
Supporter

Languages: English (English ) Spanish (Español )

Timezone: Europe/London (GMT+00:00)

view-outer.png
view-inner.png

Hi Nick

I tried this locally and my solution was very simple and worked.

Check the screenshots for the inner and outer Views.

#623659

BD
Screen Shot 2018-03-09 at 11.54.13.png

Thanks Nigel,

I have tried this - but what happens is that even if just one of the child posts has content, the javascript fires and is applied to every single instance of the container - which means that we have empty parent months appearing (see screenshots). The difference between your parent view and mine is that your whole parent is hidden if there aren't any children (your targeted DIV is outside the loop). However, my parent returns months. Each month has child posts associated with it - which means that the target DIV has to be inside the loop, not outside.

Does this make sense?

Regards, Nick

#623663

Nigel
Supporter

Languages: English (English ) Spanish (Español )

Timezone: Europe/London (GMT+00:00)

Sorry, I thought you wanted to hide the whole of the parent View if there were no child posts.

You just want to hide iterations of the parent View where the nested Views of that iteration do not return any results.

So you would need your container to have a unique class or id. You can achieve that with something like...

<div class="results-container-[wpv-post-id]">

Then in the child View where you add the script if you are doing it how I have done it would look like:

jQuery('.results-container-[wpv-post-id id="$parent-slug"]').css('display', 'block');

You'll need to handle setting the initial display:none; without having to create a rule for every individual .results-container-{id}.

I think you can just inline that style on the div, I expect the jQuery will overwrite that even though it's an inline style, but I would have to test it to be sure.

#623665

BD

Nigel - you're a star. That works perfectly, thank you.

Nick