Angular Expressions Missing in Dynamically Generated Template - javascript

I am having problems with a dynamically generated directive (using a function) and angular expressions being filtered out in the final markup ending up on the screen. I've created a JSFiddle displaying the problem here.
I am generating the template dynamically because we needed a way to generate the markup based on the attributes inside the element.
Basically when defining an expression like so:
angular.module('ui.directives', []).directive('uiBar', function() {
return {
restrict: 'E',
template: function(element, attrs) {
console.log('hello');
return '<div>lol: {{ user }}</div>';
}
};
});
And place it into the body like so:
<div ng-app="myApp">
<ui-bar>I should change to iambar</ui-bar>
</div>
The resulting markup is:
<div ng-app="myApp" class="ng-scope">
<ui-bar><div class="ng-binding">lol: </div></ui-bar>
</div>
The expression is stripped out for some reason. Has anyone experienced this?

I do not quite understand what you are trying to accomplish with a dynamically generated template. Below is what I would typically do to solve your example.
The HTML would look like this.
<ui-bar new-value="hello">I should change</ui-bar>
The return statement of the directive would look something like this
return {
restrict: 'E',
template: "<div>lol: {{ user }}</div>",
link: function (scope, element, attrs){
scope.user = attrs.newValue;
} // end link
} // end return
The above code works in your jsFiddle.
If you could add more information we may be able to give more examples.

Related

How do I use Angular 1.x template interpolation inside an HTML comment? [duplicate]

I'd like to add an HTML comment with an interpolated expression within an ng-repeat block. However, when I try to do this, the expression is not interpolated. For example:
<tr ng-repeat="item in items"
<!-- ID that I don't want the user to see, but want for debugging = {{item.id}} -->
<td>{{item.prettyName}}</td>
<td>{{item.someProperty}}</td>
<td>{{item.someOtherProperty}}</td>
</tr>
When I view the DOM (i.e. the Elements tab in Chrom DevTools), I just see the uninterpolated string ("{{item.id}}") instead of the interpolated value.
What's the correct syntax here?
This is way overkill, since you could just use the suggestions in the comments of display: none or similar, but just as a fun exercise:
The syntax to invoke a directive on a comment is:
<!-- directive: foo expression -->
I was hoping that something like ng-bind supported comments, but it doesn't. But you could easily create your own:
app.directive('comment', function($interpolate) {
return {
restrict: 'M',
scope: true,
link: function(scope, element, attrs) {
var comment = $interpolate(attrs.comment)(scope);
element.replaceWith("<!-- " + comment + "-->" );
}
};
});
And the usage is:
<!-- directive: comment "something something {{item.id}}" -->

Using a reusable directive for form inputs

I just read through the directive docs and I'm still not understanding how I'd accomplish the following with reusable code. I have multiple form fields that would best be used in a select > option setup, however I'm looking to replace that with a directive with a template because of how mobile browsers handle selects (iOS magnifies the options and some of my values are too long to be viewed in the display).
So my template would look something like this:
<div class="list">
<div class="option" ng-repeat="option in form.questionOneOptions" ng-click="selectOption(option)">
{{option}}
<i class="checkIcon" ng-if="option.isSelected"></i>
</div>
</div>
This would be the only thing on the detail page. Its parent page being the list of fields you're filling out, which is where the data needs to be available. I just don't know where to start on the directive. There is an isolated scope for each question, which holds the options for that question. There needs to be a way to give the directive the list of options. There is an encompassing scope for all the questions to keep the recorded answers in a single object like form.
I know I could do this with a single controller and copy/pasting the above and changing form.questionOneOptions with one massive object living in the controller, but I'm trying to do this the right way by limiting my DOM manipulation to directives.
You'll want to use the html you have there as the template for your directive. Then you implement selectOptions in your link function.
app.directive('gsQuestion', function() {
return {
restrict: 'E',
require: 'ngModel',
scope: {
ngModel: '=',
options: '='
},
template:'<div class="list">'+
'<div class="option" ng-repeat="option in options" ng-click="selectOption(option)">'+
'{{option}}'+
'<i class="checkIcon" ng-if="option.isSelected"></i>'+
'</div></div>',
link: function(scope, element, attrs) {
scope.selectOption = function(option)
{
// implement selectOption
}
}
};
});
Then you can use the directive in your html.
<gs-question ng-model="myValue1" options="form.questionOneOptions"></gs-question>
<gs-question ng-model="myValue2" options="form.questionTwoOptions"></gs-question>
Just to be clear, a directive can share data to/from the view through the use of the directive's $scope variables.
angular.module('app', [])
.directive('mySampleDirective', function(){
return{
restrict: 'AE',
scope: {
data: '=' // this sets up a two way binding
}
},
link: function(scope, element, attributes){
console.log(scope.data) // <---- this is where you would do DOM manipulation,
// because you have access to the element.
}
})
And then in your markup, pass in the data you want to make available in your directive.
<my-sample-directive data="FeeFee"></my-sample-directive>

Unable to change inner HTML of a table

