I have a data source that contains an array of raw HTML strings. I must display these in a page to the user.
Being a bit new with Angular, the first thing I tried was this:
<div ng-repeat="html in ctrl.html" ng-bind="html"></div>
This causes Angular to escape my HTML and display it as a string. It isn't what I need, but at least it shows that Angular is, indeed, loading the data.
Doing a Google search, I read about the ng-bind-html-unsafe directive, which I understand is supposed to inject text into an HTML document without escaping or sanitizing it in any way, which is what I want because I must trust our data source.
<div ng-repeat="html in ctrl.html" ng-bind-html-unsafe="html"></div>
This doesn't work, either. It just shows me a blank page. When I open the document inspector, I see that there is a div tag for each entry in the HTML array, but the divs are all blank.
Doing more Google-fu, I found discussions about calling methods on $scope to make Angular play nice with this. They say that ng-bind-html-unsafe is deprecated.
With all the talk about different ways to do what I need with different versions of Angular, how do I do this with today's version: 1.4?
I think you have to "sanitize" your html's..
Example:
angular.module('sanitizeExample', ['ngSanitize'])
.controller('ExampleController', ['$scope', '$sce', function($scope, $sce) {
this.html = array with your htmls;
this.sanitizeHtml = function(html) {
return $sce.trustAsHtml(html);
};
}]);
Then
<div ng-repeat="html in ctrl.html" ng-bind-html="ctrl.sanitizeHtml(html)"></div>
I think it will work
use ngSanitize module...
var app = angular.module("myApp", ['ngSanitize']);
see this link. this work perfect
In view
<div ng-repeat="html in ctrl.html" ng-bind-html="ctrl.sanitizeHtml(html)"></div>
In controller
myApp.filter('unsafe', function($sce) { return $sce.trustAsHtml; });
This will work
Related
I'm trying to add a chart to code created by others.
I understand a bit of angular, only...
I'm using angular-ui, so I don't have access to the HEAD tag where the simple Google instructions say to put the SCRIPT tags. I tried putting it later in the html, with other SCRIPT tags, but it kept saying "google" was undefined.
Finally, it seems to work if I put it inside the onload function:
<script src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
drawCharts = function() {
var is = issue;
... create the chart using data inside $scope.issue
}
// google.charts.load( -- DOESN'T WORK HERE, google is undefined
$(window).load(function () {
// finally, in here, 'google' is defined
google.charts.load('current', {'packages': ['corechart', 'bar']});
google.charts.setOnLoadCallback(drawCharts);
}
PROBLEM: drawCharts() needs to access the $scope, but here there's no access to $scope, so my angular data isn't accessible and drawCharts() fails.
So somewhere I need to connect
google.charts.setOnLoadCallback() and $scope
How?
I found one question where the person had a:
$rootScope
.$on('$viewcontentLoaded', function(...)
But he has it in main.js, and I don't have a main.js.
I tried putting it in my controller for the page, but it doesn't define $rootScope. I tried adding $rootScope to the parameters passed to the first line of my controller:
people.controller("voterIssueCtrl", function ($rootScope, $scope, $http, $cookieStore, $window, ClIENT
And that took care of the undefined $rootScope, but the $viewContentLoaded function was never called (it just contained a console.log() message...)
Perhaps my app.js is his main.js.
But it references the controller by name, so it probably loads it, so it probably can't reference something the controller defines.
Help?
===============
I pulled a google chart directive from the web
added the tag in index.html to pull it in
and added the directive to my module definition.
(Of course I forgot the 2nd one and the code didn't complain...)
Nothing. But no complaint that Google wasn't known...
Putting in sample data helps.
In the grand style of js and angular, it doesn't complain if the data isn't exactly in the form it needs...
If only Angular2 + Typescript had been invented sooner...
So with regular jquery, I can do:
var = variable.data('data-attribute');
What would be the correct way to do this with Angular?
There are various ways to do this, you should even use the jQuery command that you wrote above to read data attribute. Do this inside a controller and you are OK. I used jQuery for readability sake, you should use jQueryLite or even Vanilla Javascript.
In your controller:
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.expando = $( "#testId" ).data( "test" );
});
and in your view:
<p id="testId" data-test="this is expando attr">Hello {{name}}, {{expando}}!</p>
See the plunker for example.
This hovewer (maybe) is not the best solution! More suitable should be the use of a directive. Probably that attribute is something that you read inside a Javascript function to do something with the DOM, and the directives are the angular way to reuse component that alter the DOM.
So, your question can't have a better answer until you don't specify what's the use of that data-attribute.
I'd like to turn off angular interpolation from the top level of my site but re-enable it for individual elements.
I'm trying to add some angular functionality to a rather large legacy site, and it's not feasible to add ng-non-bindable everywhere that could possibly contain {{bindable}} brackets. This is especially important because the site may have {{unparseable:er9 >-14?%(% randomness}} within those brackets. (Angular throws a [$parse:syntax] error for that and stops parsing any of the rest of my page)
Ideally, I'd set up something like <html ng-app="MyApp" ng-non-bindable> on every page, and then have <div ng-controller="myController"> on the few places that actually use angular.
So far I haven't figured out a way to do this. I looked at changing the angular parser to ignore text nodes until it sees a controller, but that seems like overkill. I also tried adding ng-app only to the nodes I want the app to live on, but I then have to manually bootstrap each node with the app, and I think that causes me to have multiple copies of the app running simultaneously and any singletons I was hoping for would (e.g. for cacheing) would be instantiated multiple times (unless I'm wrong about this?)
Is there a way to put ng-app and ng-non-bindable on the top level <html> and then manually add the divs I care about to the app?
I've set up a plunkr with a simple example: http://plnkr.co/edit/antMrWmWnKXHcxklh9IY?p=preview
Michal Charemza's comment above led me to a working solution.
I wrote a terminal directive that compiles and attaches to the $rootScope each [ng-bindable] element in the (using jquery):
app.directive('defaultNonBindable', ['$compile', '$rootScope',
function($compile, $rootScope) {
return {
compile: function(scope, elm, attrs) {
var bindables = $('[ng-bindable]');
bindables.each( function() {
var el = angular.element(this),
compiled = $compile(el);
compiled($rootScope);
});
},
terminal: true,
}
}
]);
I then wrap each block that's angular-ified in an ng-bindable div:
<div ng-bindable>
<div ng-controller="MyController">
{{this_works}}
</div>
</div>
I've forked the original plunkr with an example of it working: http://plnkr.co/edit/2rWCy7dwJNqTShXKiLgG?p=preview
One possible solution would be to configure angular in a way, that it is not looking for curly braces, but your own special syntax for interpolating.
var myApp = angular.module('App', []);
myApp.config(function($interpolateProvider) {
$interpolateProvider.startSymbol('[[');
$interpolateProvider.endSymbol(']]');
});
myApp.controller("Ctrl", function($scope){
$scope.value="value";
});
<div>[[value]]</div>
Plunker
I did not test it and there might be some side effects to it, especially for third party code, but it could be a way for managing the transition phase
regards
New to Angular. I feel like I'm missing something obvious: Shouldn't I easily be able to run to separate AngularJs apps (modules) in the same html page? Something like this:
<section ng-app="HelloWorldApp" ng-controller="HelloWorldController">
Hello {{name}}!
</section>
<br />
<section ng-app="MyNameIsApp" ng-controller="MyNameIsController">
My Name is {{FirstName}} {{LastName}}!
</section>
Javascript:
var HelloWorldApp = angular.module('HelloWorldApp', []);
HelloWorldApp.controller('HelloWorldController', function($scope) {
$scope.name = 'World';
});
var MyNameIsApp = angular.module('MyNameIsApp', []);
MyNameIsApp.controller('MyNameIsController', function($scope) {
$scope.FirstName = 'John';
$scope.LastName = 'Smith';
});
This only runs the first module, while the second doesn't appear to do anything. I want to do this so that I can build reusable, encapsulated directives for multiple pages that don't have to name their modules the same thing.
Live Example: http://plnkr.co/edit/cE6i3ouKz8SeQeA5h3VJ
We ended up building small hierarchy of modules, however my original question can done, with just a bit of work (see below).
It is possible, but it requires a little bit coding by hand. You need to bootstrap the angular apps on your own. Don't worry, it is not that complicated
Do not add ng-app attributes in your HTML
Make sure you can fetch the DOM elements holding the app
When DOM is loaded you need to start the apps on your own: angular.bootstrap( domElement, ['AppName']);
Fork of you plunker which works: http://plnkr.co/edit/c5zWOMoah2jHYhR5Cktp
According to the Angular docs for ngApp:
Use this directive to auto-bootstrap an application. Only one
directive can be used per HTML document. The directive designates the
root of the application and is typically placed at the root of the
page.
Seems it's by design.
You can specify any nested apps in the module def of the main one.
angular.module("myapp", ['statusapp', 'tickerapp']).controller(....
and in a separate file, you have the other apps defined. We're using a template engine which hides some of this, but you'll end up with HTML that contains nested ng-apps and javascript for each one that defines the module/controller. The code above is the trick to getting more than one bootstrapped.
A similar question was asked here but it did not help me.
I am learning angularjs and I noticed the controller is executed twice.
I have a very simple fiddle example that shows the behavior here
I built the example as I was learning about services and at first I thought it was the injecting of the services into the controller but I commented all the code related to the services and still the controller is executed twice.
My example works but I am afraid I am doing something wrong.
<div ng-app="MyApp">
<div ng-controller="MyCtrl">
{{data1}}
</div>
</div>
var app = angular.module('MyApp', [])
app.service('Service1', function(){
return {
ajxResponse1: 'dataFromService1'
};
});
function MyCtrl($scope, Service1){
alert('Entering MyCtrl');
$scope.data1 = Service1.ajxResponse1;
alert('Exiting MyCtrl');
}
One possible source is this: if you are using Routing and specify the controller in routes - you must not specify it in template that the route uses. We had that problem when this was overlooked.
Your controller was running twice in the fiddle because angular is referenced twice (once via the Frameworks & Extensions drop down and another as an External Resource).
See this updated fiddle where I removed the External Resource and the alerts only show up once.
The code remains unchanged:
function MyCtrl($scope, Service1, Service2, Service3){
alert('Entering MyCtrl');
$scope.data1 = Service1.ajxResponse1;
$scope.data2 = Service2.ajxResponse2;
$scope.data3 = Service3.ajxResponse3;
alert('Exiting MyCtrl');
}
I had a similar problem and it was due to slashes in my routing.
I had something like /post{slug:[a-z0-9-]*/} for my route and when visiting the page at domain.com/post it would redirect to the version with a slash on the end.
Took me ages to work it out!
Edit:
Actually, just took a more detailed look at your code and noticed there is no routing in there, so this is probably not the cause in your case.
Might be useful for people like me who were looking for a different solution though.
For all the people using rails and angularjs:
The rails framework that maps URLS to views and loads them clashes with the angularjs $route even when you have a single-view application.
To prevent the double-loading of your controller:
go to application.js and remove "//= require turbolinks
You're welcome.