angular directive require non parent directive controller - javascript

I wrote ADM-dateTimePicker module.
Currently I'm appending datePicker popup to main directive element, and requiring its controller to use it like an api:
angular.module('ADM-dateTimePicker', [])
.directive('admDtp', [function() {
return {
require: ['ngModel', 'admDtp'],
link: function() {},
controller: [function() {}]
}
}])
.directive('admDtpCalendar', [function() {
return {
require: '^^admDtp',
link: [function() {}]
}
}])
In case of using adm-dtp in some dialogs with hidden overflow, my datePicker will hide too, and sometimes has z-index issue.
I think i have these two options:
make popup position fixed.
this will solve the problem but i think sticking in screen and not moving with scroll might be annoying for users.
append to document body
the only problem is i cannot require ^^admDtp! because admDtp is not the parent of popup anymore.
Any idea for solving this issue!?
I'm Ok with appending popup to document body but how can I require non parent directive controller to use its api ?

Related

Labels missing on first time load

I am facing an issue with AngularJS directive/model load of my labels. I have a directive for building a table. All the header names, except the "Actions" one, are built dynamically by passing from controller as you can see in the snippet below:
<div class="col-xs-12" ng-cloak>
<custom-data-table grid-options="ctrl.gridOptions"></custom-data-table>
</div>
And in my controller:
function MyController(allInjections) {
var ctrl = this;
//Load some stuff
ctrl.gridOptions = [];
function initCtrl() {
ctrl.gridOptions = {
// all fields
};
// some other stuff
}
initCtrl();
}
The directive works well most of the time, but some times, mainly on the first time loading the page, for some reasons my labels are not loading. Look at the pictures.
The below image shows you the problem I am facing.
This table image is what I am expecting
I have added a log in my directive to see what I am receiving like:
function CustomDataTable(allInjections) {
var _dirPath = 'table.html';
var directive = {
restrict: 'EA',
templateUrl: _dirPath,
scope: {
gridOptions: '=',
checks: '='
},
link: linkFunc
};
function linkFunc($scope) {
console.trace($scope.gridOptions);
//do some stuff
}
}
Finally, after this and debugging in order to try to figure out what is happening, I noticeed that my directive is loading twice (I don't know exactly why) when the labels work. Look at the log
Log when the labels was not loaded well
Log when the labels is loaded
I am testing with the cache disabled.
You can use ng-cloak directive. The ngCloak directive is used to prevent the AngularJS html template from being briefly displayed by the browser in its raw (uncompiled) form while your application is loading. Check this link : https://docs.angularjs.org/api/ng/directive/ngCloak

ng-click not working in modal angular js [duplicate]

The Situation
Nested within our Angular app is a directive called Page, backed by a controller, which contains a div with an ng-bind-html-unsafe attribute. This is assigned to a $scope var called 'pageContent'. This var gets assigned dynamically generated HTML from a database. When the user flips to the next page, a called to the DB is made, and the pageContent var is set to this new HTML, which gets rendered onscreen through ng-bind-html-unsafe. Here's the code:
Page directive
angular.module('myApp.directives')
.directive('myPage', function ($compile) {
return {
templateUrl: 'page.html',
restrict: 'E',
compile: function compile(element, attrs, transclude) {
// does nothing currently
return {
pre: function preLink(scope, element, attrs, controller) {
// does nothing currently
},
post: function postLink(scope, element, attrs, controller) {
// does nothing currently
}
}
}
};
});
Page directive's template ("page.html" from the templateUrl property above)
<div ng-controller="PageCtrl" >
...
<!-- dynamic page content written into the div below -->
<div ng-bind-html-unsafe="pageContent" >
...
</div>
Page controller
angular.module('myApp')
.controller('PageCtrl', function ($scope) {
$scope.pageContent = '';
$scope.$on( "receivedPageContent", function(event, args) {
console.log( 'new page content received after DB call' );
$scope.pageContent = args.htmlStrFromDB;
});
});
That works. We see the page's HTML from the DB rendered nicely in the browser. When the user flips to the next page, we see the next page's content, and so on. So far so good.
The Problem
The problem here is that we want to have interactive content inside of a page's content. For instance, the HTML may contain a thumbnail image where, when the user clicks on it, Angular should do something awesome, such as displaying a pop-up modal window. I've placed Angular method calls (ng-click) in the HTML strings in our database, but of course Angular isn't going to recognize either method calls or directives unless it somehow parses the HTML string, recognizes them and compiles them.
In our DB
Content for Page 1:
<p>Here's a cool pic of a lion. <img src="lion.png" ng-click="doSomethingAwesone('lion', 'showImage')" > Click on him to see a large image.</p>
Content for Page 2:
<p>Here's a snake. <img src="snake.png" ng-click="doSomethingAwesone('snake', 'playSound')" >Click to make him hiss.</p>
Back in the Page controller, we then add the corresponding $scope function:
Page controller
$scope.doSomethingAwesome = function( id, action ) {
console.log( "Going to do " + action + " with "+ id );
}
I can't figure out how to call that 'doSomethingAwesome' method from within the HTML string from the DB. I realize Angular has to parse the HTML string somehow, but how? I've read vague mumblings about the $compile service, and copied and pasted some examples, but nothing works. Also, most examples show dynamic content only getting set during the linking phase of the directive. We would want Page to stay alive throughout the life of the app. It constantly receives, compiles and displays new content as the user flips through pages.
In an abstract sense, I guess you could say we are trying to dynamically nest chunks of Angular within an Angular app, and need to be able to swap them in and out.
I've read various bits of Angular documentation multiple times, as well as all sorts of blog posts, and JS Fiddled with people's code. I don't know whether I'm completely misunderstanding Angular, or just missing something simple, or maybe I'm slow. In any case, I could use some advice.
ng-bind-html-unsafe only renders the content as HTML. It doesn't bind Angular scope to the resulted DOM. You have to use $compile service for that purpose. I created this plunker to demonstrate how to use $compile to create a directive rendering dynamic HTML entered by users and binding to the controller's scope. The source is posted below.
demo.html
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="angular.js#1.0.7" data-semver="1.0.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.js"></script>
<script src="script.js"></script>
</head>
<body>
<h1>Compile dynamic HTML</h1>
<div ng-controller="MyController">
<textarea ng-model="html"></textarea>
<div dynamic="html"></div>
</div>
</body>
</html>
script.js
var app = angular.module('app', []);
app.directive('dynamic', function ($compile) {
return {
restrict: 'A',
replace: true,
link: function (scope, ele, attrs) {
scope.$watch(attrs.dynamic, function(html) {
ele.html(html);
$compile(ele.contents())(scope);
});
}
};
});
function MyController($scope) {
$scope.click = function(arg) {
alert('Clicked ' + arg);
}
$scope.html = '<a ng-click="click(1)" href="#">Click me</a>';
}
In angular 1.2.10 the line scope.$watch(attrs.dynamic, function(html) { was returning an invalid character error because it was trying to watch the value of attrs.dynamic which was html text.
I fixed that by fetching the attribute from the scope property
scope: { dynamic: '=dynamic'},
My example
angular.module('app')
.directive('dynamic', function ($compile) {
return {
restrict: 'A',
replace: true,
scope: { dynamic: '=dynamic'},
link: function postLink(scope, element, attrs) {
scope.$watch( 'dynamic' , function(html){
element.html(html);
$compile(element.contents())(scope);
});
}
};
});
Found in a google discussion group. Works for me.
var $injector = angular.injector(['ng', 'myApp']);
$injector.invoke(function($rootScope, $compile) {
$compile(element)($rootScope);
});
You can use
ng-bind-html https://docs.angularjs.org/api/ng/service/$sce
directive to bind html dynamically.
However you have to get the data via $sce service.
Please see the live demo at http://plnkr.co/edit/k4s3Bx
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope,$sce) {
$scope.getHtml=function(){
return $sce.trustAsHtml("<b>Hi Rupesh hi <u>dfdfdfdf</u>!</b>sdafsdfsdf<button>dfdfasdf</button>");
}
});
<body ng-controller="MainCtrl">
<span ng-bind-html="getHtml()"></span>
</body>
Try this below code for binding html through attr
.directive('dynamic', function ($compile) {
return {
restrict: 'A',
replace: true,
scope: { dynamic: '=dynamic'},
link: function postLink(scope, element, attrs) {
scope.$watch( 'attrs.dynamic' , function(html){
element.html(scope.dynamic);
$compile(element.contents())(scope);
});
}
};
});
Try this element.html(scope.dynamic);
than element.html(attr.dynamic);

"perfect-scrollbar" (jQuery plugin) isn't initialised properly when a its container is being filled with Angular.js

On my page I programmatically generate a food/drinks menus from a json file with Angular.js. The problem is with the "perfect-scrollbar" used for scrolling the angular-generated content, which appears to require a scroll-wheel event to initialise on these menus. This makes it impossible to scroll on devices without a scroll-wheel. Apart from the angular-generated content other pages initialize perfect-scrollbar properly. This gave me a clue that the problem might lie with the interaction between jQuery world (perfect-scrollbar is a jQuery plugin) and Angular world.
The site is themockingbird.co.uk - navigate to the "Food" and "Drinks" to see the problem in action - can't scroll the content (the perfect-scrollbar won't appear) unless with the mouse scroll-wheel.
I've written this little directive:
mainMenuApp.directive('scrollBar', function(){
return {
restrict: 'C',
template: '<div ng-transclude></div>',
transclude: true,
scope: {},
link: function(scope, element, attrs){
$(element).perfectScrollbar();
//element.perfectScrollbar(); - doesn't work
//angular.element(element).perfectScrollbar(); - doesn't work
}
}
});
to facilitate the "perfect-scrollbar" via angular for the two menus, but this did not solve the problem.
How can I make the perfect-scrollbar work perfectly with angular (pun intended :)?
I appreciate your time.
cheers
Jared
At the time your link function is executed, your menu-food.json and menu-drink.json are not arrived yet and perfectScrollbar needs an update at the time the data arrive, called with:
$(element).perfectScrollbar('update');
Since you have no architecture for handling the food and drinks lists as decoupled watchable values in a controller attached by your directive, you may simply broadcast an event from the root scope, listened by your directive link functions, thus updating the perfectScrollbar instance at the right moment.
I had the same problem when using https://github.com/itsdrewmiller/angular-perfect-scrollbar - the following "add-on directive", solved this problem:
.directive('psMouseOver', function () {
return {
link: function(scope, element) {
element.bind("mouseover", function(e){
e.stopPropagation();
e.preventDefault();
element.perfectScrollbar('update');
});
}
}
});
In your case however, one would just add those lines and write your directive as:
mainMenuApp.directive('scrollBar', function(){
return {
restrict: 'A',
template: '<div ng-transclude></div>',
transclude: true,
scope: {},
link: function(scope, element, attrs){
element.perfectScrollbar();
element.bind("mouseover", function(e){
e.stopPropagation();
e.preventDefault();
element.perfectScrollbar('update');
});
}
}
});
That happen in my case when Angular.js files loaded before JQuery and Perfect Scrollbar scripts files includes.
<script src="assets/libs/jquery.min.js"></script>
<script src="assets/libs/jquery.mousewheel.js"></script>
<script src="assets/libs/perfect-scrollbar.js"></script>
Try to load firstly JQuery, Perfect Scrollbar, and just after AngularJS.

