uibCollapse nested in an directive element won't work, - javascript

When I click my-dir, collapsed elements won't expand.
I have set size in css for .btn class to ensure actually click event.
// index.html
<body ng-controller="ctrl">
<p>{{hello}}</p>
<div class="btn" my-dir></div>
</body>
// app.js
var app = angular.module('app', ['ui.bootstrap']);
app.controller('ctrl', function($scope){
$scope.hello = 'hello';
});
app.directive('myDir', function() {
return {
restrict: 'A',
replace: false,
templateUrl: './tmpl.html',
link: function (scope, element, attrs) {
scope.isCollapsed = true;
element.bind('click', function (e) {
scope.isCollapsed = !scope.isCollapsed;
})
}
};
});
// tmpl.html
<div class="dir-box">
<div uib-collapse="isCollapsed">
<p>Hello World!</p>
<p>Nice to meet You!</p>
<p>: )</p>
</div>
</div>
Why uibCollapse does not work in this situation ?
Any ideas ?
plunker here

There are 2 things that you need to fix:
1.Change click binding to ng-click. By default angular does not run its $digest cycle on jquery events. You can fix that by adding $scope.$apply() or $timeout in your code, but I recommend using ng-click as a more proper way.
<div class="btn" my-dir ng-click="isCollapsed = !isCollapsed"></div>
By your original code I should add it inside your directive template, but your dir-box position does not overlap with the blue area...
2.Use overflow:hidden on uib-collapse element.
<div uib-collapse="isCollapsed" style="overflow: hidden;">
The contents were still showing when the parent height is 0
plunker

Related

ng-repeat got more and more element when used in ng-transclude and toggled by ng-if

Here is my full test code:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.16/angular.min.js"></script>
<script>
angular.module('app', []).controller('MainController',['$scope', function($scope){
$scope.model = {
showList: false,
list:[1,2,3,4]
}
}]).directive('withTransclude', function(){
return {
restrict: 'E',
replace: true,
transclude: true,
template:'<div><span>transclude container</span><ng-transclude></ng-transclude></div>',
link:function($scope, $element, attr, ctrl, transclude){
// i dont want the ng-transclude tag so i replaced it
// if comment this expression problem disappear but the ng-transclude tag remains
$element.find('ng-transclude').replaceWith(transclude());
}
}
})
</script>
</head>
<body ng-app='app' ng-controller="MainController">
<button ng-click='model.showList = !model.showList'>toggle show</button>
<div ng-if="model.showList">
<span>shown</span>
<with-transclude>
<div>hello</div>
<ul>
<li ng-repeat="number in model.list">{{number}}</li>
</ul>
</with-transclude>
</div>
</body>
</html>
Problem:
When using ng-transclude, I don't want the ng-transclude tag remains in my page, so i replaced it with the transcluded content,but when toggle ng-if by clicking button, more and more ng-repeat element got rendered, if I don't replace,it's ok.
Question:
Why this problem occurred? and is there any other way to remove the ng-transclude tag, which doesn't give this problem.
Change:
$element.find('ng-transclude').replaceWith(transclude());
To:
var transcludeEl = $element.find('ng-transclude')
$element.append(transcludeEl.contents())
transcludeEl.remove();
https://plnkr.co/edit/7zw27ldkGp0zeey5z9QI?p=preview
try with ng-show instead of ng-if.....i didn't see the repetition when using ng-show
<div ng-show="model.showList">
CHECK it

Using jquery from angular directive does not work

I am trying to implement jquery-ui's sortable on the elements inside the ng-repeat.
Problem : i cannot actually do the sortable action on the elements inside the ng-repeat.
I've checked for answers. My code seems similar to most answers which apparently work, but my code doesn't work
Below is the html snippet:
<div class="container-fluid rt-widget-list-dim-adj">
<div my-dir>
<div ng-repeat="widget in model.widgets" ng-switch="widget.widgetType">
<div ng-switch-when="HEADER">
<ng-include src="'views/widget/widget-header.view.client.html'">
</div>
<div ng-switch-when="IMAGE">
<ng-include src="'views/widget/widget-image.view.client.html'">
</div>
<div ng-switch-when="YOUTUBE">
<ng-include src="'views/widget/widget-youtube.view.client.html'">
</div>
</div>
</div>
</div>
my app.js:
(function(){
angular.module("myApp", ['ngRoute', 'myDir']);
})();
below is my custom directive:
(function () {
angular
.module("myApp", [])
.directive("myDir", makeSortable);
function makeSortable() {
var directive = {
restrict : 'ACE',
link : linker
};
function linker(scope, element, attrb) {
element.sortable();
}
return directive;
}
})();
element in linker function args is not jQuery, it is a jqLite wrapper.
If you want to apply jQuery, try this:
$j(element[0]).sortable();

use jQuery.matchHeight from Angular directive