I am very new to AngularJS. I am facing difficulty in changing the inner HTML of a table. I am storing a string in a variable, the string include 3 types of tags strong tag this works well and two tags tr and td when I replace the inner value of table with JavaScript variable then it does not convert the tr and td tags into the DOM elements.
Here is my JavaScript code:
var app = angular.module('myApp', ['ngSanitize']);
app.controller('personCtrl', function($scope,$http)
{
$scope.fun = function()
{
varible="<tr><td><strong>Id</strong></td><td><strong>ahmed</strong></td><td><strong>45kg</strong></td></tr>";
$scope.bind=varible;
};
});
Here is my HTML Code
<div ng-app="myApp" ng-controller="personCtrl">
<table ng-bind-html="bind">
</table>
<button ng-click="fun()">check</button>
</div>
You need to use $sce to trust that string as html otherwise Angular won't use them because unsafe:
JSFiddle
app.controller('dummy', function($scope,$sce)
{
$scope.fun = function()
{
varible="<tr><td><strong>Id</strong></td><td><strong>ahmed</strong></td><td><strong>45kg</strong></td></tr>";
$scope.bind = $sce.trustAsHtml(varible);
};
});
It should be better to use a directive, this is a really simple example:
Directive JSFiddle
HTML:
<div ng-app="app" ng-controller="dummy">
<table><custom-table ng-show="bind"></custom-table></table>
<button ng-click="bind=true">check</button>
</div>
JS:
app.directive('customTable',
function(){
return {
restrict: 'AE',
template: '<tr><td><strong>Id</strong></td><td><strong>ahmed</strong></td><td><strong>45kg</strong></td></tr>',
link: function(scope, element, attr) {
//If you need do something here
}
};
});
When manipulating the DOM by adding elements to it or anything, try using an Angular Directive.
Directives have a special property called link that allows you to interact with the DOM.
This should be used whenever possible.
Also, to do things like this, look into ng-repeat & two-way Data Binding in Angular.
Angular shines here, so get to know it a bit more.

Issue with element.html() in Angular directive

I am new to Angular. I have a directive and in the linkFunction I am able to set the scope to a certain value by using attributes["attribute-value"]. I was expecting element.html() to provide the inner html of the directive.
.directive("sfGroupbar", function () {
var linkFunction = function (scope, element, attributes) {
scope.text = element.html();
scope.attr = attributes["text"];
};
return {
restrict: 'E',
templateUrl: "controls/groupbar.html",
link: linkFunction,
scope: {}
};
In my view, I am using the directive like so...
<sf-groupbar warning="50" error="80" text="Web Server">Some Text</sf-groupbar>
In groupbar.html, I am using the code like so...
<div ng-controller="groupbarController">
{{text}} <br />
{{attr}}
</div>
I was expecting to see "Some Text" and "Web Server" as output. I am getting only Web Server as output, and instead of "Some Text", I am getting the following as output...
<div ng-controller="groupbarController">
{{text}} <br />
{{attr}}
</div>
You have to set transclude property of the directive to true and have to include ng-transclude attribute inside the template or templateurl 's HTML element to make innerHTML of the directive to render.
Here is the working plunker based on your code,
http://embed.plnkr.co/sXoLPxeFA21fxzzeAcVs/preview
Hope this helps!!!!
You need to include text and the other attributes in your scope definition like so
scope { text : '=' } and also you might wanna add transclude option to true to try and get the text inside your directive.
I'm sure you'll be interested to look at this part of the documentation
Directive to manipulate the DOM

How to use ng-bind on html that is inserted with innerHTML inside a directive

I made a custom directive that had two ng-repeats inside of it. One ng-repeat was nested inside the other one. I got the code to work, and it performs well in chrome but on the iPad and iPhone it is sluggish.
There are 10 sections with 5 rows each and it needs to be very fast when it comes to scrolling and changing the bindings. I think the slowdown comes from all the loops through the bindings but only one array needs to be changed on user input. The rest of the bindings never change after the page loads.
So I am trying to figure out a way to load nested unordered lists while only binding one variable. This is pseudo code for my directive.
.directive('myDirective', function($compile) {
return {
restrict: 'A'
link: function(scope, elm, attrs) {
outerList = '<ul><li>statically generated content that does not change'
outerList += '<ul><li ng-bind="I only need to bind one thing"><li></ul>'
outerList += < /ul>'
elm[0].innerHTML = outerList
}
}
});
As you can see I am trying to generate the html content, and then insert it with innerHTML. The problem is the ng-bind isn't working when I do it this way. I tried to $compile it again but that didn't change anything.
Does anyone have a better way? I don't care how hideous the solution is I just really need this part of the app to be super fast. The main thing is I don't want ng-repeat unless there is a way to make it do its thing on load and then never loop through anything again.
I would like to do this in the most Angular way possible but I realize I might have to do something that goes completely against Angular philosophy
Here is an example of how to modify your code in order to bind some variable in a directive from a scope outside of it. I've used $compile to ensure that your directive DOM manipulation has its own directives compiled. I've used replaceWith to replace the directive element with your compiled DOM:
HTML
<div ng-app="myApp">
<div ng-controller="ctrlMain">
<div my-directive="bindMe"></div>
</div>
</div>
JavaScript
var app = angular.module('myApp',[]);
app.controller('ctrlMain',function($scope){
$scope.bindMe = {id:1,myvar:"test"};
});
app.directive('myDirective', function($compile){
return{
restrict: 'A',
scope: {
varToBind: '=myDirective'
},
link: function(scope, elm, attrs){
outerList = '<ul><li>statically generated content that does not change'
outerList += '<ul><li ng-bind="varToBind.myvar"><li></ul>'
outerList += '</ul>';
outerList = $compile(outerList)(scope);
elm.replaceWith(outerList);
}
}
});
Here is a demo

Categories