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

Setting a Value?

Within the services.js/Cordova file, I am encountering an issue with the following code: .factory('GCs', ['$http', function($http) { var obj= {}; $http.post("mydomina.com?myrequest=getbyid", { "id": "1"} ) ...

Prevent a dynamically generated text input from being used if it exceeds a specific character limit in VUE.JS

I am currently working on a Vue template that dynamically creates Bootstrap text inputs. The goal is to allow the user to retrieve the values onSubmit from these inputs. One requirement I have is to disable an input if the length of the text exceeds 10 ch ...

How can I reference a Bootstrap icon in the buttonImage attribute of a jQuery datepicker?

How can I customize the jQuery datepicker button image? I want to use the Bootstrap calendar icon as the button image for the jQuery datepicker. The icon image can be referenced in the HTML page like this: <i class=icon-calendar></i> When us ...

Using Angular to make a request to a NodeJS+Express server for a simple GET operation

I need help with making a successful GET request from my Angular component to a NodeJS+Express server. someComponent.ts console.log("Before"); // send to server console.log(this.http.get('/email').map((res:Response) => { console.log(" ...

VueJS - Vuefire - Unexpected Error: document.onSnapshot is not a valid function

I'm currently working on integrating Vuefire into my project. I have been following the instructions provided on the Vuefire website, but I am encountering an error. db.js: import firebase from 'firebase/app' import 'firebase/firestore ...

Using .htaccess to Conceal Directories with Documents

It seems that my website is being targeted by individuals seeking to obtain all the code. I have implemented an .htaccess file that will display nothing when someone visits domain.com/images, but if they specifically request domain.com/images/logo.png, the ...

Energetic flair for Vue animations

I am currently developing a VueJS sidebar component. The objective is to allow the parent to define a width and display a toggle button that smoothly slides the sidebar in and out. Here is an example: <template> <div class="sidebarContainer ...

Unable to display modal pop-up in ASP.NET Core MVC web application

I have developed a web application using ASP.NET CORE MVC. I encountered an unusual issue while trying to display a modal popup using JQuery. The div structure I am working with is as follows: <div class="modal fade" tabindex="-1" r ...

Enabling client-side access to collections without the need for meteor startup

Whenever I define my Meteor collections on the server and attempt to access them in the client without being within any of the predefined Meteor methods such as rendered, events, created, or helpers, I consistently encounter an error stating Meteor colle ...

What is the best way to handle parsing JSON with special characters in JavaScript?

Content stored in my database: "Recommended cutting conditions" When using Json_encode in PHP, the result is: {"table1":[{"Item":{"original_text":"\u63a8\u5968\u5207\u524a\u6761\u4ef6 \b"}}]}; In JavaScript : var str ...

The content of the string within the .ts file sourced from an external JSON document

I'm feeling a bit disoriented about this topic. Is it feasible to insert a string from an external JSON file into the .ts file? I aim to display the URLs of each item in an IONIC InAppBrowser. For this reason, I intend to generate a variable with a sp ...

Is it possible for style sheets to malfunction due to the presence of the TITLE attribute on <link> tags?

We are currently involved in the process of upgrading an outdated corporate intranet. The challenge is that our users primarily rely on IE8 and IE9, while most of our sites were designed to function with compatibility for browsers between IE5 - IE9. While ...

Combining model with a string in an expression in AngularJS version 1

Can this text be transformed into an expression using ecmaScript 2015? The || operator seems to be causing issues. {{ ::$ctrl.realEstateProjectCurrentProduct.housingTax + ' €' || $ctrl.noDataMessage }} ...

I am curious if there is a feature in intro.js that allows for the highlighting of text or images to

I am having trouble getting intro.js to work with Ionic 4 as the highlighted text is not showing up view image here This is how I implemented the code in Angular 7: intro() { let intro = introJs.introJs(); intro.setOptions({ exitOnOverlayClick: false, ...

Storing the selected radio button value in AsyncStorage using React Native: A step-by-step guide

Can anyone help me with storing the users selected radio button value in AsyncStorage? I have radio button values being retrieved from another file and assigned to labels. Here is an example of how my radio buttons are structured: import RadioButtonRN fr ...

Customizing Marker Images in Google Maps JavaScript API

Currently, I am using a workaround to rotate a custom image marker in Google Maps. The issue I am encountering is regarding sizing. For example, if my PNG image is 400px wide and 200px high. When rotating the image so the width becomes vertical, it gets ...

Various Plus/Minus Containers

One issue I am facing is that when using multiple counters on the same page, my - and + buttons to decrease or increase the number in a text box do not function properly. The script provided below works for one counter. How can I modify this code so that ...

The issue with launching a Node.js server in a production environment

I'm currently facing an issue when trying to start my TypeScript app after transpiling it to JavaScript. Here is my tsconfig: { "compilerOptions": { "module": "NodeNext", "moduleResolution": "NodeNext", "baseUrl": "src", "target": " ...

Is there a way to prevent the DOM from loading images until Angular has successfully injected the correct variables?

Having some trouble with this block of code I have: <div class="image" ng-repeat="image in images"> <img src="{{image.url}}"></img> </div> It seems that the image sources are being set correctly, but I keep getting an error wh ...

Issues arising when attempting to replicate a fetch object into an undefined object in React.js

I'm facing an issue where I have defined a 'poke' object and when I try to clone an object into it, I only get a promise fulfilled with the object's link , instead of the actual object. Here's the code snippet: let poke = fetch(url ...