How can I control initialization of an angular directive?

I have a directive that is used as a form control. This directive is hidden in a modal dialog and is shown when the user clicks a button to show the form. Since this directive connects to some web services, I don't want it to initialize unless the user clicks the button and the form displays (to prevent unnecessary web service calls). So, what I'm looking for is a good way for the parent controller to trigger the directive to execute some init code. Here is an example:
App.controller('parentCtrl', ['$scope', function($scope) {
$scope.onButtonClick = function onButtonClick() {
// tell directive to init somehow
};
}]);
App.directive('myDirective', function() {
return {
restrict: 'E',
scope: {},
controller: function($scope, myService) {
function init() {
myService.getData().then(function(value) { //do init stuff });
}
}
});
Assume the template for parentCtrl contains a tag .
Tagging your element in an ng-if will prevent the directive from initializing before it's needed. (scope.loadModal should be false by default)
<my-directive ng-if='loadModal'></mydirective>
Note: Setting scope.loadModal = false after showing the directive once will unload the directive from your DOM. Setting it back to true would reload the directive resulting in another http request.

AngularJS have ng-view display a dynamic footer outside of its div

I'm fairly new to angular.js so I'm seeking advice on how to best implement the functionality described here. I have an html body with header, sidebar and a content area provided by <div class="container-fluid" ng-view></div>. Because this is a standard twitter-bootstrap layout , the ng-view is not a direct descendant of the body element.
I have one view that shall contain an infinitely scrollable list. When at least one element of the list is selected, I want to slide-up a footer that contains contextual actions (the footer shall be position:fixed so that it's always displayed on top of the list). In essence this is similar to what happens on the Windows 8 Metro home-screen when you right click a tile.
The problem I now have is that the footer can't be part of the ng-view partial because it needs to live directly under the body in the DOM. What's a good way to handle this with Angular.js and ng-view when I'd want to keep a single controller for the list and the dynamic footer? If there's a CSS solution to this, I'd be happy to hear about it too.
It sounds like you need to have the footer outside the ng-view element in order to handle the layout you are wanting. I'll leave the layout/CSS to you but the following example demonstrates a way to communicate with the directive if it were to live outside ng-view.
Here's a plunkr (http://plnkr.co/edit/NpoIrOdfvn9XZiXY9YyV?p=preview).
HTML
<body ng-app="someApp">
<div ng-view></div>
<footer-directive></footer-directive>
</body>
JS
angular.module('someApp', []).config(function($routeProvider) {
$routeProvider.when('/', {
template: '<p>template</p>',
controller: 'SomeCtrl'
}).otherwise({
redirectTo: '/'
});
}).service('broadcastService', function($rootScope, $log) {
this.broadcast = function(eventName, payload) {
$log.info('broadcasting: ' + eventName + payload);
$rootScope.$broadcast(eventName, payload);
};
}).controller('SomeCtrl', function(broadcastService, $log, $timeout) {
//fire off showfooter message
broadcastService.broadcast('ShowFooter', {
some: 'data'
});
// wait 3 seconds and hide footer
$timeout(function() {
//fire off hide message
broadcastService.broadcast('HideFooter');
}, 3000);
}).directive('footerDirective', function(broadcastService, $log) {
return {
restrict: 'E',
link: function(scope, elm, attr) {
elm.hide();
scope.$on('ShowFooter', function(payload) {
$log.info('payload received');
$log.debug(payload);
// assuming you have jQuery
elm.show();
});
scope.$on('HideFooter', function() {
// assuming you have jQuery
elm.hide();
});
}
}
});

Categories

Resources