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

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

Related

How to have a AngularJS directive update the anchors generated by another directive

I am new to angular and not sure I am wording my question properly. Here is the issue. I receive a block of html from a database with all tags included
<DOCTYPE>
<html>
<head>...</head>
<body>...</body>
</html>
It can't be helped, I don't have control over anything in the DB, this is what I receive and have to work with.
My angular code to render this info in a kendo grid. I created directives to write render the HTML in a specific column in my grid.
myDirectives.directive('column', function(){
return{
restrict: 'E',
scope: {
value: '=',
text: '=',
},
replace: true,
template: <div><span target-blank>[template html to render to column in the grid]</span></div>,
link: function(scope, element, attrs){
// code to do stuff
}
}
}
I also created another directive:
myDirectives.directive('target-blank', function () {
return {
restrict: 'A',
replace: false,
compile: function (element) {
var elems = (element.prop("tagName") === 'A') ? element : element.find('a');
elems.attr("target", "_blank");
}
};
});
What I am trying to accomplish is to check every anchor in the returned HTML and make the anchors open in a new window. The columns HTML render perfectly with no issue, however, the anchors still keep opening in the same tab and not in a new tab. What am I doing wrong? Thank you in advance.
UPDATE 1: #dfsq, Here is a link of your test that I updated [http://plnkr.co/edit/WVkLDjhAzRxuS1A7mPg0?p=preview] I tried using the ng-bind-html here but it is throwing errors. It works for me in my code though.
In order to define directive target-blank you need to follow camelCase notation in JS:
myDirectives.directive('targetBlank', function () {
return {
restrict: 'A',
replace: false,
compile: function (element) {
var elems = (element.prop("tagName") === 'A') ? element : element.find('a');
elems.attr("target", "_blank");
}
};
});
The problem was that the second directive was firing as soon as the page loaded and not giving time for kendo to render the html. It was purely a timing issue. Adding $timeout dependency solved the issue.

AngularJS wait for tag to load into DOM

I'm adding a google chart via the angular directive into a page and I would like to add an attribute to the element it creates after it's loaded. What is the best way to ensure the element exists before attempting to add the attribute?
From looking around it seems like my directive that I have SHOULD work but does not:
.directive('vdfWidgetGoogleChart', ['$timeout', function ($timeout) {
return {
restrict: 'E',
//replace: true,
templateUrl: 'widgetgooglechart.html',
link: function ($scope, elem, attrs) {
function addTabIndex () {
elem.find('svg').attr({tabindex: -1});
}
$timeout(addTabIndex);
},
scope: {
chartObject: '='
}
}
Personally when doing charting the easiest thing is to append the attribute to the SVG element, then you're being very angular as you aren't looking for elements.
Another option is create a controller and then a directive for the element SVG that requires that controller. Then when you have SVG as a child of that directive your svg directive should get called. (This is a guess I haven't tried it)
<div controller-directive=".."><svg></svg></div>
Then your code would have the SVG when the controller exists as a parent.
Or you can simply adjust your code to look for the svg every 100ms or so until you find it.
function addTabIndex () {
var svg = elem.find('svg');
if (svg.length != 0)
svg.attr({tabindex: -1});
else
$timeout(addTabIndex, 100);
}
$timeout(addTabIndex);

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!

Angular Page Partials Don't allow <script> tags

So long story short, I've tried a billion ways to put a parameter into a string as text, with no solution to be had. I decided that instead I might use a Document.Write to cheat and print a global variable as HTML... However, I discovered that an HTML partial in AngularJS, won't let me use and tags on the partial page.
Example:
$routeProvider
.when('/score', {templateUrl: 'partials/score.html'})
Then, on score.html, I can't put in this statement. It simply does not execute:
<script type="text/javascript">
document.write("This is a test");
//document.write("This is a test"+$rootScope.variable);
</script>
I've also tried window.document.write, even ngApp.document.write. It just doesn't seem to write at all. On network view it looks like the JS isn't running at all. Why? How?
EDIT: I know about data binding. It doesn't work inside an attribute string... I don't know why. This doesn't not work:
<div addthis-toolbox class="addthis_toolbox addthis_default_style addthis_32x32_style social-width" style="width: 150 px">
<a class="addthis_button_twitter" addthis:title="My score is {{score}}"></a>
</div>
EDIT2: Actually, I think that there might be a directive overriding the div class. Here is the angular directive in question:
myApp.directive('addthisToolbox', function() {
return {
restrict: 'A',
transclude: true,
replace: true,
template: '<div ng-transclude></div>',
link: function ($scope, element, attrs) {
// Dynamically init for performance reason
// Safe for multiple calls, only first call will be processed (loaded css/images, popup injected)
// http://support.addthis.com/customer/portal/articles/381263-addthis-client-api#configuration-url
// http://support.addthis.com/customer/portal/articles/381221-optimizing-addthis-performance
addthis.init();
// Ajax load (bind events)
// http://support.addthis.com/customer/portal/articles/381263-addthis-client-api#rendering-js-toolbox
// http://support.addthis.com/customer/portal/questions/548551-help-on-call-back-using-ajax-i-lose-share-buttons
addthis.toolbox($(element).get());
}
}
});
How does one pass data bindings into an attribute if it's got a directive affecting it?

call function inside $sce.trustAsHtml() string in Angular js

I am developing an app using Angularjs and adding HTML using $sce.trustAsHtml() in my page. I want to call a function in above dynamically added content. My html and script as below.
HTML
<div ng-app="ngBindHtmlExample">
<div ng-controller="ngBindHtmlCtrl">
<p ng-bind-html="myHTML"></p>
</div>
</div>
Javascript
angular.module('ngBindHtmlExample', ['ngSanitize'])
.controller('ngBindHtmlCtrl', ['$scope','$sce', function ngBindHtmlCtrl($scope, $sce) {
$scope.myHTML =$sce.trustAsHtml(
'I am an <code>HTML</code>string with links! and other <em>stuff</em>');
$scope.removeExp = function (){
console.log('dfdfgdfgdfg');
}
}]);
jsfiddle
Click Here to see
It's a bit tricky because ng-bind-html will simply insert plain old html and not bother compiling it (so any directives in the html will not be processed by angular.
The trick is finding a way to compile whenever the template changes. For example, you could create a directive that does this. It would look something like:
.directive('compileTemplate', function($compile, $parse){
return {
link: function(scope, element, attr){
var parsed = $parse(attr.ngBindHtml);
function getStringValue() { return (parsed(scope) || '').toString(); }
//Recompile if the template changes
scope.$watch(getStringValue, function() {
$compile(element, null, -9999)(scope); //The -9999 makes it skip directives so that we do not recompile ourselves
});
}
}
});
You can then use it like this:
<p ng-bind-html="myHTML" compile-template></p>
See the working example here:
http://jsfiddle.net/3J25M/2/

Categories

Resources