Leveraging Angular to dynamically adjust the height of ng-if child elements based on parent

I'm struggling with a few things in my current setup. I have a view that consists of 3 states - intro, loading, and completed. My goal is to create a sliding animation from left to right as the user moves through these states.

Here is the basic structure I have in place:

var app = angular.module('plunker', ['ngAnimate']);

app.controller('MainCtrl', function($scope) {
  $scope.triggered = '';
  
  $scope.loading = false;
  $scope.completed = false;
  
  $scope.one = function() {
    console.log('one');
    $scope.loading = true;
    $scope.completed = false;   
  };
  
  $scope.two = function() {
    $scope.loading = false;
    $scope.completed = true;   
  };
  
}).directive('inheritHeight', ['$window', '$timeout', function($window, $timeout) {
    return {
      restrict: 'A',
      link: function (scope, elm, attrs) {

        scope.$watch("loading", function(newV, oldV) {
          console.log(newV, elm[0]);
          $timeout(function () {
            scope.triggered = scope.triggered + 'triggered ';
            scope.height = elm[0].querySelector('.child').offsetHeight;
            console.log(elm[0].querySelector('.child'));
            console.log(elm[0].querySelector('.child').offsetHeight);
            elm.css('height', elm[0].querySelector('.child').offsetHeight + 'px');
          });
        });

      }
    };
}]);
.parent {
  border: 1px solid red;
  position: relative;
  overflow: hidden;
}

.child {
  width: 100%;
  position: absolute;
  top: 0;
  left: 0; }
  .child.ng-enter, .child.ng-leave {
    -webkit-transition: 800ms cubic-bezier(0.645, 0.045, 0.355, 1) all;
    -moz-transition: 800ms cubic-bezier(0.645, 0.045, 0.355, 1) all;
    -ms-transition: 800ms cubic-bezier(0.645, 0.045, 0.355, 1) all;
    -o-transition: 800ms cubic-bezier(0.645, 0.045, 0.355, 1) all;
    transition: 800ms cubic-bezier(0.645, 0.045, 0.355, 1) all; }
  .child.ng-enter {
    -webkit-transform: translateX(100%);
    -moz-transform: translateX(100%);
    -ms-transform: translateX(100%);
    -o-transform: translateX(100%);
    transform: translateX(100%); }
  .child.ng-enter.ng-enter-active {
    -webkit-transform: translateX(0);
    -moz-transform: translateX(0);
    -ms-transform: translateX(0);
    -o-transform: translateX(0);
    transform: translateX(0); }
  .child.ng-leave {
    -webkit-transform: translateX(0);
    -moz-transform: translateX(0);
    -ms-transform: translateX(0);
    -o-transform: translateX(0);
    transform: translateX(0); }
  .child.ng-leave.ng-leave-active {
    -webkit-transform: translateX(-100%);
    -moz-transform: translateX(-100%);
    -ms-transform: translateX(-100%);
    -o-transform: translateX(-100%);
    transform: translateX(-100%); }
    
.child-a {
  background-color: green;
  height: 100px;
}

.child-b {
  background-color: blue;
  height: 50px;
}

.child-c {
  background-color: yellow;
  height: 30px;
}
<script src="https://code.angularjs.org/1.4.9/angular.js" data-semver="1.4.9"></script>
<script  src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.2/angular-animate.js"></script>

<div ng-app="plunker" ng-controller="MainCtrl">
  <div class="parent" inherit-height="">
    <div class="child child-a" ng-if="!loading && !completed">
      <button ng-click="one()">Click One</button>
    </div>
    <div class="child child-b" ng-if="loading && !completed">
      <button ng-click="two()">Click Two</button>
    </div>
    <div class="child child-c" ng-if="!loading && completed"></div>
  </div>
  <p>{{ height }}</p>
  <p>{{ triggered }}</p>
</div>

Despite having the inherit-height directive on the parent div, it doesn't seem to properly adjust its height when transitioning between states. Even with the use of scope.$watch and $timeout, I can't get it to work smoothly...

Any advice or suggestions would be greatly appreciated. Thank you!

Answer №1

Initially, I assumed querySelector would function properly since ng-if removes elements from the DOM. Surprisingly, this was not the case. Success came when I targeted the specific child element that was set to transition in:

elm.css('height', elm[0].querySelector('.child.ng-enter').offsetHeight + 'px');

Answer №2

Prior to delving into animation and CSS, it's crucial to understand two key points:

1. The scope for directives differs from the controller's scope.

In your scenario, referencing status.loading leads to nothing. Without assigning an isolated scope to the inherit-height directive, its scope aligns with its parent, which is MainCtrl in this case. However, since you haven't defined something like $scope.status.loading, the watcher is essentially monitoring nothing, resulting in the failure of all logic within the $timeout service.

2. The element argument in a directive's link function represents the element itself, not an array.

Hence, using elm[0] serves no purpose. Achieving the same goal can be done through something like elm.eq(0).height().

3. Using querySelector will only select the first matching element.

Consequently, in your case, it will consistently choose child-a, potentially not meeting your desired outcome. It's generally considered poor practice to retrieve child elements' heights in this manner. If necessary, creating another directive and requiring it from the parent would be a more effective approach.

