AngulaJs ng-click in expression html [duplicate] - angularjs

This question already has answers here:
How to make ng-bind-html compile angularjs code
(8 answers)
Closed 3 years ago.
I am displaying HTML in a AngularJS expression.
In My HTML
<div ng-bind-html="myText"></div>
And In My Controller
$scope.myText = $sce.trustAsHtml("<div class='my-style' ng-click='onClick(10)'>click me</div>");
$scope.onClick = function(value)
{
console.log("onClick: value:"+value);
}
But I am not able to get the click event
UPDATE
In one of the question I see it has be described about how to add HTML tags in the text or how to compile HTML. But my question is related to how to get ng-click event from the complied HTML. I don't see any of the answers providing this solution.

Sorry for again writing the code :
First give a unique id to your div :
<div id="test"></div>
Then Change that code to below one:
var myText = "<div class='my-style' ng-click='onClick(10)'>click me</div>";
var element = angular.element(document.querySelector('#test'));
var generated = element.html(myText);
$compile(generated.contents())($scope);
This will manage your code to work. cheers!

Use directive to implement this functionality. Check demo here http://plnkr.co/edit/tTgVCMt7JqvaQOrjhLLL?p=preview
JS:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.testClick = function(value) {
alert("onClick: value:"+value);
};
});
app.directive("clickDynamic", function(){
return {
template:"<div class='my-style' ng-click='testClick(10)'>click me</div>"
};
});
HTML:
<body ng-controller="MainCtrl">
<div click-dynamic></div>
</body>

I have append the html using id or class
var add = angular.element( document.querySelector( '#updatechoose' ) );
add.append('<div class="form-group" ng-repeat="updatedata in getdata">'+
'<div>'+
'<input type="file" id="imgupload" ng-model="attachment" imgepath="{{updatedata.imagepath}}" imgname="{{updatedata.imagename}}" name="file[]" style="padding-left: 15px;" class="imgupload col-md-4 cropit-image-input" />'+
'<i class="glyphicon glyphicon-trash" **ng-click="remove()"**></i>'+
'</div>'+
'</div>');

One solution is to use plain javascript and get the scope. This way you can call your inside methods.
<div ng-bind-html="myText"></div>
$scope.myText = $sce.trustAsHtml( '<div class="button" onClick="var scope = angular.element(this).scope();scope.testClick(10);">click me</div>' );
Please note that I'm using onClick instead of ng-click, then I get the scope, this way I can call the inner methods.
It's a dirty solution, but maybe it can help you.

