Angular.js Directive to display in bold a piece of the string - html

I have a table in angular that is displayed this way:
<table class="row">
<tr ng-repeat="x in myWelcome | filter :search">
<td>{{$index}}</td>
<td>{{x._id}}</td>
<td>{{x.email}}</td>
<td ng-bold-number>{{x.phone}}</td>
<td>{{x.latitude}}</td>
<td>{{x.longitude}}</td>
<td><input type="button" value="Remove" ng-click="removeRow(x._id)"/></td>
</tr>
</table>
The phone column is displayed like this : +1(210)22158765.
I want to create a directive "ng-bold-number" so that the number inside the parenthesis (that is 210 here) would be displayed in bold style like this +1(210)22158765
So I made this directive in Angular :
app.directive('ngBoldNumber', [function() {
return {
restrict: 'A',
link: function($scope, iElement, iAttrs) {
var x = $scope.x.phone.substring(0, $scope.x.phone.indexOf('(')+1)
var y = $scope.x.phone.substring($scope.x.phone.indexOf('(')+1, $scope.x.phone.indexOf(')')+1)
$scope.x.phone=x+y;
}
};
}])
which I am able to cut down the string but I didn't find a way to display it in bold. Can you help me please?

Suppose you would want to go with a directive, you can do it like this : check my plunker here
the directive function itself looks like this:
function boldNumber(){
return {
restrict:"A",
template:"{{pre}} <b>{{bold}}</b> {{post}} ",
replace:false,
scope:{
inBold:"#"
},
link: function(scope, element, attr){
console.log(scope.inBold);
scope.pre = scope.inBold.substring(0, scope.inBold.indexOf('(')+1);
scope.bold = scope.inBold.substring(scope.inBold.indexOf('(')+1, scope.inBold.indexOf(')')+1);
scope.post = scope.inBold.substring(scope.inBold.indexOf(')')+1);
}
};
Basically, what I have added is a template, showing the cuts you made. I had to make one more cut (the final part after the closing ')' ). You have to set replace: false, so your template is appended into the directive element.
Then i have an isolated scope, containing the text that needs to be cut, the phone number. I did simplify the example in my plunker a bit.
The html looks like this then:
<table class="row">
<tr>
<td bold-number in-bold="{{x.phone}}">tryout</td>
<td><input type="button" value="Remove" ng-click="removeRow(x._id)"/></td>
</tr>
</table>
as you can see, the attribute in-bold contains the value for the isolated scope in the directive function. if you need more information, or if something is not clear, please ask.
EDIT: the part about inBold:"#"
in your directive function, when u add the option 'scope', you create a scope that only counts for the directive. The directive itself can no longer reach the scope of the controller. That is why it is called an 'isolated scope'. It exists for your protection. In large single page apps, you might accidentally change scope variables from the controller which you did not want, if you don't have an isolated scope.
However, we do want to be able to pass variables from the controller to the directive, right? So there is the possibility to build in 'border control' for passing variables from controller to directive. In your html, you pass an extra attribute which is called: in-bold. it has a value. This is your 'border control'. The name 'in-bold' gets transformed to camel case (inBold) for use in javascript.
So what it basically means is that the isolated scope of the directive now has a variable called inBold. The value of this variable is passed to the directive from the controller by the attribute "in-bold". This is important that they have the "same" name. For us humans, in-bold is not the same as inBold, but for angular it is. Everything in angular-html connected with a dash (-) gets transformed in javascript to camel case. So for angular in-bold(html) = inBold(javascript). In the directive, you can now access that passed variable as scope.inBold or {{inBold}}.
Then what is the "#" about? well, the "#" tells the directive that we are dealing with a String value, which is only single-bound. That means that if we change the value of inBold in the directive, the changes will not be visible in the controller! This is important. If you would want two way binding, you would need to use the following:
scope:{
inBold:"="
}
I hope that was a bit clear to you...

This is part of working solution to demonstrate how you can make it bold style. Now you can format the rest part of number
angular.module("myApp", [])
.controller('myController', ['$scope', '$log',
function($scope, $log) {
$scope.phone = '+1(210)22158765';
$scope.isBold = false;
$scope.countryCode = null;
}
])
.directive('ngBoldNumber', [
function() {
return {
restrict: 'A',
template: '<span ng-class="{bold: isBold}">{{countryCode}}</span><span>{{phone}}</span>',
link: function($scope, iElement, iAttrs) {
//debugger;
var x = $scope.phone.substring(0, $scope.phone.indexOf('(') + 1);
var y = $scope.phone.substring($scope.phone.indexOf('(') + 1, $scope.phone.indexOf(')') + 1);
if (x && y) {
$scope.countryCode = x + y;
$scope.isBold = true;
}
}
};
}
]);
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.8/angular.js" data-semver="1.4.8"></script>
<script src="script.js"></script>
<style>
.bold {
font-weight: bold;
}
</style>
</head>
<body ng-app="myApp" ng-controller="myController">
<table class="row">
<tr>
<td ng-bold-number>{{phone}}</td>
</tr>
</table>
</body>
</html>

Related

How can I use ng-bind-html inside my directive?

I'm using templates based on my JSON. So, I can't really use my ng-bind-html like I would normally do.
Seems like the only option I have is to use my sanitized html inside an directive.
Looking for similar questions, I couldn't figure it out how to apply in my case.
Yes, I am pretty newbie into angular.
I'm currently receiving this data from my controller:
$scope.safecontainers = $sanitize($scope.containersmsg);
In my html would normally be like this:
<p ng-bind-html="containersmsg"></p>
But I don't want this, I need to use this ng-bind-html inside a directive!
Some people have talked about $compile, but I couldn't really figure it out how to apply in my case.
EDIT:
Based on comments, i'll add more code to help you guys further understand my goal.
Inside my index.html I'm declaring the controllers needed and calling my
<ng-view></ng-view>
Then, based on what I receive, i'll load one view or another:
<div ng-if='setores[0].SetorTema == "1"'>
<theme-one titulo="{{setores[0].SetorNome}}" logo="
{{setores[0].SetorLogo}}" evento="{{evento[0].EventoNome}}">
</theme-one>
// I omitted some of the parameters because they ain't relevant
</div>
My template is like this: (Just a little part of it to avoid much useless code)
<section class="target">
<div class="col-md-6 col-xs-12">
<div class="" ng-repeat="banner in title">
<div class="target-title">{{ banner.BannerLevelTitulo }}
</div>
<div class="target-desc">{{banner.BannerLevelDescricao}}</div>
</div>
</div>
<div class="col-md-6 col-xs-hidden">
<div class="target-image"><img ng-src="{{targetimage}}" alt="">
</div>
</div>
</section>
This is the controller I want my sanitized code.
hotsite.controller('containerController', function($scope, $http, $sanitize)
{
$scope.containers = [];
$scope.containersmsg = '';
$scope.safecontainers = $sanitize($scope.containersmsg);
$http.get('/Admin/rest/getContainer')
.then(function onSuccess(response) {
var data = response.data;
$scope.containers = data;
$scope.containers = response.data.filter(containers =>
containers.ContainerAtivo);
$scope.containersmsg = $scope.containers[0].ContainerDesc;
})
.catch(function onError(response) {
var data = response.data;
console.log(data);
});
});
This is a piece of my directive:
angular.module('hotsiteDirectives', [])
.directive('themeOne', function($compile) {
var ddo = {};
ddo.restrict = "AE";
ddo.transclude = true;
ddo.scope = {
titulo: '#',
...
(a lot of other scope's)
contimg: '#'
};
ddo.templateUrl = 'app/directives/theme-one.html';
return ddo;
})
And yes, I am calling the ngSanitize
var hotsite = angular.module('hotsite',['ngRoute', 'hotsiteDirectives',
'ngSanitize']);
TL;DR
This is how my code looks like inside a directive, with raw html and not rendered:
This is how it works with ng-bind-html, formatted html
If I do put this inside my view
<p ng-bind-html="containersmsg"></p>
It will be alright, all of it working like it should.
BUT, I need to call this only inside my directive, and I don't know how to do it.
So, with this context:
How can I put my sanitized html inside my directive and template?
You don't even have to trust the html to render it using ngBindHtml because the directive already does it for you. You basically need to create a parameter attribute for your directive to hold the html string, so, inside the directive's template, you use ng-bind-html="myParam".
The following snippet implements a simple demonstration of creating a directive that receives and renders an html input parameter that comes from a controller.
angular.module('app', ['ngSanitize'])
.controller('AppCtrl', function($scope) {
$scope.myHtml = '<div><b>Hello!</b> I\'m an <i>html string</i> being rendered dynamicalli by <code>ngBindHtml</code></div>';
})
.directive('myDirective', function() {
return {
template: '<hr><div ng-bind-html="html"></div><hr>',
scope: {
html: '='
}
};
});
<div ng-app="app" ng-controller="AppCtrl">
<my-directive html="myHtml"></my-directive>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular-sanitize.js"></script>

How to Display value of dynamically created varible using AngularJS

My HTML template code is:
<p>{{"fieldname_"+lang}}</p>
In the controller I have the following:
$scope.lang = "mr";
$scope.fieldname_mr = "Dynamic varible";
I want the result to be Dynamic varible, but it is fieldname_mr.
How can I achieve that?
You can use bracket notation to achieve this:
angular.module('app', [])
.controller('testController',
function testController($scope) {
$scope.lang = "mr";
$scope.dynamicVars = {fieldname_mr : "Dynamic varible"};
});
<body ng-app="app">
<div class="table-responsive" ng-controller="testController">
{{dynamicVars["fieldname_" + lang]}}
</div>
</body>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
that you wnt is not able, i made a middleware solution that mantains all your needed but passed by a function that made the return of var, understand that angular force to made like this
<div ng-controller="MyCtrl">
<span ng-init="">{{getValue('fieldname_'+lang)}}</span>
</div>
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.lang = "mr"; $scope.fieldname_mr = "Dynamic varible";
$scope.getValue =function(name){
return $scope[name] ||'no existe man';
}
}
Yes you can do it like you set your dynamic variable in angular as follow:
what you want variable name store in variable than follow code:
$scope[dynamic_var]=Value of variable;
when you fetch this value as like:
Use this when you not fix property of scope
alert($scope[dynamic_var])
use when you have fix property name
alert($scope.dynamic_var)

Pass HTML element with attribute via an HTML element's attribute

I have an HTML element which I annotated with an angular directive, in this case translate from ngTranslate. ngTranslate gives the option to interpolate values in the translation string at runtime with arbitrary angular expressions (and even a subcompile option for compiling containes directives).
Now my problem is I would like to interpolate {{name}} in the message Hello {{name}} with a custom directive <test blah="'Robert'"></test>. That is, in the end I expect to get the following HTML as output from the example below:
<body>
<div ng-controller="Ctrl">
<p translate="VARIABLE_REPLACEMENT" translate-values="{ name: '<test blah="'Test'"></test>' }" translate-compile>Hi <span>Hello Robert</span></p>
<span>Hello Robert</span>
</div>
</body>
JS Code:
var translations = {
VARIABLE_REPLACEMENT: 'Hi, {{name}}'
};
var app = angular.module('myApp', ['pascalprecht.translate']);
app.config(['$translateProvider', function ($translateProvider) {
// add translation table
$translateProvider
.translations('en', translations)
.preferredLanguage('en');
}]);
app.controller('Ctrl', ['$scope', function ($scope) {
}]);
app.directive('test',function () {
return {
restrict: 'E',
template: '<span>Hello {{blah}}</span>',
replace: true,
scope: {
blah: '='
}
};
});
HTML (relevant part):
<body>
<div ng-controller="Ctrl">
<p translate="VARIABLE_REPLACEMENT" translate-values="{ name: '<test blah="'Robert'"></test>' }" translate-compile></p>
<test blah="'Robert'"></test>
</div>
</body>
You can play with it in this Plunker: http://plnkr.co/edit/FQZS4eaZVm52sbVzQ1xb?p=preview
The problem: Passing an HTML element through the attribute of another HTML element results in parsing problems with ". I tried to escape the "(\") of the attribute of the element that is passed in but that doesn't seem to work either. How can I pass an HTML element with attributes through the attribute of another HTML element?

Compiling dynamic HTML strings from database

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

How to specify model to a ngInclude directive in AngularJS?

I would like to use the same HTML template in 3 places, just each time with a different model.
I know I can access the variables from the template, but there names will be different.
Is there a way to pass a model to the ngInclude?
This is what I would like to achieve, of course the attribute add-variable does not work now. Then in my included template, I would acces the detailsObject and its properties.
<pane title="{{projectSummary.ProjectResults.DisplayName}}">
<h2>{{projectSummary.ProjectResults.DisplayName}}</h2>
<ng-include src="'Partials/SummaryDetails.html'" init-variable="{'detailsObject': projectSummary.ProjectResults}"></ng-include>
</pane>
<pane title="Documents" header="true"></pane>
<pane ng-repeat="document in projectSummary.DocumentResults" title="{{document.DisplayName}}">
<h2>{{document.DisplayName}}</h2>
<ng-include src="'Partials/SummaryDetails.html'" add-variable="{'detailsObject': document}"></ng-include>
</pane>
<pane ng-repeat="header in [1]" title="Languages" header="true"></pane>
<pane ng-repeat="language in projectSummary.ResultsByLanguagePairs" title="{{language.DisplayName}}">
<h2>{{document.DisplayName}}</h2>
<ng-include src="'Partials/SummaryDetails.html'" add-variable="{'detailsObject': language}"></ng-include>
</pane>
If I took a bad approach with using ng-include, is there something else I should try?
There is a rather simple solution, although I must admit, it's not what Misko would recommend. But if creating a directive is an overkill for you and getting Brice's patch is not feasible then the following will help you.
<div ng-repeat="name in ['A']" ng-include="'partial.html'"></div>
<div ng-repeat="name in ['B']" ng-include="'partial.html'"></div>
<script type="text/ng-template" id="partial.html">
<div>{{ name }}</div>
</script>
It's quite evident why it works. See an example here: http://jsfiddle.net/Cndc6/4/
NOTE: this is not my original answer but this is how I'd do this after using angular for a bit.
I would create a directive with the html template as the markup passing in the dynamic data to the directive as seen in this fiddle.
Steps/notes for this example:
Define a directive with markup in the templateUrl and attribute(s) used to pass data into the directive (named type in this example).
Use the directive data in the template (named type in this example).
When using the directive in the markup make sure you pass in the data from the controller scope to the directive (<address-form type="billing"></address-form> (where billing is accessing an object on the controller scope).
Note that when defining a directive the name is camel cased but when used in the markup it is lower case dash delimited (ie it's named addressForm in the js but address-form in the html). More info on this can be found in the angular docs here.
Here is the js:
var myApp = angular.module('myApp',[]);
angular.module('myApp').directive('addressForm', function() {
return {
restrict: 'E',
templateUrl: 'partials/addressform.html', // markup for template
scope: {
type: '=' // allows data to be passed into directive from controller scope
}
};
});
angular.module('myApp').controller('MyCtrl', function($scope) {
// sample objects in the controller scope that gets passed to the directive
$scope.billing = { type: 'billing type', value: 'abc' };
$scope.delivery = { type: 'delivery type', value: 'def' };
});
With markup:
<div ng-controller="MyCtrl">
<address-form type="billing"></address-form>
<address-form type="delivery"></address-form>
</div>
ORIGINAL ANSWER (which is completely different than using a directive BTW).
Note: The fiddle from my original answer below doesn't appear to work anymore due to an error (but keeping it here in case it is still useful)
There was a discussion about this on the Google Group you can see it here.
It looks like this functionality is not supported out of the box but you can use Brice's patch as described in this post.
Here is the sample code from his jsfiddle:
<script id="partials/addressform.html" type="text/ng-template">
partial of type {{type}}<br>
</script>
<div ng-controller="MyCtrl">
<ng-include src="'partials/addressform.html'" onInclude="type='billing'"></ng-include>
<ng-include src="'partials/addressform.html'" onLoad="type='delivery'"></ng-include>
</div>
There is a pull to fix this but it looks like it's dead:
https://github.com/angular/angular.js/pull/1227
Without modifying the Angular source code this will solve the problem in a reusable not-too-hacky-feeling way:
directive('newScope', function() {
return {
scope: true,
priority: 450,
};
});
And an example:
<div new-scope ng-init="myVar = 'one instance'" ng-include="'template.html'"></div>
<div new-scope ng-init="myVar = 'another instance'" ng-include="'template.html'"></div>
Here is a Plunker of it in action:
http://plnkr.co/edit/El8bIm8ta97MNRglfl3n
<div new-scope="myVar = 'one instance'" ng-include="'template.html'"></div>
directive('newScope', function () {
return {
scope: true,
priority: 450,
compile: function () {
return {
pre: function (scope, element, attrs) {
scope.$eval(attrs.newScope);
}
};
}
};
});
This is a directive that combines new-scope from John Culviner's answer with code from Angular's ng-init.
For completeness, this is the Angular 1.2 26 ng-init source, you can see the only change in the new-scope directive is the addition of scope: true
{
priority: 450,
compile: function() {
return {
pre: function(scope, element, attrs) {
scope.$eval(attrs.ngInit);
}
};
}
}
Quick'n'dirty solution:
<div ng-init="details=document||language||projectSummary.ProjectResults">
I hear you! ng-include is not that reusable because it has access to the global scope. It's a little weird.
There should be a way to set local variables. Using a new directive instead of ng-include is a cleaner solution.
The ideal usage looks like:
<div ng-include-template="'Partials/SummaryDetails.html'" ng-include-variables="{ 'detailsObject': language }"></div>
The directive is:
.directive(
'ngIncludeTemplate'
() ->
{
templateUrl: (elem, attrs) -> attrs.ngIncludeTemplate
restrict: 'A'
scope: {
'ngIncludeVariables': '&'
}
link: (scope, elem, attrs) ->
vars = scope.ngIncludeVariables()
for key, value of vars
scope[key] = value
}
)
You can see that the directive doesn't use the global scope. Instead, it reads the object from ng-include-variables and add those members to its own local scope.
It's clean and generic.