I am trying to set elements to the same height using jQuery.matchHeight. I call the function from an Angular directive
angular.module('myApp')
.directive('matchHeight', ['$timeout', function ($timeout) {
var linkFunction = function(scope, element) {
$timeout(function() {
angular.element(element).matchHeight();
});
};
return {
restrict: 'A',
link: linkFunction
};
}]);
The matchHeight plugin and jQuery are included in index.html
<html>
<head>
all head stuff
</head>
<body>
<div class="row usps">
<div class="col-sm-4 usp-block" ng-repeat="block in content.marketing" match-height>
<a href="{{block.link_url}}" class="thumbnail">
// Rest of html
</a>
</div>
</div>
<script src="bower_components/jquery/dist/jquery.js"></script>
<script src="bower_components/angular/angular.js"></script>
<script src="bower_components/matchHeight/dist/jquery.matchHeight.js"></script>
<script src="scripts/app.js"></script>
<script src="scripts/directives/matchheight.js"></script>
</body>
</html>
The problem is that the height is not set although the directive is being applied to the element.
The jQuery.matchHeight plugin will set all the items in an array to the height of the tallest element in that array.
the match-height directive is applied to a single element. Because there is no array, the height is not set on the element.
Moving the directive to the parent element in the DOM and adding the class equal to the child element(s) gives the array needed to set the height.
<div class="row usps" match-height>
<div class="col-sm-4 usp-block equal" ng-repeat="block in content.marketing">
<a href="{{block.link_url}}" class="thumbnail">
// Rest of html
</a>
</div>
</div>
In the service I apply the matchHeight function to all elements with the class equal
angular.module('myApp')
.directive('matchHeight', ['$timeout', function ($timeout) {
var linkFunction = function(scope, element) {
$timeout(function() {
angular.element(element).find('.equal').matchHeight();
});
};
return {
restrict: 'A',
link: linkFunction
};
}]);
Check this out, guys.
https://github.com/christopher-weir/AngularJs-MatchHeight
this custom directive is working fine. Also, codebase is pretty simple you can tweaks as your project need.

Show div tag when button is clicked with $emit and $on

I have two controllers ParentController and ChildController. What i want to achieve is when button from ChildController is clicked to emit event and listen for that event in ParentController and when button is clicked to show iframe in div tag. You can find my code below.
ParentController
var myApp = angular.module("myApp", []);
myApp.controller('ParentController', ['$scope', function ($scope) {
}]);
*ChildController**
myApp.controller('ChildController', ['$scope', function ($scope) {
}]);
View for ParentController
<div ng-controller="ParentController">
<div class="module-content">
<h3>Title</h3>
</div>
<div content-body>
<div ng-show="$on('showiframe')">
<iframe ng-src={{url}} frameborder="0"></iframe>
</div>
</div>
<div>
View for ChildController
Show IFrame
So can i do something like this, listen for event in div tag in ng-show?
In your ChildController, $emit an event on your button click. $emit will dispatch the event upwards through the scope hierarchy.
Child view :
<button type="button" ng-click="showIframe()">Show iframe</button>
Child controller :
myApp.controller('ChildController', ['$scope', function ($scope) {
$scope.showIframe = function () {
$scope.$emit('showiframe');
}
}]);
In your parent controller, create a listener on 'showIframe' event.
Parent view :
<div ng-controller="ParentController">
<div class="module-content">
<h3>Title</h3>
</div>
<div content-body>
<div ng-show="showIframe">
<iframe ng-src={{url}} frameborder="0"></iframe>
</div>
</div>
<div>
Parent controller :
var myApp = angular.module("myApp", []);
myApp.controller('ParentController', ['$scope', function ($scope) {
// Iframe is not showed initially
$scope.showIframe = false;
$scope.$on('showIframe', function() {
$scope.showIframe = true;
});
}]);
Edit :
Regarding your fiddle, for example, you can watch the inputText model and emit the events :
$scope.$watch('inputText', function () {
$scope.$emit('send-date', $scope.date);
$scope.$emit('send-input', $scope.inputText);
});
This will emit the events each time the inputText models changes.

Angular JS: binding in ng-show not working

I have a directive and a controller:
app.directive('responseBox', function(){
return {
restrict: 'E',
transclude: true,
templateUrl: 'responseBox.html',
link: function(scope, element, attrs) {
element.bind("click", function () {
scope.toggle();
})
}
}});
and a controller:
app.controller('responseBoxCtrl', function($scope) {
$scope.opened = false;
$scope.toggle = function() {
$scope.opened = !$scope.opened;
console.log($scope.opened);
}});
responseBox.html:
<div class="promptBlockResponse" ng-transclude>
<div class="btn-toolbar" style="text-align: right;">
<div class="btn-group" ng-show="opened">
<a class="btn btn-link" href="#"><i class="icon-pencil icon-white"></i></a>
<a class="btn btn-link" href="#"><i class="icon-remove icon-white"></i></a>
</div>
</div>
And in the main html file:
<response_box ng-controller="responseBoxCtrl"></response_box>
I want the btn-group to show when the opened variable is true. When I click the responseBox I can see the variable toggling, but the btn-group does not show/hide. What am I missing?
So repeating what Josh and I said in the comments above, the click handler runs "outside" of Angular, so you need to call scope.$apply() to cause Angular to run a digest cycle to notice the change that was made to scope (and then it will update your view):
$scope.toggle = function() {
$scope.opened = !$scope.opened;
console.log($scope.opened);
$scope.$apply();
}});
The link function can be eliminated by using ng-click in the template:
<div class="promptBlockResponse" ng-transclude ng-click="toggle()">
With Angular 1.3 and 1.2 the following snippet from an HTML template for a custom element directive:
<div ng-click="toggle($event)"></div>
<div ng-show="data.isOpen"></div>
And a snippet from the controller for that custom directive:
$scope.toggle = function ($event, destinationState) {
....
data.isOpen = true; //this is in scope and a digest cycle is already running
//calling $scope.$apply will cause an error
demonstrates an in scope scenario where you do need to use $apply.
I came across this SO question because I was using double brackets in my
<div ng-show="{{data.isOpen}}">
Changing to
<div ng-show="data.isOpen"></div>
got my binding working when I thought at first I had a scope issue.
So in angular 1.2 and 1.3 ng-click is not "outside" of Angular, at least using the signature I used for my toggle function and is explained here:
$apply already in progress error
I discovered my double bracket ng-show issue that I initially thought was a scope issue thanks to this SO:
why doesn't ng-show remove class ng-hide

Categories