I recommend referring to the Angular documentation or other tutorials on directive scope and the concept of the link function before proceeding to tackle more intricate aspects of CSS.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for retrieving a local variable from inside a callback and using it outside the function

Recently, I started utilizing npm plaid and I'm looking for a way to access the variable trans outside of the plaidClient.getTransactions function. Any suggestions would be greatly appreciated. var trans; var startDate = moment().subtract(30, &apo ...

Adjusting body styles in a NextJS application using localStorage prior to hydration: A step-by-step guide

If you visit the React website and toggle the theme, your choice will be saved in localStorage. Upon refreshing the page, the theme remains persistent. In my NextJS project, I am attempting to achieve a similar outcome by applying styles to the body eleme ...

Using the Google Identity Services JavaScript SDK in conjunction with Vue and TypeScript: A comprehensive guide

I am currently working on authorizing Google APIs using the new Google Identity Services JavaScript SDK in my Vue / Quasar / TypeScript application. Following the guidelines provided here, I have included the Google 3P Authorization JavaScript Library in ...

Send multiple input groups using Ajax POST requests

I need help setting up a form with 4 groups of checkboxes for refining search results. My goal is to send an array for each checkbox group that contains the IDs of the currently selected checkboxes. $.ajax({ url: "/stay_in_belfast/accommodation", t ...

"Exploring the challenges of implementing a jquerytools tooltip with AJAX

Currently, I have set up an ajax call to run every 15 seconds. The issue arises when the ajax call disables the tooltip if it's open at that moment for a particular item. This results in the destruction of only the tooltip being displayed, leaving oth ...

Tips for using the POST method to open an external URL in IONIC - what you need to know

I have a CodeIgniter Controller set up on the server to create an Excel file, and it's working well on the web using the submit form method. How can I execute this controller from my IONIC app? Below is my CodeIgniter controller: function excel_kpa( ...

Give a CSS Element a specific class

Can all h3 elements be styled with the class responsive-heading using only CSS? Take a look at the CSS snippet provided for more clarity: h3 { .responsive-heading } /* The responsive-heading class is defined in another CSS file and includes: .respons ...

Is there a way to delegate properties in Angular 2+ similar to React?

When working with React, I have found it convenient to pass props down dynamically using the spread operator: function SomeComponent(props) { const {takeOutProp, ...restOfProps} = props; return <div {...restOfProps}/>; } Now, I am curious how I ...

Tips for increasing a variable by one with each button click?

I have a simple JavaScript function called plusOne() that is designed to increment a variable by 1 each time a button is clicked, and then display the updated value on a webpage. However, I'm encountering an issue where the addition only occurs once. ...

Consider creating a distinct document for your "scripts"

Within my package.json configuration file, the "scripts" section contains numerous commands structured as shown below. "scripts" { "script1": "command example", "script2": "command example", "script3": "command example", "script4": "command exampl ...

What happens if I attempt to pass a blank field in multer?

I am trying to verify if the image field passed to Multer will include the file. There are no errors being thrown by Multer and the server is currently processing the request. ...

The latest update to the Server Stats code mistakenly changes the channel to "undefined" instead of displaying the total number

I've been working on a private bot for a specific server that displays server statistics and more. However, I've encountered an issue where every time a user joins or leaves the guild, the bot updates a channel with 'undefined' instead ...

Troubleshooting the Owl carousel's responsiveness with a div width of 1170px

Whenever my display size is less than 1170px, the width of the owl carousel div overflows. How can I fix this issue? jQuery(document).ready(function($) { "use strict"; // CUSTOMERS TESTIMONIALS CAROUSEL $('#customers-testimonials&a ...

textarea label align: center

I've been struggling to center-align the label for this textarea within the text box, but so far my attempts have failed. The resulting display resembles the following: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxx ...

Strange height problem detected in embedded YouTube video

Currently, I have a page with an embedded YouTube video that is intended to be placed in a container with a variable width. When the video is outside of the container, the aspect ratio appears normal. However, once it is placed inside the container, the he ...

Trigger an AJAX request by clicking a button using PHP

I've seen this question asked multiple times, but none of the answers seem to relate to my specific situation. I have a button that when clicked, should call a JavaScript function, passing it a PHP variable. The AJAX will then send that variable to a ...

Mouse over effect to highlight the crosshair on a table

My code currently enables a crosshair function, but I am looking for a way to limit the highlight effect only within the cursor (hover) area. Instead of highlighting in a "cross" shape, I would like it to show a backward "L" shape. This means that the hig ...

Issues with the HTML5 progress bar malfunctioning due to alterations made by Angular

Using the html5 progress bar <progress value="5" max="100"></progress> in angular can lead to it being replaced by angular classes. Angular also has its own progress bar that uses similar tags. However, elements like the html5 slider do not get ...

Modify the data in the <col> column

Is it feasible to update the values in a particular column of a table? <table> <col with="auto"> <col with="auto"> <col with="auto" id="update_me"> <?php for(hundreds of lines){ ?> <tr> <td>so ...

Is Javascript the best choice for managing multiple instances of similar HTML code?

I am currently facing the challenge of dealing with a lengthy HTML page consisting of around 300 lines of code. The majority of the code involves repetitive forms, each identical except for the ID (which varies by number). Is it considered appropriate or ...