Expressions inside ng-bind-html not rendering - angularjs

We have dynamic templates. Inside ng-repeat I am using ng-bind-html which calls a method that returns HTML, but the expressions in that HTML were not rendering. Any idea why?
<div ng-bind-html="vm.getSimpleLayoutHtml(item)" class="ng-binding ng-scope">
<div class="col-xs-4" ng-if="item.Account.Name">{{ item.Account.Name }}</div>
</div>
Here is the controller:
function TestController($scope, resource,$sce)
{
var vm= this;
vm.getSimpleLayoutHtml = function (item) {
var result = resource.getLayout(item.attributes.type, "Simple");
return $sce.trustAsHtml(result.Html);
};
};

Give a try to ng-HtmlCompiler #https://github.com/rroxysam/ng-htmlCompiler

Related

Unable to get content AngularJs dynamic popover inside ngrepeat

I need to display a small popover which should open on click and goaway on clicking anywhere on the page.
I found a plunker (http://plnkr.co/edit/K7cYQSDEBS3cHvDfJNLI?p=preview) which matches this requirement however, unable to get it to work inside ng-repeat.
I saw several answers and Plunker examples but not able to get this to work.
Here is my html
<div ng-controller="TestController">
<div class="row" style="background-color: #ebebeb !Important; ">
<div style="text-align:center">
<table style="width:100% !important;">
<tr ng-repeat="member in TeamMembers" style="font-size:18px !important; height: 108px;">
<td style="display:block;margin-top:30px;text-align:left;"> {{member.FirstName}} {{member.LastName}} <i class="fa fa-info-circle" aria-hidden="true" ng-show="member.Description != null" popover-template="dynamicPopover.templateUrl" popover-placement="bottom" popover-elem descr="{{member.Description}}"></i></td>
</tr>
</table>
</div>
</div>
...
<script type="text/ng-template" id="descriptionModal.html">
<div class="adp-info-dialog">
<div class="modal-body">
<div class="row">
<div class="col-md-8 col-md-offset-1">
<div class="form-group">
<label class="fieldset-label">Test {{ dynamicPopover.descr }}</label>
</div>
</div>
</div>
</div>
</div>
</script>
Here is the JS
testApp.controller('TestController', function ($scope, $rootScope, $log, $modal, SiebelAccountTeamService, $filter, $element) {
$scope.dynamicPopover = {
templateUrl: 'descriptionModal.html',
descr: null
};
var result = TestService.GetTeamMembers();
result.then(function (data) {
$scope.TeamMembers = data.data;
}, function (e) {
console.log(e);
}).finally(function () {
$scope.CompleteLoading();
});
});
testApp.directive('popoverClose', function ($timeout) {
return {
scope: {
excludeClass: '#'
},
link: function (scope, element, attrs) {
var trigger = document.getElementsByClassName('trigger');
function closeTrigger(i) {
$timeout(function () {
angular.element(trigger[0]).triggerHandler('click').removeClass('trigger');
});
}
element.on('click', function (event) {
var etarget = angular.element(event.target);
var tlength = trigger.length;
if (!etarget.hasClass('trigger') && !etarget.hasClass(scope.excludeClass)) {
for (var i = 0; i < tlength; i++) {
closeTrigger(i)
}
}
});
}
};
});
testApp.directive('popoverElem', function () {
return {
scope: {
descr: '#'
},
link: function (scope, element, attrs) {
$scope.dynamicPopover.descr = scope.descr,
alert($scope.dynamicPopover.descr),
element.on('click', function () {
element.addClass('trigger');
});
}
};
});
Appreciate your help.
Update:
To show the data of the ng-repeat inside the popover content, we need to access the individual objects through the $index of the ng-repeat. Refer the below example.
Plunkr Demo
The problem here is that you are using ng-repeat which creates a new scope read more here.
Since replicating the issue with your code is tedious, I tried replicating the issue with the plunkr!
Solution:
Plunkr Demo
You can simply define a new controller inside the descriptionModal.html like so
HTML:
<script type="text/ng-template" id="myPopoverTemplate.html">
<div class="adp-info-dialog" ng-controller="tester">
<div class="modal-body">
<div class="row">
<div class="col-md-8 col-md-offset-1">
<div class="form-group">
<label class="fieldset-label">Test {{ $parent.$parent.dynamicPopover.content }}</label>
</div>
</div>
</div>
</div>
</div>
</script>
JS:
app.controller('tester', function ($rootScope, $scope) {
console.log($scope.$parent.$parent.dynamicPopover.title);
});
Then, we will be able to access the parent scope, using $parent, the html inside the above script uses the $parent to get the variable!
Please note: It took me two $parent to reach the required $scope to access the scope variable. In your scenario it will also require two, the way to check how many is needed is use console.log($scope), then open the console(F12), then traverse through the objects $parent property till you find the correct $scope. Then count the number of $parent traversed, that will be your required number of $parent to traverse!
P.S:
There is another method you can do this, since this method will require a significant rewrite of your code, I will provide the GIST, you can use the controller as syntax and access the correct scope.
Here is the SO Answer giving the method to do it
SO Answer
I hope this fixes you issue.

How to forbideen updating model in Angular JS?

I have nested controller like:
<div ng-controller="MainController">
<div ng-controller="ChildController"></div>
</div>
Inside Child controller there is function:
$scope.isStatus = function() {
return function(item) {
}};
This function is used in ng-repeat.
So, problem is when I modify model of controller MainController it reload model of ChildController, It means that function isStatus() works again and reload model. How to fix it?
You can use controllerAs syntax to avoid this
<div ng-controller="MainController as main">
<div ng-controller="ChildController as child">
<ul ng-repeat ="item in child.item">
</div>
</div>
And in js
function ChildController (){
var vm = this;
vm.isStatus = function() {
return function(item) {
}};
}
you can see more infomation in here
https://github.com/johnpapa/angular-styleguide/tree/master/a1#controllers

how to call controller function from template in angularjs

I am creating an html from controller and appending this html to included html file in template view. but I am unable to call controller function from there when i am clicking on checkbox. I want to get all those values in that function.
This is my test html code:-
<div ng-app>
<div ng-controller="TodoCtrl">
<div id="priceappend"></div> <!-- This div I am using in included html say test1.html -->
<!-- I cannot iterate array over here, As I am appending priceHtml into test1.html which I am including using ng-include -->
</div>
</div>
and this is my controller js code:-
function TodoCtrl($scope,$window) {
$scope.pricerangelist = {
chkbox1:{selected:false,id:'below100',minvalue:'0',maxvalue:'100'},
chkbox2:{selected:false,id:'below400',minvalue:'100',maxvalue:'400'},
};
var priceHtml = '';
priceHtml +='<div class="ui-checkbox ui-checkbox-row"><label for="below100" class="ui-btn ui-corner-all ui-btn-d ui-btn-icon-left">100 and below</label><input type="checkbox" min_value="0" max_value="100" value="100 US$ and below" class="range myinput large custom" ng-change="pricelistAction(pricerangelist)" ng-model="pricerangelist.chkbox1.selected" name="range[]" id="below100"></div>';
priceHtml += '<div class="ui-checkbox ui-checkbox-row"><label for="below400" class="ui-btn ui-corner-all ui-btn-d ui-btn-icon-left">101 and 400</label><input type="checkbox" min_value="101" max_value="400" value="101 US$ and 400 US$" class="range myinput large custom" ng-change="pricelistAction(pricerangelist)" ng-model="pricerangelist.chkbox2.selected" name="range[]" id="below400"></div>';
var myEl = angular.element( document.querySelector( '#priceappend' ) );
myEl.append(priceHtml);
$scope.pricelistAction = function(val){
console.log(val);
//this function is not calling.
};
}
here is the plnkr
You need to compile the newly created HTML so as to use scope.
so replace myEl.append(priceHtml); with myEl.append($compile(priceHtml)($scope));
http://jsfiddle.net/U3pVM/23071/
Add $complie to your controller like this function TodoCtrl($scope,$window, $compile)
Now before appending the html string call $compile, so that the functions in the template string can be bound to $scope.
myEl.append($compile(priceHtml)($scope))

Angularjs change view template after link clicked

I am trying to change html template after link is clicked. Value is boolean, initial value is true and appropriate template is loaded, but when value changed to false new template is not loaded, I don't know the reason. When initial value of boolean is true other template is loaded successfully, but on method called not. Please, help.
Here is my code:
TaskCtrl
app.controller('TasksCtrl', ['$scope', 'TaskService', function ($scope, TaskService) {
// initialize function
var that = this;
that.newTask = true;
that.name = "My name is Nedim";
that.templates = {
new: "views/task/addTask.html",
view: "views/task/viewTask.html"
};
// load all available tasks
TaskService.loadAllTasks().then(function (data) {
that.items = data.tasks;
});
$scope.$on('newTaskAdded', function(event, data){
that.items.concat(data.data);
});
that.changeTaskView = function(){
that.newTask = false;
console.log("New task value: " + that.newTask);
};
return $scope.TasksCtrl = this;
}]);
task.html
<!-- Directive showing list of available tasks -->
<div class="container-fluid">
<div class="row">
<div class="col-sm-6">
<entity-task-list items="taskCtrl.items" openItem="taskCtrl.changeTaskView()"></entity-task-list>
</div>
<div class="col-sm-6" ng-controller="TaskDetailCtrl as taskDetailCtrl">
<!-- form for adding new task -->
<div ng-if="taskCtrl.newTask" ng-include="taskCtrl.templates.new"></div>
<!-- container for displaying existing tasks -->
<div ng-if="!taskCtrl.newTask" ng-include="taskCtrl.templates.view"></div>
</div>
</div>
entityList directive
app.directive('entityTaskList', function () {
return {
restrict: 'E',
templateUrl: 'views/task/taskList.html',
scope: {
items: '='
},
bindToController: true,
controller: 'TasksCtrl as taskCtrl',
link: function (scope, element, attrs) {
}
};
});
directive template
<ul class="list-group">
<li ng-repeat="item in taskCtrl.items" class="list-group-item">
<a ng-click="taskCtrl.changeTaskView()">
<span class="glyphicon glyphicon-list-alt" aria-hidden="true"> </span>
<span>{{item.name}}</span>
<span class="task-description">{{item.description}}</span>
</a>
</li>
{{taskCtrl.newTask}}
Without any plunker or JSFiddle I can't tell for sure, but it might be issue with ng-if. I'm thinking of two workarounds.
First that I think is better. Use only 1 ng-include and only change the template.
HTML:
<entity-task-list items="taskCtrl.items" openItem="taskCtrl.changeTaskView('view')"></entity-task-list>
...
<div ng-include="taskCtrl.currentTemplate"></div>
JS:
that.currentTemplate = that.templates.new;
...
that.changeTaskView = function(template) {
that.currentTemplate = that.templates[template];
};
Or if you don't like this solution, try with ng-show instead of ng-if. With ng-show the elements will be rendered with display: none; property when the page loads, while with ng-if they will be rendered when the passed value is true.
Hope this helps you.

ng-show not working for dynamically added HTML within my controller

Please see relevant JSFiddle
Within this code I dynamically added in sanitized HTML code using ng-bind-html but when assigning ng-show to the html it does not work. I tried using $compile but so far nothing as well.
I am trying to hide and show an element using ng-show but both are visible.
JS:
var app = angular.module('App', ['ngSanitize'])
app.controller('MyCtrl', function($scope, $compile) {
$scope.myHTML = [];
$scope.myHTML.push('<li ng-show = "visible(1)">Test1</li>');
$scope.myHTML.push('<li ng-show = "visible(0)>Test2</li>');
$scope.visible = function(arg)
{
if (arg == 1) return true;
if (arg == 0) return false;
}
$compile($scope.myHTML)($scope);
});
HTML:
<div ng-app="App">
<div ng-controller="MyCtrl">
<ul ng-repeat="snippet in myHTML" ng-bind-html="snippet"></ul>
</div>
</div>
Your controller should pretty much never be aware of markup, ever. That's the power of MVC. You should repeat across a real dataset and build up markup in your view. Something like the following, where you set items = [item1, item2] in a controller.
<div ng-app="HelloApp">
<div ng-controller="MyCtrl">
<ul ng-repeat="item in items">
<li ng-show = "test($index)">Test{{$index}}</li>
</ul>
</div>
</div>
If you really need to do it the way you're doing, you should use a custom directive and use angular.element to append your compiled html to the current directive element. ng-bind-html doesn't expect a compiled node, rather just an html string.
Your method is not true.
First , your ng-repeat is in ul, scope.myHTML will loop ul instead of adding DOM in ul, so you should ng-repeat in li. code like this:
<ul>
<li ng-repeat="snippet in myHTML" ng-bind-html="snippet" ng-show="snippet.visible">{{ snippet.content }}</li>
</ul>
app.controller('MyCtrl', function($scope, $compile) {
$scope.myHTML = [{content:'Test1', visible:1},{content:'Test2', visible:0}];
$scope.visible = function(arg)
{
if (arg == 1) return true;
if (arg == 0) return false;
}
});
Maybe, you want to just add DOM like jQuery. code like this:
<ul>
</ul>
app.controller('MyCtrl', function($scope, $compile) {
$scope.visible = function(arg)
{
if (arg == 1) return true;
if (arg == 0) return false;
}
$('ul').html($compile('<li ng-show="visible(1)">Test1</li><li ng-show="visible(0)">Test2</li>')($scope));
});
i used $timeout function it's working with me : sample code
$scope.allowDelete = false;
$scope.allowDeleteCount = '';
$timeout(function () {
$('div').append($compile('<button type="button" ng-show="allowDelete" class="btn btn-danger btn-sm btn-delete"><i class="fa fa-trash-o"></i> Delete <span>{{allowDeleteCount}}<span></button>')($scope))
},1000);

Resources