Is there a callback for ng-bind-html-unsafe in AngularJS - javascript

I would like to remove some of the elements that are brought into the DOM by this...
<div ng-bind-html-unsafe="whatever"></div>
I wrote a function that will remove the elements, but I need a way to trigger the function after ng-bind-html-unsafe is complete. Is there a callback for ng-bind-html-unsafe or a better approach?

ng-bind-html-unsafe has been removed from the current version (1.2+) of angular. I would recommend using the $sanitize service in the new version of angular. You'll need to include the sanitize library and add it to your module.
That way you can do whatever you want once the sanitize operation is complete and not worry about a callback.
A quick and dirty implementation:
<!doctype html>
<html lang="en">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.11/angular.js"></script>
<script src="libs/angular/angular-sanitize.js"></script>
</head>
<body ng-app="myApp">
<div ng-controller="myController">
<div>{{sanitized}}</div>
</div>
<script>
angular.module('myApp', ['ngSanitize'])
.controller('myController', ['$scope', '$sanitize', function($scope, $sanitize) {
$scope.sanitized = $sanitize('someTextFromSomeSource');
// Do whatever you want here with the cleaned up text
}])
</script>
</body>
</html>

I would move the html in your demo to a directive:
<div ng-bind-html-unsafe="whatever"></div>
in the directive I would manipulate the html based on my needs, I am here basing this on an assumption since I am not sure how often or how whatever variable is being updated so the generic solution will be a $watch listener over this variable like this:
$scope.$watch('whatever',function(newValue,oldValue, scope){
//do something
});
All this code in my new directive, probably you will want to use the postLink function
Here is the Documentation from Angular about this.
Post-linking function
Executed after the child elements are linked. It is safe to do DOM transformation in the post-linking function.

Related

Extend ng-if or implement similar directive

I would like to include or exclude content based on a boolean value.
The boolean value comes from a service that provides feature toggles. I don't want to inject the directive in a lot of places or use $root in a condition for ng-if.
So I would like to create an AngularJS attribute directive that is similar to ng-if but has the service injected and includes/excludes the content based on the boolean value, instead of getting a condition expression from the attribute value in the template.
Is it possible to somehow extend the existing ng-if directive? If not, what would be the easiest/best way to implement similar behavior?
Here is a simple directive embedding a service which when it executes the linking function will decide whether or not to display the DOM element based on the value returned from the service. The directive relies on the currentUser service to return the value which would normally be part of the ng-if expression. It evaluates this in the linking function so the assumption is that this value is static and does not need to be re-evaluated. In fact, it cannot be re-evaluated as we totally remove the element from the DOM.
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#1.4.8" data-semver="1.4.8" src="https://code.angularjs.org/1.4.8/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<button is-admin>ADMIN BUTTON</button>
<button>REGULAR BUTTON</button>
<script>
var app=angular.module("app",[]);
app.service("currentUser",function(){
return {
isAuthorized : function(){return false;}
}
});
app.directive("isAdmin",["currentUser", function(currentUser){
var linkFx = function(scope,elem,attrs){
debugger;
if(!currentUser.isAuthorized()){
elem[0].remove();
}
}
return {
link: linkFx,
restrict: "A"
}
}]);
angular.bootstrap(document,[app.name]);
</script>
</body>
</html>
I would suggest a different approach perhaps, and that is have an object in any parent controller, and use that object to do whatever.
So if your first controller is MainCtrl you can:
$scope.serviceDataWrapper = function() {
return MyService.getValue();
}
and use the regular ng-if anywhere, even if it involves different controllers:
<div ng-if="serviceDataWrapper()"></div>

How to write HTML inside elements with Angular

