How can I control initialization of an angular directive? - javascript

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.

Related

angular directive require non parent directive controller

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 ?

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);

Clicking on element not hit angularjs directive link function

It seems my li elements in angularjs directive not responding clicking event.
HTML:
<my-selbg>
<ul>
<li ng-repeat="bgimage in bgimages"><img src={{bgimage}} width="85" height="82" dir={{bgimage}}></li>
</ul>
</my-selbg>
JS:
var mlwcApp = angular.module('mlwcApp', [])
.controller('BgImagesListController', function($scope, $http) {
$http.get("http://localhost:8080/webcontent/bg_images").success(function(response) {
$scope.bgimages = response;
});
})
.directive('myselbg', function(){
return {
restrict: 'E',
scope: true,
link: function(scope, element, attrs){
var elementOne = angular.element(element.children[1]);
var elementTwo = angular.element(element.children[2]);
var elementThree = angular.element(element.children[3]);
setUpBGImg = function(){
console.log('link function');
};
$(elementOne).on('click', setUpBGImg);
$(elementTwo).on('click', setUpBGImg);
$(elementThree).on('click', setUpBGImg);
}
};
});
I have 3 li elements and clicking any of them dose not hit the code in link function. Anyone has idea?
You're new to angular, by the looks of it.
First off, before going any further - your directive will not even bind at all in the state it is in. You've got an element directive (which is fine, though if I were you I'd make it an attribute directive by restricting on A, which allows you to then apply it to the list rather than an element above it) named myselbg in your code. However, your markup is set as my-selbg, which would then look for the angular directive mySelbg, which does not exist.
In addition to this, your directive will evaluate before the list is rendered (thanks to the order of priority in execution). You have two choices to go around this:
You can do something like this: https://jsfiddle.net/a01n3srw/1/ . Really not recommended - I am using $timeout in order to evaluate code after the current refresh cycle is done, at which point the list fully exists
You can use the simple ngClick angular core directive in order to make this easy. Added bonus, when your function that you evaluate starts modifying scope, you won't shoot yourself in the foot using the previous method and having to use $apply

Set form attributes on angular.ready function

I have a form in my modal(angular ui). I need to set the attributes of the form from my controller.
my HomepageController:
var iframeModal = $modal.open({
templateUrl: 'immdModal.html',
controller: 'immdController',
scope: $scope
});
iframeModal.result.then(function(selectedItem) {
$scope.selected = selectedItem;
}, function() {
});
And I have :
<div id="immdPwdResetFrame">
<form id="myForm"></form>
<iframe name="hidden_form_for_iframe"></iframe>
</div>
<script>
$(document).ready(function() {
document.forms["myForm"].submit();
});
</script>
in my modal HTML(immdModal.HTML), and I have
angular.element(document).ready(function() {
var form = document.getElementById("myForm");
form.setAttribute("method", "post");
});
in my immdController controller. But I get the error in console as : form is undefined.
How can I set the attributes of the form from the controller?
DISCLAIMER: Trying to give a hacky workaround to the question's situation which seems strange: How to set the attribute on the form. This solution does exactly that using angular.
You could always define a directive for your form to perform DOM manipulations like:
var app = angular.module('myapp', []);
// Given that the myapp is properly referenced in the HTML (i.e. <html ng-app="myapp">)
// this will execute on any element with an attribute named 'immd-modal-form'
app.directive("immdModalForm",function(){
return {
restrict: 'A',
link: function(scope, element, attrs) {
element.attr("method", "post");
}
};
});
then
<form immd-modal-form></form>
Using angular.ready() / $(document).ready() in this fashion for a modal form is calling for trouble. These events are used upon the initial load of the page, not for modal initialization.
I believe that calling "submit()" on a form will probably kick you out of the page (redirect). There are many issues in your question. You really need to tell us what you are trying to achieve. I am sure there is a much simpler way to do all of this!

How to add AngularJS directives to .NET WebForms Update Panels?

I have a project that uses legacy code in .NET and WebForms. The legacy code uses several Update Panels.
My hope is to use AngularJS without impacting the legacy code base.
<div ng-app="myApp">
<NS:TheUserControl ID="TheUserControl1" runat="Server" />
</div>
Here is the javascript:
var app = angular.module("myApp", []);
app.run(function ($rootScope, $compile) {
function insertDirective() {
jQuery(targetElementSelector).attr("my-directive", "");
}
insertDirective();
var mgr = Sys.WebForms.PageRequestManager.getInstance();
mgr.add_endRequest(function (sender, args) {
insertDirective();
$compile(jQuery(targetElementSelector))($rootScope);
});
});
The code above adds an attribute to an HTML element inside NS:TheUserControl, and the attribute specifies a directive (see directive below).
Then, these steps are used when the update panel changes:
Detect the change using Sys.WebForms.PageRequestManager (EndRequest listener)
Re-add the directive using javascript
Run $compile on the newly-inserted directive
Here is the directive:
app.directive("myDirective", function () {
return {
template: "<span ng-repeat='item in items'>{{item}}</span>"
+ "<span ng-transclude></span>"
, transclude: true,
, controller: function ($scope) {
$scope.items = ["a", "b", "c"];
}
};
});
This almost works... except...
Problem 1:
AngularJS creates a scope associated with each instance of a directive. When the update panel inside NS:TheUserControl changes the DOM, the previous scopes are still present. I can see this in Batarang (a Chrome developer tool for AngularJS).
Problem 2:
For a brief moment after the update panels change, I get:
abc <-- Initial page load
abcabc <-- Subsequent update panel changes
abcabcabc
abcabcabcabc
Then, a moment later after each update panel change, the content jumps back to the correct:
abc
Questions:
So, how do I either:
Remove the orphaned scopes?
Or, incorporate AngularJS directives in a way that plays nice with the update panels?

Categories

Resources