Watch ng-model in ng-bind-html using AngularJs - angularjs

Here is my Markup
<div ng-app="myApp" ng-controller="myCtrl">
<input type="text" ng-model="name">
<p ng-bind-html="myText|unsafe"></p>
</div>
I am using this code
var app = angular.module("myApp", ['ngSanitize']);
app.controller("myCtrl", function($scope) {
$scope.myText = "My name is: <h1>{{name}}</h1>";
$scope.name="Habib";
});
app.filter('unsafe', function ($sce) {
return function(val) {
return $sce.trustAsHtml(val);
};
});
Output:
My name is: {{}}
Desired Output:
My name is: Habib
I want it should also reflect value from textbox.

EDIT
The reason you are having issues with getting $scope.name bound is because ng-bind-html does not bind that html to the current scope. You can use a directive to fix this. Here is an answer that fixes your issue.
Here is a plunker that adds the directive and shows the behavior you are looking for.
Here is the directive that was added that fixes your issue:
app.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() {
// The -9999 makes it skip directives
// so that we do not recompile ourselves
$compile(element, null, -9999)(scope);
});
}
}
});
You need to declare $scope.name="Habib" before the myText scope variable.
var app = angular.module("myApp", ['ngSanitize']);
app.controller("myCtrl", function($scope) {
$scope.name="Habib";
$scope.myText = "My name is: <h1>{{name}}</h1>";
});
app.filter('unsafe', function ($sce) {
return function(val) {
return $sce.trustAsHtml(val);
};
});

Related

angularjs click event inside the ngBindHtml directive