I'm beginning with Angular, and just wanted to make some tests with Ajax, retrieving a document into my page. It works perfectly, but then a new challenge appeared: I want to be able to add HTML inside a DOM element.
Normally, one would do that from a directive and the templates thingy. But I want to do it at runtime, using a controller.
This is my code:
$http.get("import.html").then(function(response){
var element = document.createElement("div");
element.innerHTML = response.data;
angular.element("#div1").innerHTML(element);
});
Maybe I'm not using correctly "angular.element"? I tried using document.getElementByID, but it doesn't work either. I receive correctly the information from the file, but I just don't find a way I can compile that HTML in runtime.
Any help with this?
edit for showing my full code:
HTML:
<!DOCTYPE html>
<html ng-app="miApp">
<head>
<meta charset="UTF-8">
<script src="angular.js"></script>
<script src="mainmodule.js"></script>
<link rel="stylesheet" href="bootstrap/css/bootstrap.css">
</head>
<body ng-controller="controlador1">
<div id="div1" ng-bind-html="myHtml" style="top:50px;left:50px">
</div>
</body>
</html>
JS:
(tested all your examples, none worked for me, this is the last I used)
app.controller('controlador1', ["$scope", "$http", "$sce", "$compile", function($scope, $http, $sce, $compile) {
$http.get("import.html").then(function(response) {
var parent = angular.element("#div1");
var element = angular.element($sce.trustAsHtml(response.data);
$compile(element)($scope);
parent.append(element);
});
}]);
Usually, you want to compile your HTML if it contains any angular functionality at all (you need to declare '$compile' in your controller dependency list):
myApp.controller('myController', ['$scope', '$sce', '$compile'],
$scope, $sce, $compile) {
$http.get("test.html")
.then(function(response){
var parent = angular.element("#div1");
parent.append($compile(response.data) ($scope));
});
}]);
if you are hell-bent on useing innerHTML, note that angular.element("#div1") is the same as $("#div1") in jQuery. So you need to write angular.element("#div1")[0].innerHTML= ...
Here's a plnkr: http://plnkr.co/edit/p3TXhBppxXLAMwRzJSWF?p=preview
In this, I have made use of $sce. It's a dependency injection of AngularJS, where it treats the HTML as safe to bind. You can read about it in AngularJS site. Please find below code and working jsfiddle for the raised concern:
HTML:
<div ng-app="app" ng-controller="test">
<div>
This is onload HTML
</div>
<div ng-bind-html="dynamicHtml"></div>
</div>
JS
var app = angular.module('app', []);
app.controller('test', function ($scope, $sce) {
$scope.dynamicHtml = $sce.trustAsHtml("<div>This is dynamic HTML!!");
});
[Update]
HTML:
<div ng-bind-html="dynamicHtml"></div>`
JS:
$http.get("import.html").then(function (response) {
$scope.dynamicHtml = $sce.trustAsHtml(response.data); //Assuming 'response.data' has the HTML string
});
You are not using correctly angular. If you use angular, you don't harly ever have to use DOM selector.
Scope is binded with ng-model and {{}} on the view.
Now your answer. To print html on the view, you can do it in that way:
In controller:
$http.get("import.html").then(function(response){
$scope.html = $sce.trustAsHtml(response.data);
});
In view:
<div ng-bind-html="html"></div>
And more easy way:
<ng-include="'url/import.html'"><ng-include>
That's it!!!

I am trying to understand AngularJS's controllerAs syntax

However after writing up a few examples to play around with the controllers would not load. I was getting an error:
firstController is not a function
After some googling I found that Angular 1.3.x no longer supports global controllers. All the examples I have seen of the new way of creating controllers seem to create them in my app.js file. I am confused, does this now mean that I must create all my controllers here rather than having a dedicated file for each controller. I have tried this to create the controller and still no luck:
UPDATE: I changed my controller to match jedanput's answer but changed $scope to this.
app.controller('firstController', [function(){
this.name = "Tim";
}]);
Also I find it very annoying that all that the majority of the example out there still reference the way it was done in Angular 1.2.
Any help would be greatly appreciated as I am having trouble understanding this issue.
EDIT: Here is my index.html file. Hopefully this will help you guys understand what is going wrong.
<!DOCTYPE html>
<html lang="en" xmlns:ng="http://angularjs.org" id="ng-app" ng-app="myApp">
<head >
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>ControllerAs</title>
<meta name="description" content="">
</head>
<body>
<div class="content" ng-view=""></div>
<!-- jQuery -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<!-- AngularJS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.6/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.6/angular-route.js"></script>
<script type="text/javascript" src="../js/app.js"></script>
<!--Directives-->
<script type="text/javascript" src="../js/directives/it-works.js"> </script>
<!--Controllers-->
<script type="text/javascript" src="../js/controllers/firstController.js"></script>
<script type="text/javascript" src="../js/controllers/secondController.js"></script>
</body>
</html>
So far I have avoided Controllers as everything I have been doing could be done with directives and services but it is time I understood more about controllers. I think it may be something fundamental I am missing. Again any help is greatly appreciated.
UPDATE: still getting the same error. This is my app.js file. Maybe it can shed some light on the problem.
var app = angular.module('myApp',[
'ngRoute'
]);
app.config(function($routeProvider) {
$routeProvider.when('/', {
templateUrl: "../partials/test-skeleton.html"
})
});
It should be
app.controller('firstController', ['$scope', function($scope){
$scope.name = "Tim";
}]);
Also, controllerAs syntax is synthetic sugar for the scope simply, you avoid using this:
<div ng-controller="oneCtrl">
{{name}}
</div>
And instead use this:
<div ng-controller="oneCtrl as one">
{{one.name}}
</div>
Which helps tremendously when you have nested controllers.
You're right, Angular allows for multiple different notations and that can be annoying and confusing. I would recommend you to stick with the guidelines from John Papas Angular Style Guide. He uses this:
(function() {
'use strict';
// Get reference to your application
angular.module('myapp')
// Add the controller
.controller('mycontroller',controller);
// This makes the injection of the controller arguments
// explicit
controller.$inject = ['$scope', '$http'];
// Here the actual controller is defined - where
// the arguments are injected on the same location as
// in the previous array
function controller($scope, $http) {
// Controller logic
});
})();
You want to keep stuff out of the global space. Really - you do. That's why he wraps everything in an Immediately-Invoked Function Expression (IIFE).
Also - you want to explicitly define what you're injecting ( the $inject array ). If not, you will not be able to minify later.
So I'm sorry - I just added another way of defining your AngularJS artefacts. From what I understand, this is one the more well known style guides out there. I've heard that he's working closely with the Angular guys to make sure his style guide will also make it easier to transition to the new Angular version.
And no - you do not need to put everything in 1 file - just make sure you have a file with angular.module('myapp',[]) loaded before any of the other files. This will declare the myapp module and will append the controller to it.
As I'm writing this - I realize that there's also another way: you create a new module in this file, append the controller and then load that module into your application. But yeah ... it's confusing.

AngularJS databinding query

This is a nooob question - as in I started learning Angular today. I was following the tutorial at Angular JS in 30 mins
The idea is to setup basic databinding in Angular. The code displays an input box, and shows(updates) whatever is typed in the box adjacent to it. Coming from a Java world and Spring MVC background, it makes perfect sense as long as the code is as follows:
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Test Angular</title>
<script type="text/javascript" src="angular.min.js"></script>
<script type="text/javascript" src="app.js"></script>
<script type="text/javascript" src="maincontroller.js"></script>
</head>
<body>
<div id="content" ng-app="SampleAngular" ng-controller="MainController">
<input type="text" ng-model="inputVal">{{ inputVal }}
</div>
</body>
</html>
app.js
var app = angular.module('SampleAngular', []);
maincontroller.js
app.controller("MainController", function($scope){
$scope.inputVal = "";
}
);
But, the same thing still works if I have a blank body for the controller i.e.:
maincontroller.js
app.controller("MainController", function($scope){
}
);
I understand that I will not be able to get the value of inputVal in the controller but
a) why does it still work in the view?
b) Clearly, I don't have a corresponding model 'inputVal' as defined in ng-model directive, and there are no errors/warnings - which IMHO - is equivalent to failing silently. Is this how Angular is designed? Is this a potential issue in large apps and will make debugging a nightmare?
Thanks in advance.
The following is from the api documentation for ngModel:
Note: ngModel will try to bind to the property given by evaluating the expression on the current scope. If the property doesn't already exist on this scope, it will be created implicitly and added to the scope.
Whenever a ng-model is used in the view, corresponding model value is created on the scope of the controller. Angular won't be throwing any error in this case, as this is its default behaviour.
$scope.inputVal = "";
is used more like initialisation of the variable being used.

How to register existing elements with Angular?

Fairly new to Angular and working inside of an existing code base.
Basically, there's an element that exists within the root document (index.html) that already exists in the html before the Angular library loads. Because of this, the ng-click directive isn't registered.
Is there an easy way that I can pass Angular a reference to the element in question and have it register that as one of its own?
Sample code (obviously missing parts, just to illustrate Angular loads after):
<html>
<body ng-app="allMyCookiesApp">
<a ng-click="giveGeuisACookie()">GIMME</a>
<script src="angular.js"></script>
</body>
</html>
I'd like to get a cookie when I click GIMME.
ng-app will bootstrap everything inside it once angular loads. This includes compiling and linking the ng-click in your example. So I think the real problem may be elsewhere.
The biggest omission from this example is any controller. I expect you are missing a controller that can place the giveGeuisACookie method on the correct scope to be used by ng-click. For example
angular.module('allMyCookiesApp', [])
.controller('geuisCtrl', function($scope) {
$scope.giveGeuisACookie = function() {
// Cookie time
};
});
would define your module for ng-app and register a controller for it. This controller will add the giveGeuisACookie function to its scope.
<html>
<body ng-app="allMyCookiesApp" ng-controller="geuisCtrl">
<a ng-click="giveGeuisACookie()">GIMME</a>
<script src="angular.js"></script>
</body>
</html>
tells angular to use the controller so that ng-click will have access to the correct method.
If this is not the problem it may be worth adding a jsfiddle with a working (or not) example of what you are doing.

Categories