I need to add a small amount of HTML inside part of my Javascript array for the synopsis. Is there a method to do this as currently it outputs as text?
So far I have the below
app.controller('primaryServiceListController', ['$scope', '$http', function($scope, $http){
$scope.serviceListings = [
{
url: "details.html",
img: "service-01.jpg",
sector: "Business",
synopsis: "Completely synergize resource taxing relationships <strong>via premier niche markets</strong>. Professionally cultivate one-to-one customer service robust."
}
]
}]);
You can add it as a text and then use ng-bind-html in html to render it.
<element ng-bind-html="expression"></element>
You need to include ngSanitize in your app and then, in your template, you can bind the HTML easily using ng-bind-html directive:
<div ng-bind-html="serviceListings[0].synopsis"></div>
Please see the following working demo:
angular.module('app', ['ngSanitize'])
.controller('ctrl', ['$scope', function($scope) {
$scope.serviceListings = [{
url: "details.html",
img: "service-01.jpg",
sector: "Business",
synopsis: "Completely synergize resource taxing relationships <strong>via premier niche markets</strong>. Professionally cultivate one-to-one customer service robust."
}]
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.23/angular-sanitize.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<div ng-bind-html="serviceListings[0].synopsis"></div>
</div>
You can render the string as html like this:
<div ng-bind-html="serviceListings[0].synopsis"></div>
You can also create a filter and sanitize the data using $sce if you want:
.filter('trustHtml', [
'$sce',
function($sce) {
return function(value) {
return $sce.trustAs('html', value);
}
}
]);
And to use it in the html file
<div ng-bind-html="serviceListings[0].synopsis | trustHtml"></div>
Related
I'd like to be able to use dynamic strings that may contain directives. The problem is I can't seem to get $compile nor $interpolate to work. I have the following in a plunker
index.html
<body ng-controller="MainCtrl">
<ul>
<li ng-repeat="user in users">{{user.badge}}</li>
</div>
</body>
app.js
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, $element, $compile) {
$scope.users = [{
badge: $compile('<p><name-badge name="Carl Sagan"></name-badge></p>')($scope)
},{
badge: $compile('<p><name-badge name="Richard Dawkins"></name-badge></p>')($scope)
},{
badge: $compile('<p><name-badge name="Cecilia Payne"></name-badge></p>')($scope)
}];
});
app.directive('nameBadge', function ($compile) {
return {
template: '<strong>Hi, I\'m {{name}}',
link: function ($scope, $element, $attrs) {
$scope.name = $attrs.name;
}
}
});
The circular structure to json error isn't being throwing by $compile service. It occurs because you're trying to bind an invalid value to the view, in this case, a compiled element.
The "correct" way to add the returning value of $compile to the DOM is by using the $element service:
app.controller('MainCtrl', function($scope, $element, $compile) {
$element.append($compile('<p><name-badge name="Micah"></name-badge></p>')($scope));
});
Working Plunker
I said "correct" because you shouldn't be doing that kind of DOM manipulation in a controller. You should try to use a directive instead.
I have a form that I want to build at run time via js and use it in a form controller in angularjs.
As you can see in the following example, it is not being thrown as html, and i want it to be binded to the model variable. http://jsfiddle.net/g6m09eb7/
<div>
<form ng-controller="TodoCtrl" ng-submit="blabla()">
<div ng-repeat="field in fields">{{field.input}}</div>
</form>
</div>
function TodoCtrl($scope) {
$scope.model = {
'FirstName': 'Test',
'LastName': 'Test Last'
}
$scope.fields = [{
input: '<input type="text" ng-model="model.FirstName">'
}, {
input: '<input type="text" ng-model="model.LastName">'
}, ];
}
First, I'm going to show you how to make this work as you're trying to accomplish it, for the sake of being informative. This is not the approach you should use to solve your overall problem. This example will get the html in the document, but it won't be compiled with Angular. To do that, you would have to have a different directive, like this (click). This is all kinds of a bad approach.
angular.module('myApp', [])
.controller('TodoCtrl', function($scope) {
$scope.fields = [{
input: '<input type="text" ng-model="model.FirstName">'
}, {
input: '<input type="text" ng-model="model.LastName">'
}, ];
})
// filter to make Angular trust the html
.filter('safeHtml', ['$sce', function ($sce) {
return function (text) {
return $sce.trustAsHtml(text);
};
}])
;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<form ng-app="myApp" ng-controller="TodoCtrl">
<!-- use ng-bind-html on trusted html to bind it (see the js) -->
<div ng-repeat="field in fields" ng-bind-html="field.input | safeHtml"></div>
</form>
Instead, you can do this naturally. Just use the properties of your object as the criteria for ng-repeat. Simple and clean!
angular.module('myApp', [])
.controller('TodoCtrl', function($scope) {
$scope.model = {
'FirstName': 'Test',
'LastName': 'Test Last'
};
})
;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<form ng-app="myApp" ng-controller="TodoCtrl">
<div ng-repeat="(key,value) in model">
<input type="text" ng-model="model[key]"/>
</div>
</form>
Be sure to avoid concerning your controller with DOM manipulation. If you have html snippets in a controller, your approach is probably off track. DOM manipulation should be done entirely with directives.
Generally, what I want to do, is to initialize nested ng-controller inside ng-repeat using variable.
JSFiddle
JS
angular.module('app',[])
.controller('main',function($scope){
angular.extend($scope,{
name:'Parent Controller',
items:[
{name:'nested2'},
{name:'nested1'}
]
});
})
.controller('nested1',function($scope){
$scope.name = "Name1";
})
.controller('nested2',function($scope){
$scope.name = "Name2";
});
I want this:
<div ng-controller="main" ng-app='app'>
Nested: {{name}}
<div ng-controller="nested1">{{name}}</div>
<div ng-controller="nested2">{{name}}</div>
</div>
to become to something like this:
<div ng-controller="main">
Nested: {{name}}
<div ng-repeat="item in items">
<div ng-controller="item.name">{{name}}</div>
</div>
</div>
Problem: it does not work this way. Neither it works any other way, that I've tried after google'ing for an hour or so.
Is there any "legal" and nice way to achieve that at all?
There isn't a real way,using angular features it at this point, i suppose. You could create a directive and use un-documented dynamic controller feature controller:'#', name:'controllerName'. But this approach will not evaluate bindings or expressions that provide the controller name. What i can think of is a hack by instantiating a controller provided and setting it to the element.
Example:-
.directive('dynController', function($controller){
return {
scope:true, //create a child scope
link:function(scope, elm, attrs){
var ctrl =scope.$eval(attrs.dynController); //Get the controller
///Instantiate and set the scope
$controller(ctrl, {$scope:scope})
//Or you could so this well
//elm.data('$ngControllerController', $controller(ctrl, {$scope:scope}) );
}
}
});
And in your view:-
<div ng-controller="main">
<div ng-repeat="item in items">
Nested:
<div dyn-controller="item.name" ng-click="test()">{{name}}</div>
</div>
</div>
Demo
Note that i have changed the position of ng-controller from the element that does ng-repeat, since ng-repeat (1000) has higher priority than ng-controller (500), ng-repeat's scope will prevail and you end up not repeating anything.
While looking at it
Invested more couple hours into it, inspecting angular's sources etc.
The expression, given to ng-controller, is not being evaluated at all.
Here is best approach I've found:
HTML:
<div ng-controller="main">
Nested: {{name}}
<div ng-repeat="item in items">
<div ng-controller="nested" ng-init="init(item)">{{name}}</div>
</div>
</div>
JS:
angular.module('myApp', [])
.controller('main', function ($scope, $controller) {
angular.extend($scope, {
name: 'Parent Controller',
items: [
{name: 'nested2'},
{name: 'nested1'}
]
});
})
.controller('nested', function ($scope, $controller) {
angular.extend($scope, {
init: function (item) {
$controller(item.name, {'$scope': $scope});
}
});
})
.controller('nested1', function ($scope) {
$scope.name = 'test1';
})
.controller('nested2', function ($scope) {
$scope.name = 'test2';
});
I understand how Angular dependency injection works with directives but wanted clarification on something. I have a dummy test directive as follows:
app.directive("test", [
function() {
return {
restrict: "E",
scope: {},
controller: ["$scope", "$filter",
function($scope, $filter) {
var food = ["Apple pie", "Apple cobler", "Banana Split", "Cherry Pie", "Applesauce"];
$scope.favorites = $filter('filter')(food, "Apple");
}
],
template: "<div>{{favorites}}</div>"
}
}
]);
This works fine and will filter the food array as expected. However I noticed if I inject the $filter service in the directive as follows, it still works:
app.directive("test", ["$filter",
function($filter) {
return {
restrict: "E",
scope: {},
controller: ["$scope",function($scope) {...
My question: Is it better practice to inject services into a directive in the declaration line like so:
app.directive("test", ["$filter", function($filter) {
or in the controller line like this:
controller: ["$scope", "$filter", function($scope, $filter) {?
Is there no difference? Here is a Plunker of the directive code.
In this case, you're receiving the same service, so it likely doesn't matter too much. I personally prefer to keep them as localized as possible; if you don't need $filter in the link function or something like that, I'd just inject it into the controller.
In certain cases, this may also make it easier to mock dependencies during testing.
You can do this also. Much better way by splitting directive and its controller in a single file. Or you can write in separate files. But, better understand
app.directive('throbberDirective',
[
function(){
return {
restrict: "EA",
templateUrl: "common/utils/throbbers/throbber.html",
controller: throbberController
}
}
]);
app.controller('throbberController', throbberController);
throbberController.$inject = ['$scope', '_$ajax'];
function throbberController($scope){
$scope.throbber = _$ajax.getThrobberConfigs();
$scope.throbber.templateName = $scope.throbber.templateName;
}
This is my template:
<div class="span12">
<ng:view></ng:view>
</div>
and this is my view template:
<h1>{{stuff.title}}</h1>
{{stuff.content}}
I am getting the content as html and I want to display that in a view, but all I am getting is raw html code. How can I render that HTML?
Use-
<span ng-bind-html="myContent"></span>
You need to tell angular to not escape it.
To do this, I use a custom filter.
In my app:
myApp.filter('rawHtml', ['$sce', function($sce){
return function(val) {
return $sce.trustAsHtml(val);
};
}]);
Then, in the view:
<h1>{{ stuff.title}}</h1>
<div ng-bind-html="stuff.content | rawHtml"></div>
In angular 4+ we can use innerHTML property instead of ng-bind-html.
In my case, it's working and I am using angular 5.
<div class="chart-body" [innerHTML]="htmlContent"></div>
In.ts file
let htmlContent = 'This is the `<b>Bold</b>` text.';
You shoud follow the Angular docs and use $sce - $sce is a service that provides Strict Contextual Escaping services to AngularJS. Here is a docs: http://docs-angularjs-org-dev.appspot.com/api/ng.directive:ngBindHtmlUnsafe
Let's take an example with asynchroniously loading Eventbrite login button
In your controller:
someAppControllers.controller('SomeCtrl', ['$scope', '$sce', 'eventbriteLogin',
function($scope, $sce, eventbriteLogin) {
eventbriteLogin.fetchButton(function(data){
$scope.buttonLogin = $sce.trustAsHtml(data);
});
}]);
In your view just add:
<span ng-bind-html="buttonLogin"></span>
In your services:
someAppServices.factory('eventbriteLogin', function($resource){
return {
fetchButton: function(callback){
Eventbrite.prototype.widget.login({'app_key': 'YOUR_API_KEY'}, function(widget_html){
callback(widget_html);
})
}
}
});
So maybe you want to have this in your index.html to load the library, script, and initialize the app with a view:
<html>
<body ng-app="yourApp">
<div class="span12">
<div ng-view=""></div>
</div>
<script src="http://code.angularjs.org/1.2.0-rc.2/angular.js"></script>
<script src="script.js"></script>
</body>
</html>
Then yourView.html could just be:
<div>
<h1>{{ stuff.h1 }}</h1>
<p>{{ stuff.content }}</p>
</div>
scripts.js could have your controller with data $scope'd to it.
angular.module('yourApp')
.controller('YourCtrl', function ($scope) {
$scope.stuff = {
'h1':'Title',
'content':"A paragraph..."
};
});
Lastly, you'll have to config routes and assign the controller to view for it's $scope (i.e. your data object)
angular.module('yourApp', [])
.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/yourView.html',
controller: 'YourCtrl'
});
});
I haven't tested this, sorry if there's a bug but I think this is the Angularish way to get data