I've added a snippet, which shows an implementation via a directive and the $compile service.
angular.module('TestApp', [])
.directive('compileDir', function($compile) {
return {
restrict: 'E',
scope: {
dynTpl: '='
},
template: 'Num clicked: {{numClicked}}',
link: function(scope, elem) {
scope.numClicked = 0;
scope.click = function() {
scope.numClicked++;
};
var tpl = $compile(scope.dynTpl)(scope);
elem.append(tpl);
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.12/angular.min.js"></script>
<div ng-app="TestApp">
<compile-dir dyn-tpl="'<button ng-click=\'click()\'>Click</button>'"></compile-dir>
</div>

Related

HTML string not binded with angular [duplicate]

This question already has answers here:
ng-bind-html doesnt work for Input tags
(3 answers)
Closed 5 years ago.
<!DOCTYPE html
<body>
<div ng-app="myApp" ng-controller="myCtrl">
<p ng-bind-html="myText"></p>
</div>
<script>
var app = angular.module("myApp", ['ngSanitize']);
app.controller("myCtrl", function($scope) {
$scope.myText = "<input type='text' value='here'>";
});
</script>
<p><b>Note:</b> This example includes the "angular-sanitize.js",
which has functions for removing potentially dangerous tokens from the HTML.</p>
</body>
</html>
I'm new to angular as well. I could not bind the html text to the angular view. Any suggestions could help.
You shoud use $sce.trustAsHtml() like this
$scope.myText=$sce.trustAsHtml("<input type='text' value='here'>")
Like this fiddle
but then you cannot bind scope variables to your html so best way is writing a directive which can be replace with ng-bind-html-unsafe
something like
.directive('bindUnsafeHtml', ['$compile', function ($compile) {
return function(scope, element, attrs) {
scope.$watch(
function(scope) {
// watch the 'bindUnsafeHtml' expression for changes
return scope.$eval(attrs.bindUnsafeHtml);
},
function(value) {
// when the 'bindUnsafeHtml' expression changes
// assign it into the current DOM
element.html(value);
// compile the new DOM and link it to the current
// scope.
// NOTE: we only compile .childNodes so that
// we don't get into infinite loop compiling ourselves
$compile(element.contents())(scope);
}
);
};
}])
working fiddle

How can I use isolated scope with a component and a directive?

The goal here is to let MainCtrl know when there is an error(s) found in the directive. The error must be displayed here:
<div ng-if="errors[0]">Error 1: {{errors[0]}}</div>
How can I get isolated scope with a directive inside a component? The following application works if you uncomment the 2 lines mentioned below. As it is, I get error:
Multiple Directive Resource Contention
I can read the causes. I need to know how to fix this while still allowing the directive to have isolated scope. I may have 3-4 of these directives on a page and each one needs it's own unique of errors that is also available to the parent.
(working case example on codepen)
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope) {
$scope.errors = [false, false];
$scope.text = "bobby";
});
app.directive('testDirective', function(){
return {
restrict: 'A',
scope: {
errors: '=',
text: '#'
},
link: function($scope, $element, $attr, ngModel) {
console.log('link fired');
console.log('errors: ', $scope.errors);
console.log('scope.text', $scope.text);
$attr.$observe('text', function (val) {
if($scope.text === 'bobby'){
$scope.errors[0] = true;
}else{
$scope.errors[0] = false;
}
});
},
template: '<p>text: {{ text }} </p>'
+ '<p>errors: {{errors}}</p>'
+ '<p><input type="text" ng-model="errors" /></p>'
};
});
app.component('panel', {
bindings: {
},
template: [
'<div>',
'</div>'
].join(''),
controller: function() {
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.11/angular.min.js"></script>
<section ng-app="app" ng-controller="MainCtrl">
<h3>Parent Scope</h3>
<p>errors: {{errors}}</p>
<input type="text" ng-model="text"></div>
<div ng-if="errors[0]">Error 1: {{errors[0]}}</div>
<div ng-if="errors[1]">Error 2: {{errors[1]}}</div>
<!-- UNCOMMENT THE FOLLOWING 2 LINES AND THIS APP WILL WORK
<h3>Directive by itself</h3>
<div test-directive text="{{text}}" errors="errors"><div>
-->
<h3>Directive in component</h3>
<panel test-directive text="{{text}}" errors="errors"></panel>
</section>
After researching, I noticed Angular only returns bool from $validators (as opposed to object). At this point I decided my approach was wrong. I decided to create a unique $valiators for each unique error message. Then use ng-message for the output.
In order to work with multiple components on the same page, I also have to check the ngModel.$error as part of validation. This blog covers the basic approach.

Angular 2 way binding doesn't work with template even though I'm not using primatives

I can't figure out what's wrong with my code. I'm following the example of this fiddle in order to bind the two inputs. What I'm trying to do is just a little more involved- I'd like to load a certain template based on an attribute passed into the directive. I can't figure out what's wrong over here.
This is my html
<!-- template -->
<script type="text/ng-template" id="X-template.html">
<button ng-click='clickMe()'><b>Check model</b></button>
Directive: <input ng-model="this.test"></input>
</script>
<div ng-controller="MyCtrl">
<my-directive type="X"></my-directive>
No Directive:<input ng-model="this.test"></input>
{{this.test}}
</div>
And my JS:
var app = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.this= {test : "asdf"};
$scope.clickMe = function() {
alert($scope.this.test);
}
}
app.directive('myDirective', function() {
return {
restrict: 'E',
compile: function(element, attrs) {
element.append('<div ng-include="\'' + attrs.type + '-template.html\'"></div>');
}
}
});
The issue is with trying to use this, which is a reserved keyword in JavaScript, as a property name.
Try using a different property name. I changed your example to use foo in the link below.
http://jsfiddle.net/01h3ne4y/

Accessing controller function in Directive scope

I am trying to access the function defined in the controller scope from directive nested in another directive. I am using the "&" and passing the function name as the attribute to the directive, however I am still not able to reach the function in the controller scope.
Could some one correct where I am going wrong? I had spent few hours trying to find that I have to use the "&" for reaching controller scope but stuck here after whatever I did.
JSFiddle here - http://jsfiddle.net/fwR9Q/12/
Code:
<div ng-controller="PlayerCtrl">
<div class="col-md-4 col-sm-6 col-xs-12" ng-repeat="video in videos" >
<tf-video src="{{video.src}}" width="{{video.width}}" handleClick="playVideo(videoId, modeNum)" height="{{video.height}}" title="{{video.title}}"/>
</div>
</div>
<script>
var myApp = angular.module('myApp', []);
myApp.controller('PlayerCtrl',
function PlayerCtrl($scope,$log, trailers)
{
$scope.videos = trailers;
$scope.playVideo = function(videoId, modeNum){
alert("video Id = " + videoId + "; mode Num = " + modeNum);
return false;
};
}
);
myApp.directive('tfVideo', function() {
return{
restrict: 'AE',
scope: {
src: '#',
handleClick: '&',
width: '#width',
height: '#height'
},
template: '<img src="http://img.youtube.com/vi/{{src}}/0.jpg" height="{{height}}" width="{{width}}"/>'
};
});
myApp.factory('trailers', function(){
var trailerVideos = [
{
src:"6kw1UVovByw",
width:"324",
height:"300"
},
{
src:"uWgDVz4NEO4",
width:"324",
height:"300"
}
];
return trailerVideos;
});
</script>
Thank you
1) On your directive declaration:
<tf-video src="{{video.src}}" width="{{video.width}}" handleClick="playVideo(videoId, modeNum)" height="{{video.height}}" title="{{video.title}}"/>
Since attributes use the snake-cased form.
Instead of: handleClick=
You want the snake cased: handle-click=
2) In your template:
template: '<img src="http://img.youtube.com/vi/{{src}}/0.jpg" height="{{height}}" width="{{width}}"/>'
Instead of: {videoId: {{src}}, modeId: modeNum{{$parent.$index}} }
You want: {videoId: src, modeNum: $parent.$index }
Since you need the "modeNum" parameter name to match between the template and the directive and you want to map directly to the variables, not expressions.
Updated fiddle

Getting MathJax to update after changes to AngularJS model

I am trying to use AngularJS two-way binding text which includes Latex style equations. I would like to call MathJax to format the equations, but I'm not sure of the best way to ensure that MathJax is called after AngularJS finishes changing the model. I think I need a callback. Here is my JavaScript:
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.Update = function() {
$scope.Expression = 'Evaluate: \\( \\frac{9}{4} \\div \\frac{1}{6} \\)';
MathJax.Hub.Queue(["Typeset", MathJax.Hub]);
}
$scope.Expression = 'Evaluate: \\( \\frac{5}{4} \\div \\frac{1}{6} \\)';
}
And here is my HTML:
<div ng-controller="MyCtrl">
<button ng-click="Update()">Update</button>
{{Expression}}
</div>
Fiddle is here: http://jsfiddle.net/LukasHalim/UVjTD/1/. You'll notice that on the fiddle the original expression isn't removed even after you click the update button twice - seems like a bug or conflict.
Having wasted many days (and maybe weeks) fighting MathJax, I'm all too familiar with its various quirks with updating math expressions on the fly. I'm brand new to Angular but this gave me a good chance to dive in and I ended up with a solution which solves my problems -- hopefully it'll solve yours as well.
Live demo: jsfiddle
Instead of using the plain interpolation that Angular provides, I created a new directive based on ng-bind called mathjax-bind.
If expression is a variable containing math code, then instead of \( {{expression}} \) you can write:
<span mathjax-bind="expression"></span>
and everything will be typeset and updated at the appropriate times.
The supporting code for the directive follows:
myApp.directive("mathjaxBind", function() {
return {
restrict: "A",
controller: ["$scope", "$element", "$attrs",
function($scope, $element, $attrs) {
$scope.$watch($attrs.mathjaxBind, function(texExpression) {
var texScript = angular.element("<script type='math/tex'>")
.html(texExpression ? texExpression : "");
$element.html("");
$element.append(texScript);
MathJax.Hub.Queue(["Reprocess", MathJax.Hub, $element[0]]);
});
}]
};
});
Simplest, fastest and most stable solution:
$rootScope.$watch(function(){
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
return true;
});
Advantages:
Easy to setup, just copy this code.
Everything on your page is typeset.
It renders much faster than the other solutions. This is because it can render the page in one go. Other answers here wait for one item to finish, until they typeset the next one. That makes rendering veeeery slow if there are for example multiple mathjax-bind directives (as another answer suggests). This point is the reason I was looking for a different answer.
You can still easily exclude elements using the option “ignoreClass” in your mathjax settings.
Benchmarking:
100 mathjax-bind directives took 63 seconds, while with this method it took 1.5 second to render the page. I know that this function will be executed a lot since it's called on every digest cycle, however, it doesn't noticeably slow down the page.
I created a simple fiddle expanding on Ben Alpert's answer. Here's the fiddle and plunk.
Specifically If a text has only a part of it to be converted by Mathjax, you can use this.
For inline mathjax you must surround the text by $, and for block display you must surround the block by $$. (You can use any format you like if you create the corresponding regex)
app.js
MathJax.Hub.Config({
skipStartupTypeset: true,
messageStyle: "none",
"HTML-CSS": {
showMathMenu: false
}
});
MathJax.Hub.Configured();
var myApp = angular.module("myApp", []);
myApp.directive("mathjaxBind", function() {
return {
restrict: "A",
scope:{
text: "#mathjaxBind"
},
controller: ["$scope", "$element", "$attrs", function($scope, $element, $attrs) {
$scope.$watch('text', function(value) {
var $script = angular.element("<script type='math/tex'>")
.html(value == undefined ? "" : value);
$element.html("");
$element.append($script);
MathJax.Hub.Queue(["Reprocess", MathJax.Hub, $element[0]]);
});
}]
};
});
myApp.directive('dynamic', function ($compile) {
return {
restrict: 'A',
replace: true,
link: function (scope, ele, attrs) {
scope.$watch(attrs.dynamic, function(html) {
html = html.replace(/\$\$([^$]+)\$\$/g, "<span class=\"blue\" mathjax-bind=\"$1\"></span>");
html = html.replace(/\$([^$]+)\$/g, "<span class=\"red\" mathjax-bind=\"$1\"></span>");
ele.html(html);
$compile(ele.contents())(scope);
});
}
};
});
function MyCtrl($scope, $element) {
$scope.html = "A coin of is $ \\frac{5}{4} $ thrown $$\\frac{1}{6}$$ dfv";
}
index.html
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML&delayStartupUntil=configured&dummy=.js"></script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.2.x" src="http://code.angularjs.org/1.2.7/angular.js" data-semver="1.2.7"></script>
<script src="app.js"></script>
</head>
<body>
<div ng-controller="MyCtrl">
<input type="text" ng-model="html"/><br/>
<div dynamic="html"></div>
</div>
</body>
style.css
input[type="text"] {
width: 800px;
}
.red{
color:red;
display:inline-block;
}
.blue{
color:blue;
display:block;
}
Take a look at http://jsfiddle.net/pz5Jc/
In your template:
{{Label}} <span id="mathElement">{{Expression}}</span>
In your controller:
$scope.Update = function() {
$scope.Expression = '\\frac{9}{4} \\div \\frac{1}{6}';
$scope.Label = 'Updated Expression:'
var math = MathJax.Hub.getAllJax("mathElement")[0];
math.Text('\\frac{4}{4} \\div \\frac{2}{6}');
}
Couple of points:
I'm not too familiar with mathjax, but:
Splitting the label out from the expression allows you to work with the expression directly.
You need to manually pick up a DOM element to force a refresh of the expression. This isn't a very 'angular' way to do things unfortunately - but when mathjax parses the expression (and inserts it's own DOM elements), it pushes those elements outside the angular bindings.
Fix here is to specifically select the correct mathjax element and call a text change function to update the expression.
Here's a directive that lets you use double curly markup inside the expression (and doesn't require setting an expression variable on the scope). It's based on this blog post, except I only support MathJax, and I save the compiled DOM, so that it updates on changes to scope variables.
As Alex Osborn said, it's best to separate non-math from math.
Usage:
<p>This is inline math: <latex>x^{ {{power}} }</latex>,
and this is display math: <div latex> y^{ {{power}} } .</div></p>
In a snippet:
angular.module('app', [])
.controller('ctrl', function($scope) {
$scope.power = "\\sin(x^2)";
})
.directive('latex', function() {
return {
restrict: 'AE',
link: function(scope, element) {
var newDom = element.clone();
element.replaceWith(newDom);
var pre = "\\(",
post = "\\)";
if (element[0].tagName === 'DIV') {
pre = "\\[";
post = "\\]";
}
scope.$watch(function() {
return element.html();
}, function() {
console.log(element);
newDom.html(pre + element.html() + post);
MathJax.Hub.Typeset(newDom[0]);
});
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<div ng-app="app" ng-controller="ctrl">
<p>Power:
<input ng-model="power" />
</p>
<p>This is the inline latex,
<latex>x^{ {{power}} }</latex>, followed by some display mode latex
<div latex>y^{ {{power}} } = {{power}}.</div>And that's it!
</p>
</div>
A simple solution is to use $timeout to put MathJax.Hub.Queue(["Typeset", MathJax.Hub]) in the browser event queue (see Run a directive after the DOM has finished rendering).
Something like this:
var app = angular.module('myApp', []);
app.controller('myController', function ($scope, $timeout) {
controller = this;
$scope.Update = function () {
$scope.value = " \\( \\frac{5}{4} \\div \\frac{1}{6} \\)";
$timeout(controller.updateMathJax, 0);
}
this.updateMathJax = function () {
MathJax.Hub.Queue(["Typeset", MathJax.Hub]);
}
});
You can try with my modifications http://jsfiddle.net/bmma8/4/
modify input or click on button will update your expression.
js:
MathJax.Hub.Config({
extensions: ["tex2jax.js"],
jax: ["input/TeX","output/HTML-CSS"],
tex2jax: {inlineMath: [["$","$"],["\\(","\\)"]]}
});
var myApp = angular.module('myApp',[]);
function MyCtrl($scope, $log) {
var QUEUE = MathJax.Hub.queue; // shorthand for the queue
$scope.Update = function() {
QUEUE.Push(["Text",MathJax.Hub.getAllJax("MathOutput")[0],"\\displaystyle{"+ $scope.Expression+"}"]);
//$scope.Expression = 'Updated Expression: \\( \\frac{9}{4} \\div \\frac{1}{6} \\)';
//MathJax.Hub.Queue(["Typeset", MathJax.Hub]);
}
$scope.Expression = 'Original Expression: \\( \\frac{5}{4} \\div \\fra
and html:
<div ng-controller="MyCtrl">
<button ng-click="Update()">Update</button>
<input ng-model="Expression" ng-change="Update()">
<div id="MathOutput">
You typed: ${}$
</div>
</div>
Alexandre
I actually thought of another solution. When you render some angular and math you do this:
ANGULAR CONTROLLER
$scope x = 5;
HTML
<h3> {{ '$ Multiplication = '+ x + ' * 2 =' + (x*2) + '$'}} </h3>
Formated Math Jax result
Multiplication = 5 * 2 = 10
The key is to include the dollar signs inside the brackets as text. When Angular renders them, the dollar signs will appear as plain text, but when the Math Jax format comes into action it will recognize the dollar signs and do the magic.
I Build a directive for this....
FIDDLE: http://jsfiddle.net/8YkUS/1/
HTML
p data-math-exp data-value="math">
JAVASCRIPT
appFlipped.directive("mathExp", function () {
return {
scope: {
value: "="
},
link: function (scope, el) {
var domEl = el[0];
scope.$watch("value", function (newValue) {
//nothing to do here
if (newValue == null || window.MathJax == null)return;
//update the dom with the new value and pass the hub for styling the equation
domEl.innerHTML = '`' + newValue + '`';
MathJax.Hub.Queue(["Typeset", MathJax.Hub, domEl]);
});
}
}
});
I fiddled a bit more on Roney's solution. The display math should be displayed in display mode; with
<script type="math/tex; mode=display">
I added an attribute to the generated span to indicate that.
Fiddle is here http://jsfiddle.net/repa/aheujhfq/8/

Resources