I stumbled upon an issue with AngularJS and creation of elements.
In short: I need to create an element with html contents, but I do want to have the reference to that element at hand, so that I can perform some actions on it without actually rendering it in the browser.
I tried doing the following:
var template = angular.element('<div>' + templateString + '</div>');
$compile(template)($scope);
which kinda does the trick, but...
Some of these templates have logical expression in them. in example:
<div ng-if="a && b">something</div>
Unfortunately it seems that when I try to create such an element, regardless if I use $sce.trustAsHtml() with ng-bind-html or not the & characters within these conditions get escaped as &.
The html I get in the template looks like this:
<div ng-if="a && b">something</div>
Please take a look at the fiddle: https://jsfiddle.net/3uxdrp7b/1/
if this is a known issue - I'd be thankful for pointing me in the right direction, cause I've been banging my head against this for quite some time and I can't get it to work. Every example I looked at had a simple html inside the binding - it all works ok unless I use the cursed &.
The main issue is that the element is not updated right after compiling. It will contain the correct value after the current digest cycle is finished.
<!-- HTML -->
<div class="container" ng-app="app" ng-controller="DemoController">
<h3>Result:</h3> <span ng-bind-html="el"></span>
</div>
// js
var app = angular.module("app", ["ngSanitize"]);
app.controller("DemoController", ["$scope", "$compile", "$timeout",
function($scope, $compile, $timeout) {
$scope.a = true;
$scope.b = true;
let contents = "<div ng-if='a && b'>something</div>";
let compiled = $compile("<div>" + contents + "</div>")($scope);
$timeout(function(){
console.log(compiled.html());
$scope.el = compiled.html();
},0,true);
}]);
The fiddle: https://jsfiddle.net/vy9svmop/
It might be that in your case it is simpler to use the $interpolate service to recive the immediate result.
Related
I am currently trying ionic. I am not really used to this kind of web dev so I might be totally off-track. Anyway, I want to access an SQLite DB and add the results to a ion-slide-box.
I was trying something like that :
function selectResultSuccess(tx, results)
{
var div = "";
div += "<ion-slide-box >";
for (var i = 0 ; i < len ; i++)
{
div+="<ion-slide>"
div+= results.rows.item(i).Result ;
div+="</ion-slide>"
}
div += "</ion-slide-box >";
$(".result-list").html(div);
}
HTML :
<ion-content ng-controller="ExampleController" class="result-list">
</ion-content>
app.js :
angModule.controller("ExampleController", function($scope, $ionicSlideBoxDelegate) {
$scope.navSlide = function(index) {
$ionicSlideBoxDelegate.slide(index, 500);
}
$scope.nextSlide = function() {
$ionicSlideBoxDelegate.next(500);
}
$scope.update = function() {
$ionicSlideBoxDelegate.update();
}
});
So this does not work, the slidebox does not get updated and I got all my results on the same page (instead of having them on different slides), I have tried multiple things and I believe this is not the best way to handle that but I can't find anything that matches my needs.
I am also trying to avoid working with SQLite plugins.
So, you're going at it completely wrong, but that's ok. You're making the beginner mistake of still being in the JQuery mindset instead of the Angular mindset. Everyone goes through it.
So, Angular is based on templates, not DOM manipulation (with the exception of directives). What you want to do is build a template that does an ng-repeat on a set of slides, stored in some kind of scope variable.
Let's start with this:
<ion-content ng-controller="ExampleController" class="result-list">
<ion-slide-box></ion-slide-box>
</ion-content>
In our controller, lets take those results and put them in a scope variable. I'm going to do some "theoretical" code here as I don't have a context as to what you are doing above.
angModule.controller("ExampleController", function($scope, $ionicSlideBoxDelegate) {
$scope.results = results;
});
Once we have the results in the scope, let's do an ng-repeat on the results.
<ion-content ng-controller="ExampleController" class="result-list">
<ion-slide-box>
<ion-slide ng-repeat="result in results">{{result}}</ion-slide>
</ion-slide-box>
</ion-content>
Again, this isn't complete code, more pointing you in the right direction.
I would suggest starting with some basics and fundamentals.
Suggest starting with these articles:
http://mcgivery.com/structure-of-an-ionic-app/
http://mcgivery.com/creating-views-with-ionic/
http://mcgivery.com/controllers-ionicangular/
And if you want even more learning resources:
http://mcgivery.com/100-ionic-framework-resources/
I am building a directive that takes a "message" that can also contain html and nested angular directives. Right now in my directive's contrller I do:
var compiled = $compile(message)($scope);
this.message = $sce.trustAsHtml(compiled.html());
But it won't compile if the message doesn't have a valid start and end tag. I was wondering if there is another approach or if angular has any way to do this without me using a regex.
I would definitely stay away from parsing or validating HTML. jQuery/jqLite already does that for us.
angular.element requires a root element. So, wrap your message in the root element, compile and link, then take the contents out:
link: function(scope, element){
scope.param = "test";
var message = "foo {{param}} bar";
var el = angular.element("<div>").append(message);
var compiled = $compile(el)(scope);
element.append(compiled.contents());
}
I am trying to copy/display the content of one div (which contains directives) into another.
When I execute the below the directives show up fine but none of the functionality, e.g. ng-click, work.
The ng-click and other options work in the source Div just not when it is copied into the destination.
Also I am not getting any console errors.
Please advise.
var srcDiv = document.getElementById(id + '-content'); //contains directives + all working fine
var dstDiv = document.getElementById(id + 'x'); //contains directives but no functionality or errors
dstDiv.innerHTML = srcDiv.innerHTML;
You need to tell Angular to $compile the contents. Angular will not recognise bits of HTML being inserted into the DOM at arbitrary times.
Inject the $compile service to wherever you are doing your DOM manipulation.
var srcDiv = angular.element(document.getElementById(id + '-content'));
var dstDiv = angular.element(document.getElementById(id + 'x'));
dstDiv.html(srcDiv.html());
$compile(dstDiv)(scope);
try
...
dstDiv.innerHTML = srcDiv.innerHTML;
scope.$apply();
I intend to allow users to input iframes, custom inlines styles and maybe other things.
But I would like to remove anything JS.
By using $sanitize I seem to remove too much.
If you only want to strip out script tags you should do something like this:
angular.module('app', ['ngSanitize'])
.directive('testDir', function() {
return {
restrict: 'A',
controller: function($scope, $sce) {
$scope.sanitizeIt = function() {
var replacer = /<script>.*(<\/?script>?)?/gi;
var stripped = $scope.html.replace(replacer, '');
$scope.sanitizedHtml = $sce.trustAsHtml(stripped);
};
$scope.$watch('html', $scope.sanitizeIt);
}
};
});
The regular expression will wipe out all script tags and $sce tells the application that you can trust the string that is stripped as HTML.
In your application you can then write something like this:
<body ng-app="app">
<div test-dir>
<textarea ng-model="html"></textarea>
<div ng-bind-html="sanitizedHtml"></div>
</div>
</body>
When you persist to the database, make sure you save the sanitized HTML string instead of the html one.
Here's more documentation on $sce:
https://docs.angularjs.org/api/ng/service/$sce
It's basically what $sanitize uses behind the scenes. NOTE: I have not tested this regular expression in browsers aside from Chrome, so you might have to do some of your own tweaking to get it to work to suit your needs. However, the idea is the same.
I have some AngularJS stuff that holds a bunch of arrays and data. Once a user uploads a file, the file gets parsed up and saved into the scope with its different arrays. However, after all of this and the file is held in the scope, I try to update the innerHTML, but the AngularJS code does not work. I use ng-repeat to create a table based on the arrays, but it remains a single cell with content looking like {{column}} and the like.
I have had extreme difficulty using directives and templates because my index.html says that app and module, etc, are undefined when I do something such as app.directive(...
The significant parts of my index.html file include:
<html ng-app>
... //once a file is uploaded, total.js is called;
//this was so the app didn't try to run before a file was uploaded
<div id="someStuff">This will become a table once a file is uploaded</div>
This is a simple example of how my scope is set up in total.js:
function sheet($rootScope, $parse){
$rootScope.stuff = text;
//text is a variable that contains the file's contents as a string
};
document.getElementById('someStuff').innerHTML="<div ng-controller='sheet'>{{stuff}}</div>";
The HTML changes but instead of printing the file's contents, it only prints {{stuff}}.
How can I get the innerHTML to understand that it contains AngularJS, preferably without using a partial or a directive, unless you can thoroughly explain where I'd input it and the syntax of it.
Edit 1:
I have tried using $compile but it is marked as undefined. I looked at this to figure out the compile problem, but I don't understand rtcherry's syntax, and how I should apply it to my own code.
Edit 2:
I still receive $compile undefined errors when I include it like so:
function sheet($rootScope, $parse, $compile){...};
document.getElementById('someStuff').innerHTML=$compile("<div ng-controller='sheet'>
{{stuff}}</div>")(scope);
Edit 3:
While itcouldevenbeaboat's comment was extremely unhelpful, I decided I should perhaps show you the directive way I attempted to do it.
I included this code under my sheet function:
var app = angular.module('App', []);
app.directive('spreadsheeet', function($compile){
return{
templateUrl: innerHTML.html
}
});
Where innerHTML contains <div ng-controller='sheet'>{{stuff}}</div>and on index.html I've included <div spreadsheet></div>
With this, I receive no errors, but the text does not show up, neither as {{stuff}} or as the file's contents. Even when I do something simple, such as provide template: "<h2>Hello!</h2>" instead of a templateUrl, I cannot get Hello! to print.
It works for me
document.getElementById('someStuff').innerHTML = "<div ng-controller='sheet'>{{stuff}}</div>";
$compile( document.getElementById('someStuff') )($scope);
Simple solution (it will work 100%):
In controller, don't forget to inject $document in controller as a dependency, like this:
App.controller('indiaController', function ($scope, $document,$filter, $location) {
var text_element = angular.element($document[0].querySelector('#delhi');
text_element.html('your dynamic html placed here');
}