Manage the heights of two panels dynamically using directive in angularjs - angularjs

There are two panels in html page, set with the min-height.I want to equate the heights of two panels dynamically based on the height of highest panel using directive.I have worked with the following directive,
app.directive('setHeightmaster', function ($timeout) {
return function (scope, element, attrs) {
scope.$watch(function () {
element.css("min-height", $('#mp').css("height"));
});
});
app.directive('setHeightpanel', function ($timeout) {
return function (scope, element, attrs) {
scope.$watch(function () {
element.css("min-height", $('#hm').css("height"));
});
}
});
In html file, I have included the directive for two panels section.
<div class="col-lg-2 col-md-2 col-sm-2 pad-left-3 pad-right-1">
<section class="panel panel-success sideMealPanel" id="hm" set-heightmaster>
<div class="panel-heading panel-title">
<span>Meal
</span>
</div>
<div class="panel-body panel-body-height-without-footer pad-0">
<div class="pad-15" ng-include="'..MealtimeDetail.html'"></div>
</div>
</section>
</div>
<div class="col-lg-10 col-md-10 col-sm-10 pad-left-1 pad-right-0 page-table">
<section class="panel panel-success" id="mp" set-heightpanel>
</div>
When the height of one panel changes, other panel's height should be dynamically set to the height of first panel and vice a versa. The above code equates the heights of two panels leaving the blank space at the bottom of content in both panels. I want to remove that extra space and eauate the heights without leaving the space in both panels at the bottom.
Where am i wrong with above code? How can i solve this?
Thank you.

I believe you can't do this in this way.
Your directive would only be executed only once or the first time content is loaded. So, when your content has changed, there is no way for the directive to know that it has to work its magic again.
You need to make sure that code runs again after content is loaded again.
After you content has changed, broadcast an event from the controller, and catch it in the directive and then re-adjust the heights.
Watch doesn't works over CSS properties.
And Event mechanism works much better.

Related

ng-repeat data binding with custom directive

I have a list and on each item in list I am calling a modal window (custom directive) which should have details about that item being clicked , but the data does not change and remains same across each item. Please find the code below.
angular
.module('Testapp')
.directive('testDirective', function () {
return {
restrict: "AE",
templateUrl: "/Apps/templates/mytem/testdir.html",
translucent: true,
scope: {item:'=data'},
link: function (scope, element, attribute) {
console.log(scope.sequence);
}
};
});
Directive
<div class="modal fade" id="modalAddFilters">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body tree">
{{item}}
</div>
</div>
</div>
</div>
Calling Template
<div>
<div ng-repeat="items in TestList>
<test-Directive id="directive_modalAddFilters" data="items"></test-Directive>
</div>
I am able to see the data correctly loaded in DOM but directive template doesnt change the data.
You code works fine, except that you forget to close you ng-repeat with a quotation mark.
I think you just didn't properly resolved you data into the modal view.
I have made a plunk based on your (partial) code, I added a modal and everything works fine. I've used ui-bootstrap to show the modal with the repeated data injected.

Detecting an image is fully loaded from a directive, where the directive is not applied to the image element

there are many examples in SO on how we can use an angular directive to know when an image is loaded. The examples describe a directive that is directly applied to the img element like so:
<img ng-src="myimage.jpg" my-great-directive />
And then in the directive "my-great-directive"
we can do an:
element.bind("load" , function(e){ }
And i works great.
However, what if the construct is like this:
<div my-great-directive>
<img ng-src="myimage.jpg" />
</div>
Within my-great-directive, how do I bind to the inside image loaded event?
This is not really a theoretical problem for me. I am using a 3rd party slider called angular-carousel-slider that wraps around like so:
<ul rn-carousel rn-carousel-buffered
rn-carousel-index="mycarousel.index"
rn-carousel-auto-slide="0.3" rn-carousel-pause-on-hover >
<li ng-repeat="slide in slides">
<img ng-src="{{eventBasePath}}{{slide.img}}?rand={{rand}}"/>
</li>
</ul>
I'm trying to modify its code so that it does not slide to the next image unless the current image is fully loaded (to avoid the situation of a fast slider half loading images and moving to the next). I'm stuck on how I can trap this image loaded event inside the directive. Doing element.bind("load") does not work as the directive is not applied to the image element.
Thank you!
Check this working demo: JSFiddle
Use anguler.element to find the img element and bind the event.
In your directive, it should be element.find('img').
angular.module('Joy', [])
.directive('hello', [function () {
return {
restrict: 'A',
link: function (scope, ele) {
var img = ele.find('img');
console.log(img);
img.bind('load', function () {
console.log('img is loaded');
});
}
};
}]);
HTML:
<div ng-app="Joy">
<div hello>
<img ng-src="https://www.google.com.hk/images/srpr/logo11w.png">
</div>
</div>
Update 1
If use ng-repeat, add a $timeout to wait for ng-repeat finishes first. Check working demo: JSFiddle.
Note: this demo is loading a very large image. After the image is loaded, the text img is loaded will be shown.
Update 2
If the event binding is not working, try native image.onload binding (or oncomplete to find images cached). Working demo: JSFiddle.

How to set focus on last element in ng-repeat?

I display a list of items through ng-repeat
<div ng-repeat="{item in items}" >
<h3>{{item.name}}</h3>
<h3>{{item.price}}</h3>
<h3>{{item.date}}</h3>
</div>
I have an add where i click and add new items..I display through ng-repeat in a div and that contains scroll..Whenever i add a new item the scroll bar stand still and i can see the newly added item by scrolling down only.
I used ng-focus="{$last}" to set focus on last item but it didnt work.
I need to set focus on last element in ng-repeat even i have 50 elements in div.
Plss help me in this..
I'd recommed using a directive to achieve this behaviour.
module.directive('setFocus', function(){
return{
scope: {setFocus: '='},
link: function(scope, element){
if(scope.setFocus) element[0].focus();
}
};
});
And on your HTML:
<div ng-repeat="{item in items}" set-focus="$last">
<h3>{{item.name}}</h3>
<h3>{{item.price}}</h3>
<h3>{{item.date}}</h3>
</div>
When you load the page, .focus() is ran on the last element.
If you add an element, .focus() is ran on that new last element.
Now your problem lies on which elements are focusable across different browsers. I suggest you check which HTMLElement can receive focus.
So, you may want to add a hidden focusable element inside your div, and modify the directive to focus on that, or maybe add a tabindex attribute to your ng-repeat div. (tabindex="some number other than -1")
<div ng-repeat="{item in items}" set-focus="$last" tabindex="0">
<h3>{{item.name}}</h3>
<h3>{{item.price}}</h3>
<h3>{{item.date}}</h3>
</div>
I hope this helps!
To clarify: ng-focus is used to specify behaviour ON focus, not to trigger focus on the element.
You can use $anchorScroll in AngularJS.
Just place a link (< a> < /a>) at the bottom of your div with the id="bottom" and ng-hide="true", so you dont actually see the link.
<div id="scrollArea" ng-repeat="item in items">
<h3>{{item.name}}</h3>
<h3>{{item.price}}</h3>
<h3>{{item.date}}</h3>
<a id="bottom"></a>
</div>
<button type="button" ng-click="addNewItem" value="Add"/>
When you click on your "add" button or whatever, call a function in the Controller that leads you to the bottom link with the id="bottom".
$scope.addNewItem = function() {
//Add new item here...
//After adding item, go to bottom:
$location.hash('bottom');
$anchorScroll();
};
You can read more about this on the Documentaion of AngularJS:
Documentaion of $anchorScroll.

How do I get the clicked element's info (from the scope) in a given AngularJS directive?

Well, let me describe my original problem that's being aroused time to time. Consider the following product.html page:
<div data-ng-controller="productsCtrl" data-ng-init="getProducts()" class="row-fluid">
<div class="span12">
<div data-ng-repeat="section in sections">
<h2>{{section.name}}</h2>
<div class="products-container">
<div data-ng-repeat="product in section.products">
<img alt="{{product.name}}" class="product-img" data-zoomable-image="/path_to_large_img_or_empty_if_no_image_to_zoom" data-ng-src="/path_to_thumbnail_img">
<div class="product">
<!-- Product Info -->
</div>
</div>
</div>
</div>
</div>
</div>
The productsCtrl controller is obviously responsible to get the products information, one of which is the thumbnail / large image url of the product. The idea is to create a directive that controls the img element based on "data-zoomable" attribute on a given image. If the attribute's value is empty, there's no image to be displayed; Otherwise, a modal box should be opened to display the image.
So far, so good. So let me show the zoomableImage directive:
app.directive('zoomableImage', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var zoomIn = element.wrap('').click(function (e) {
var href = element.data('zoomableImage');
var modal = $('#zoomInModal');
//Here, I need to pass the "product's name" to the zoomInCtrl's scope.
//This can be done using the following 3 lines of code...
var s = modal.scope();
s.product = 'The clicked element\'s name obtained from the DOM "element"';
s.$apply();
//I believe that those 3 above lines suck, since I'm not supposed to
//set the scope of another controller in a directive. Right?
modal.prependTo('body').modal('show');
e.preventDefault();
});
$('<span class="label label-info">Zoom In</span>').insertAfter(zoomIn);
}
};
});
So, the question is that what's the correct way of passing the "clicked" product's Name, Part#, and Image URL to the dialog I'm going to open above? Please note that the ProductsCtrl returns an array of section, each section contains multiple products and so on...
Of course, I can put the name, part# and other related information in different data attributes of the img element, but I do believe this duplicates the information that's already there in the products controller.
Thanks.
You can pass the reference of your product objects(s) to the directive by adding this to the directive:
scope: {
product: '='
}
The object is then shared between your controller and your directive instance.
You have to add the attribute in your html too:
<img data-product="product" data-zoomable-image="..." ... >
EDIT:
Modal should be a service. Checkout the angular-bootstrap modal.

How to do an ng-repeat that doesn't duplicate the ng-repeat element

I need to use ng-repeat to produce several elements but those elements cannot be each wrapped inside a div (this is for box layout purposes where the box layout only works on the immediate children). For example, I need this result:
<div class='box-layout'>
<div class='item-header>Head 1</div>
<div class='item-body>Body 1</div>
<div class='item-header>Head 2</div>
<div class='item-body>Body 2</div>
</div>
There is no wrapping element on the repeated sections. This structure is required to make use of flex-box style layouts. How can I do this with AngularJS?
As #Anders Bornholm said, it can't be done (fyi, in knockout.js this is easy).
Albiet its ugly, you can accomplish this via a directive, e.g.:
AngularJS ng-repeat with no html element
ng-repeat without HTML element (this time really without any)
JS:
directive('htmlAppend', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
element.append(attrs.htmlRepeat);
}
};
})
HTML:
<div class='box-layout'>
<div class='item-header' ng-repeat="s in sections" html-append="<div class='item-body'>Body</div>">Head</div>
</div>
Short answer: can't be done. One of Angular's biggest drawbacks for me personally.
If directive templates would have allowed more than one root element you could have done it with a directive, but that doesn't work either.

Resources