I have an angularjs sample code snippet here where i can bind the html tags using ng-bind-html directive. But how can I include some other tags like angularjs ng-click, id tag etc inside ngBindHtml directive like
Test
My sample code is here:
var app = angular.module("myApp", ['ngSanitize']);
app.controller("myCtrl", function($scope) {
$scope.myText = "<a href='#' ng-click='someFunction()'>Test</a>";
$scope.someFunction = function(){
alert("Link Clicked");
};
});
FYI, the data is loaded dynamically from server side script and i have to use ng-bind-html inside ng-repeat directive and i have to pass respective id's to click events something like ng-click="myFunction(x.id)" as in sample 2.
As suggested #Dr Jones, you need use $compile directive.
Live example on jsfiddle.
angular.module('ExampleApp', [])
.controller('ExampleController', function($scope) {
$scope.myText = "<button ng-click='someFunction(1)'>{{text}}</button>";
$scope.text = "Test";
$scope.someFunction = function(val) {
console.log(val);
};
})
.directive('bindHtmlCompile', function($compile) {
return {
restrict: "A",
scope: {
bindHtmlCompile: "="
},
link: function(scope, elem) {
scope.$watch("bindHtmlCompile", function(newVal) {
elem.html('');
var newElem = angular.element(newVal);
var compileNewElem = $compile(newElem)(scope.$parent);
elem.append(compileNewElem);
});
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="ExampleApp">
<div ng-controller="ExampleController">
<h3>
Write code for test button
</h3>
<textarea cols="100" ng-model="myText"></textarea>
<div bind-html-compile="myText">
</div>
</div>
</div>

Dynamically creating directive template

I am trying to create template dynamically. When I inject the hard coded value through directives attribute it works fine. But when I assign it using angular variable it does not seem to work. Below is the js code
(function(angular) {
'use strict';
angular.module('docsTemplateUrlDirective', [])
.controller('Controller', ['$scope', function($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
$scope.nameTempl = 'customer-name.html';
$scope.addressTempl = 'customer-address.html';
}])
.directive('myCustomer', function() {
return {
templateUrl: function(elem, attr){
return attr.type;
}
};
});
})(window.angular);
this is html part
<body ng-app="docsTemplateUrlDirective">
<div ng-controller="Controller">
<div my-customer type="{{nameTempl}}"></div>
<div my-customer type="{{addressTempl}}"></div>
</div>
</body>
Instead of using variables if i directly use values it seems to be working fine.
I dont understand why is this happening?
Plunker code
Your problem is that angular first process the template and then the DOM. So when he gets to the attr.type, it's still {{nameTemp1}}, angular has yet manipulated the DOM. My suggestion is, try to pass the address in another way. This plunker shows how you can try creating a service that would hold an object, and then bind the url to the object. Then, inject the service to the directive and use the url. Just make sure to put an "ng-if" on the directive the checks if the scope had created that object and bound it to the service's object
Here is the service
service('service', [function() {
return {
template: {}
};
}])
And this is the controller:
controller('Controller', ['$scope', 'service', function($scope, service) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
$scope.addressTempl = 'customer-address.html';
service.template.url = $scope.addressTempl;
}])
and the directive looks like this:
directive('myCustomer', function(service) {
return {
templateUrl: function(elem, attr){
return service.template.url;
}
};
})
You can use different templates as follows:
Live example on plunker.
(function(angular) {
'use strict';
angular.module('docsTemplateUrlDirective', [])
.controller('Controller', ['$scope', function($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
$scope.nameTempl = 'customer-name.html';
$scope.addressTempl = 'customer-address.html';
}])
.directive('myCustomer', function() {
return {
scope: {
type: "#",
customer:"=myCustomer",
},
template: '<div ng-include="type"></div>'
};
});
})(window.angular);
And html part
<body ng-app="docsTemplateUrlDirective">
<div ng-controller="Controller">
<div my-customer="customer" type="{{nameTempl}}"></div>
<div my-customer="customer" type="{{addressTempl}}"></div>
</div>
</body>
You cannot access scope in templateURL function however there is a way to load template at run time. Plunker - http://plnkr.co/edit/n20Sxq?p=preview
app.directive("cellItem", ["$compile", '$http', '$templateCache', '$parse', function ($compile, $http, $templateCache, $parse) {
return {
restrict: 'A',
link: function(scope , element, attrs) {
scope.$watch(attrs.template, function (value) {
if (value) {
loadTemplate(value);
}
});
function loadTemplate(template) {
$http.get(template, { cache: $templateCache })
.success(function(templateContent) {
element.replaceWith($compile(templateContent)(scope));
});
}
}
}
}]);
Hope this solve your issue.

AngularJS Directive accessing controler scope

TL;DR I solved my problem. Here is plunker with 3 different solutions:
http://plnkr.co/edit/E0ErKs?p=preview
I don`t like slider1 because it stores value in $scope ( {{sliderValue}} ) and according to recommendation from Angular Style Guide we should avoid that.
I don`t like slider2 because it assumes that controler have alias vm in a view (so we create some kind of coupling between view and directive).
Solution 3 looks OK for me. Am I missing something?
How would you write differently this directive to be in complience with Angular philosophy?
INITIAL QUESTION:
I am learning angular and not everything is clear to me yet.
I found this question:
How to use jQuery in AngularJS
So I created working example:
Directive:
(function() {
'use strict';
angular.module('demoApp').directive('slider', function () {
return {
restrict: 'A',
controller: function ($scope, $element, $attrs) {
$scope.onSlide = function (e, ui) {
$scope.sliderValue = ui.value;
$scope.$digest();
};
},
link: function (scope, el, attrs) {
var options = {
value: scope.sliderValue,
slide: scope.onSlide
};
// set up slider on load
angular.element(document).ready(function () {
scope.$slider = $(el).slider(options);
});
}
}
});
})();
Controller:
(function() {
'use strict';
angular.module('demoApp').controller('DemoAppTestCtrl', DemoAppTestCtrl);
DemoAppTestCtrl.$inject = [ '$scope' ];
function DemoAppTestCtrl($scope) {
$scope.sliderValue = 10;
}
})();
And Html page:
<div ng-controller="DemoAppTestCtrl as vm">
Value: {{sliderValue}}
<div slider></div>
</div>
Everything works fine. Angular put slider in place of <div slider> and I can move it and I see changing values in {{sliderValue}}.
Then I found this Angular Style Guide
https://github.com/johnpapa/angular-styleguide
In chapter about controllers they recommend to use controllerAs with vm syntax (because $scope is bad or something).
Ex:
function CustomerController() {
var vm = this;
vm.name = {};
vm.sendMessage = function() { };
}
So I changed my controller to this:
(function() {
'use strict';
angular.module('demoApp').controller('DemoAppTestCtrl', DemoAppTestCtrl);
DemoAppTestCtrl.$inject = [ ];
function DemoAppTestCtrl($scope) {
var vm = this;
vm.sliderValue = 10;
}
})();
And Html page to:
<div ng-controller="DemoAppTestCtrl as vm">
Value: {{vm.sliderValue}}
<div slider></div>
</div>
But i don`t know how to fix my directive.
I want the same functionality, when i move the slider i want to set vm.sliderValue inside controler instead $scope.sliderValue inside scope.
EDIT1:
I was able to make it work by adding $scope.vm inside controller and link functions (because my controller sits in scope as vm). But I am not sure if this is right way to do it, because now my directive assume that there is controller in scope under $scope.vm alias.
Is this bad design or normal way of doing things in Angular ?
(function () {
'use strict';
angular
.module('demoApp')
.directive('slider', slider);
slider.$inject = [ ];
function slider() {
return {
restrict: 'A',
controller: function ($scope, $element, $attrs) {
$scope.vm.onSlide = function (e, ui) {
$scope.vm.sliderValue = ui.value;
$scope.$digest();
};
},
link: function (scope, el, attrs) {
var options = {
value: scope.vm.sliderValue,
slide: scope.vm.onSlide
};
// set up slider on load
angular.element(document).ready(function () {
scope.$slider = $(el).slider(options);
});
}
}
}
})();
EDIT2:
I was able to create working Plunker with 3 different versions:
http://plnkr.co/edit/E0ErKs?p=preview
Use scope: false as a option in the directive.
http://www.undefinednull.com/2014/02/11/mastering-the-scope-of-a-directive-in-angularjs/
try something like this:
angular.module('myApp', []);
angular.module('myApp').controller('DemoAppTestCtrl', DemoAppTestCtrl);
function DemoAppTestCtrl() {
var vm = this;
vm.sliderValue = 10;
}
angular.module('myApp').directive('slider', sliderDirective );
function sliderDirective() {
return {
restrict: 'A',
controller: sliderController,
controllerAs: 'sliderCtrl',
template: "<p>{{sliderCtrl.test}}</p>"
}
}
function sliderController() {
var vm = this;
vm.test = "hello";
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="DemoAppTestCtrl as vm">
Value: {{vm.sliderValue}}
<div slider></div>
</div>
</div>

ng-include with ng-src not working

I'm trying to compile a template which has data-ng-include and data-ng-src defined. I'm trying to set the src by calling getPartial(), which returns the path of the template. But the getPartial() is not getting called.
HTML:
<button ng-click="displayElements();">Display Elements</button>
<div id="container"></div>
JS:
$scope.displayElements = function() {
var template = '<div data-ng-include data-ng-src="getPartial()"></div>';
var linkFn = $compile(template)($scope);
//console.log(angular.element(document.getElementById("container")));
angular.element(document.getElementById("container")).append(linkFn);
}
$scope.getPartial = function() {
console.log("from partial");
return 'hello.html';
}
Plnkr : http://plnkr.co/edit/ig9TAXVpK4k1bQwi9PQo?p=preview
ng-include uses either its own value or value of src attribute to get the path (see documentation). You're trying to use ng-src, which is a directive in its own right. So you need to do either this:
<div data-ng-include="getPartial()"></div>
or this:
<div data-ng-include data-src="getPartial()"></div>
On a sidenote, watching a function call result hurts performance. You'd be better served by putting the resolved source path into a scope variable.
Here is the solution:-
var app = angular.module('plunker', []);
app.controller('MainCtrl', ['$scope', '$compile', function($scope, $compile) {
$scope.displayElements = function() {
var template = '<div data-ng-include data-ng-init="getPartial()" src="htmlToLoad"></div>';
var linkFn = $compile(template)($scope);
angular.element(document.getElementById("container")).append(linkFn);
};
$scope.getPartial = function() {
alert("from partial");
$scope.htmlToLoad = 'hello.html';
};
}]);
http://plnkr.co/edit/y3PgeBMVHytftI7NDSq4?p=preview
'<div data-ng-include data-ng-src="getPartial()"></div>';
replace your code with this
'<div data-ng-include data-ng-init="getPartial()" src="htmlToLoad"></div>';
var app = angular.module('plunker', []);
app.controller('MainCtrl', ['$scope', '$compile', function($scope, $compile) {
$scope.displayElements = function() {
var template = '<div data-ng-include data-ng-init="getPartial()" src="htmlToLoad"></div>';
var linkFn = $compile(template)($scope);
//console.log(angular.element(document.getElementById("container")));
angular.element(document.getElementById("container")).append(linkFn);
}
$scope.getPartial = function() {
alert("sdfsdfsdf");
console.log("from partial");
return 'hello.html';
}
}]);

Angularjs HTML compilation in Controller

I am facing a small problem during the compilation of html code in angularjs. Here is the brief description of my problem :
$scope.report= reportdata;
reportdata is the html code that contains angularcontents like : {{name}} , {{firstname}} etc.
so, I am searching for a function that can compile the above html in my controller just like this :
$scope.compiledReportdata = function() {
$scope.compildeHtml = somefunction(reportdata);
}
Is there any function that can do the trick for me , Please suggest.
This is what i have tried i works for HTML but not for Controller
angular.module('myapp')
.directive('dynamic', function ($compile) {
return {
restrict: 'A',
replace: true,
link: function (scope, ele, attrs) {
ele.bind('blur', function () {
scope.$apply(attrs.uiBlur);
debugger
});
scope.$watch(attrs.dynamic, function (html) {
ele.html(html);
var data1 = ele.html(html);
var data2 = $compile(ele.contents())(scope);
$compile(ele.contents())(scope);
});
}
};
});
You can use the $interpolate service in the controller to interpolate the string...
var app = angular.module('app', ['ngSanitize']);
app.controller('controller', function ($scope, $interpolate) {
$scope.name = 'Costanza';
$scope.firstname = 'George';
$scope.report = '<strong>{{name}}</strong> , {{firstname}}';
$scope.compiledReportdata = function () {
return $interpolate($scope.report)($scope);
};
});
And you can use ngBindHtml with ngSanitize to display it...
<div ng-app="app" ng-controller="controller">
<div ng-bind-html="compiledReportdata()"></div>
</div>
JSFiddle

Resources