I came across this post, which entails more or less exactly what I want to do: AngularJS - Is it possible to use ng-repeat to render HTML values?
As of right now, the code looks like this, rendering correctly as text:
<div ng-repeat="item in items.Items">
{{item}}<br>
</div>
I want this to render as HTML now, so taking from that post, I have it looking something like this:
<div ng-repeat="item in items.Items" ng-bind-html-unsafe="item">
<br>
</div>
However, nothing renders as a result. Does anyone know why that is? I have also tried setting ng-bind-html-unsafe to "{{item}}", which had no effect
You should use ng-bind-html,
and you could do that:
$scope.myHTML = $sce.trustAsHtml('<b>some</b> html');
See the updated fiddle.
In your case use some function or pass the array in the controller first:
$scope.getHtml = function(v){
return $sce.trustAsHtml(v);
};
It is always a good practice to mark the html that one is binding from the .js file to the .html which are in string format, as safe.
One way, it can be achieved is by making use of AngularJS's dependency injection with $sce. One can read more about it, in length here.
I have created a demo using a list, since you have shared only the HTML but not the JS, so I have assumed one and illustrated how to make use of it by trusting the HTML string using $sce.trustAsHtml().
Refer demo.
Please have a look at the code below:
HTML:
<div ng-app="app" ng-controller="test">
<div ng-repeat="item in items" ng-bind-html="item">
</div>
</div>
JS:
var app = angular.module('app', []);
app.controller('test', function($scope, $sce) {
$scope.data = [{
"Items": [{
"data": "<p>Item 1</p>"
}, {
"data": "<p>Item 2</p>"
}, {
"data": "<p>Item 3</p>"
}, {
"data": "<p>Item 4</p>"
}]
}];
$scope.items = [];
angular.forEach($scope.data[0].Items, function(v, k) {
$scope.items.push($sce.trustAsHtml(v.data));
});
});
Related
I am trying to call (or use) few custom directives in ionic framework, dynamic is like <mydir-{{type}} where {{type}} will come from services and scope variable, having values radio, checkbox, select etc, and created my directives as mydirRadio, MydirCheckbox, mydirSelect, But its not working.
Is their any good approach to get the dynamic html as per {{type}} in scope?
Long story short; no you can't load directives dynamically in that way.
There are a few options for what you can do. You can, as other answers have mentioned, pass your context as an attribute (mydir type="checkbox"). You could make a directive that dynamically loads another directive, as also mentioned by others. Neither of these options are imo every good.
The first option only works if you write the directive yourself, not when using something like ionic. It also requires you to write multiple directives as one, which can get very messy very quickly. This mega directive will become hard to test and easy to mess up when maintaining it in the future. Note that this is the correct way to pass data to a directive from the view, it's just not good for this specific use case.
The second option is problematic because obfuscates things a bit too much. If someone reads your html and sees a directive called dynamic that is given dynamic data... they have no idea what is going to happen. If they see a directive called dropdown that is given a list they have a fair idea of what the result will be. Readability is important, don't skimp on it.
So I would suggest something simpler that requires much less work from you. Just use a switch:
<div ng-switch="type">
<mydir-select ng-switch-when="select"></mydir-select>
<mydir-checkbox ng-switch-when="checkbox"></mydir-checkbox>
</div>
I dont understand why do you need dynamic directives.
Simple use single directive and change the template accordingly.
For example -
angular.module('testApp')
.directive('dynamicDirective', function($compile,$templateCache,$http) {
return {
restrict: 'C',
link: function($scope,el) {
//get template
if(radio){
$http.get('radio.html', {cache: $templateCache}).success(function(html){
//do the things
el.replaceWith($compile(html)($scope));
});
} else if(checkbox){
//load checkbox template
} //vice-versa
}
};
});
You can inject service variable in directive also.
a bit more code would help. I don't know, if its possible to do dynamic directives like the ones in a tag
<{dyntag}></{dyntag}>
but you also can use an expression like
<your-tag dynamic_element="{type}">...</your-tag>
which should have exactly the same functionality. In your case it would be like:
Your JSObject ($scope.dynamics):
{"radio", "checkbox", "select"}
and your HTML:
<div ng-repeat="dyn in dynamics">
<your-tag dynamic_element="{dyn}"></your-tag>
</div>
Yes, that's not a problem. You can interpolate your data using {{}} and in your directive compile a new element using that data:
myApp.directive('dynamic', function($compile, $timeout) {
return {
restrict: "E",
scope: {
data: "#var" // say data is `my-directive`
},
template: '<div></div>',
link: function (scope, element, attr) {
var dynamicDirective = '<' + scope.data + ' var="this works!"><' + scope.data + '>';
var el = $compile(dynamicDirective)(scope);
element.parent().append( el );
}
}
});
HTML:
<div ng-controller="MyCtrl">
<dynamic var="{{test}}"></dynamic>
</div>
Fiddle
Below is a piece of code that connects with a Firebase database.
I have got a value for numberOfUsers and now I would like to use this variable in the html like so {{numberOfUsers}}.
I'm not really sure the best way to do this or if I need to use controllers also? Sorry if it's a stupid question, I'm still learning Javascript and Angular.
angular.module('myApp', ['ngRoute', 'firebase'])
var userList = new Firebase("https://my-app.firebaseio.com/presence/");
userList.on("value", function(snap) {
numberOfUsers = snap.numChildren();
console.log("Users = " + numberOfUsers);
});
;
http://jsfiddle.net/HB7LU/11820/
Any help will be much appreciated.
Thanks
The formal way to make a value available would be to put it in $rootScope, but it might be better to expose it as part of a service.
Try using constant
http://jsfiddle.net/HB7LU/11818/
var myApp = angular.module('myApp',[]);
myApp.constant('numUsers', 4);
function MyCtrl($scope,numUsers) {
$scope.name = 'Superhero';
$scope.numUsers = numUsers;
$scope.addUser = function(){
numUsers++;
$scope.numUsers = numUsers;
}
}
You can use a constant to achieve the same as Lucas suggested it. However, instead of creating a constant service for every value you can group then together like this :
angular.module("myModule")
.constant("CONST", { "KEY1" : "VALUE1",
"KEY2" : "VALUE2"});
This way you can gather a bunch of constants together and use it like:
CONST.KEY1
CONST.KEY2
EDIT: Your problem seems to be very different.
First of all you should use the AngularJS flavour of Firebase. Its called AngularFire. You can find out more about it here. I will answer the question of rendering the UI based on Model changes. AngularJS promotes the MVC pattern. You need objects of Service, Controller and View (HTML page) to achieve the functionality you want.
In the example I am providing below, everything is clubbed into one file (index.html) but ideally, the code should be separated.
<div ng-app="myapp">
<div ng-controller="PostCtrl" >
<ul>
<li ng-repeat="post in posts"> <!-- $scope.posts of PostCtrl" -->
<div>
<span>{{post}}</span> <!-- {{}} is used to render data -->
</div>
</li>
</ul>
</div>
<script>
//factory is a type of service. Services are used to write business logic or fetch data from the backend. Your Firebase related calls should come here.
angular.module("myapp", []).factory("myService", function($http) {
return {
fetchData: function() {
return [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; //can also be a request to the backend returning data.
}
};
});
//a controller connects a service to the HTML file. $scope service provided by AngularJS is used to achieve this.
angular.module("myapp").controller("PostCtrl", ["$scope", "myService", function($scope, myService) {
//posts variable is used in HTML code to retrieve this data.
$scope.posts = myService.fetchData();
}]);
</script>
</div>
To learn the basics of AngularJS you can go through codeschool tutorials. They are interactive and start from the basics.
I've got the following getUserData.js:
var App = angular.module('App', []);
App.controller('UserdataController', function($scope, $http) {
$http.get('data/getUserData.php')
.then(function(res){
$scope.users = res.data;
});
});
Which works perfectly, a Json is created inside the getUserData.php file, nothing wrong with that.
But when I want to display it inside the index.php file, I only get it to work using ng-repeat, like this:
<div ng-controller="UserdataController">
<ul>
<li ng-repeat="user in users">
{{user.voornaam}}
</li>
</ul>
</div>
But how can I display this data ("voornaam" is dutch for first name) without using ng-repeat? I thought it should be something like this, but it doesn't work..
<div ng-controller="UserdataController">
{{users.voornaam}}
</div>
Tried for several hours now, and searched all over the place. If someone can tell me what I'm doing wrong, that would be great!!
Well that's because users.vornaam just does not exists. However instead of showing an error Angular will just show a "blank value".
Check your object strucutre to understand your error. It will be something like
Users {
0:{ vornaam: xyz, ...}, //this is a single "user" which has a vornaam attribute
1:{ vornaam: xyz, ...},
2:{ vornaam: xyz, ...}
}
As you can see your Users object/array itself just does not have a "vornaam" but consists of several objects which then have the requested attribute.
In the above example
Users.vornaam
will be undefined. But if you try to access it like
Users[0].vornaam
it will work.
In my web app, in a search result, each item can have a different look according to the type of item. To achieve this, I was thinking of including the layout with data placeholders for that particular item in the json response. How do I bind the scope data to the placeholders in the json response template?
for eg.
My search result would look something like :
<li ng-repeat="item in items">
<div ng-bind-html="item.template"></div>
</li>
And the JSON response would look like :
[{
"template_name":"One",
"template":"<div ng-repeat='subitem in item.subitems'>{{subitem.name}}</div>",
"subitems":[{name:"John"},{name:"Jane"}]
},
{
"template_name":"Two",
"template":"<div ng-repeat='subitem in item.subitems'>{{subitem.name}}</div>",
"subitems":[{name:"John"},{name:"Jane"}]
}
]
Like you can see, I need to bind the placeholders in the json response with the scope data. Is there any way to do it? If not, how do you suggest i should address this situation?
Thank You!
While this will obviously not work:
<li ng-repeat="item in items">
<div ng-bind-html="item.template"></div>
</li>
Since you are telling angular to display html that is inside item.template but what you really want is to tell it to treat item.template as it would be markup that should be parsed in a similar way to a template inside directive.
What you can do however is to define custom directive that will treat item.template in a desired manner. For instance:
<li ng-repeat="item in items">
<div dynamic-template="item.template" />
</li>
Where dynamic-template is defined as follows:
module.directive('dynamicTemplate', function($compile){
return {
scope: {
dynamicTemplate:'='
},
replace:true,
transclude: true,
link: function($scope, $element, $attrs, _, $transcludeFn){
// since we have isolate scope - transclude the element so it has access
// to item and anything else available inside ng-repeat
$transcludeFn(function($childElement, $childScope){
var link = $compile($scope.dynamicTemplate);
var bound = link($childScope);
$element.append(bound);
});
}
};
});
Of course this is not ideal since changes to item.template wont be reflected but you can easily extend this by adding $watch.
You can find working example here.
This fiddle tries to render two partials in string form. Of course, this solution won't work (the partials are rendered as-is and not interpreted as angular templates).
From what I understand, I somehow have to use the $compile service, but I don't quite get it. For performance reasons, I would like to have each partial only compiled once and then only updated locally. Can $compile do that, and if so, how? In my case there are not many partials, not many changes to the set of partials and each partial can be very large (entire sub-page).
And no, I do not want them to be in separate files. Also, the views are lazy-loaded.
Is it maybe even possible to achieve this by inserting a new module for each such partial?
UPDATE:
Thanks to #threed, I was able to fix it all up. This is an improved version with lazy-loaded content and controller.
You can use the ng-include directive (see this jsfiddle)...
HTML:
<div ng-app="app" ng-controller="MyCtrl">
<div ng-repeat="partial in partials">
<div ng-include="partial.name"></div>
</div>
</div>
JavaScript:
var app = angular.module('app', []);
var data = [
'data1-apple',
'data2-banana'
];
var partials = [
{
name: 'template1',
content: '<div>{{data[0]}}</div>'
},
{
name: 'template2',
content: '<div>{{data[1]}}</div>'
}
];
function MyCtrl($scope, $templateCache) {
for(var i in partials){
$templateCache.put(partials[i].name, partials[i].content);
}
$scope.partials = partials;
$scope.data = data;
}