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;
}
Related
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));
});
});
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 have a problem that occurs sometimes, when the template entry is not defined in the template cache, it happens every once in a while after reloading the page.
The related code:
index.html
<div ng-include src="'Views/templates.html'"></div>
Views/templates.html
<script type="text/ng-template" id="customTemplate/spare-request-item.html">
<div>..</div>
</script>
directive.js
.directive("spare", function ($templateCache) {
console.log($templateCache.info());, //return length 30 when it fails, 31 otherwise
console.log($templateCache.get('customTemplate/spare-request-item.html'));//return undefined when it fails
return {
..
templateUrl: 'customTemplate/spare-request-item.html',
Anyone else has experienced this or know how to assure that the template is loaded and parsed before the directive runs? It is a angular bug?
Angular compiles the directives as it goes over DOM. You can't have it "wait" on a directive. What you could do, is not to show the directive until templates have loaded.
I don't think this is a good approach since it requires "knowledge" on the part of the user of the directive:
<div ng-include="'Views/templates.html'"
onload="$root.templatesLoaded = true"></div>
<my-directive ng-if="templatesLoaded"></my-directive>
Another approach would be to manually load all the templates and the directive's specific template. Here's a conceptual way of how to do this:
app.directive("foo", function($templateRequest, $templateCache, $compile) {
return {
link: function(scope, element) {
$templateRequest("templates.html")
.then(function(templateContainer) {
$compile(templateContainer);
return undefined; // just to show that no need to return anything
})
.then(function() {
var template = $templateCache.get("foo.html");
if (template) {
element.html(template);
$compile(element.contents())(scope);
}
});
}
}
});
plunker
$templateRequest is used here since it caches the templates.html template (to prevent double-requests), but it is a "lazy" use, since you would not actually need the template with id === "templates.html".
You can abstract it into a service, of course, to make it nicer.
Typically, however, I have seen directive developers embed the template in the .js file of the directive. This frees the user of the directive from loading separate .html files to make the directives work. I suppose you could load themes this way.
My problem is I have one ng-app. Does that mean I have to do dependency injection for plugins I may not be using on that given view? Example I bring in ngTagsInput does that mean I have to do it even when the view doesn't call for it? That would mean I have to include that js for every view even if it doesn't use ngTagsInput.
I have a very large MVC .NET application and I am trying to figure out what is he best way to handle bringing in external plugins.
I have some code like so in our Main _Layout template:
<html ng-app="ourApp">
<head>
<!-- all of our includes are here -->
</head>
<body>
<directive></directive>
<anotherdirective></anotherdirective>
#RenderBody()
</body>
</html>
RenderBody is where MVC slides in our views from our mvc routing.That view may look like so:
<script src="~/Scripts/HomeAngular.js"></script>
<div ng-controller="HomeCtrl">
<directive3></directive3>
</div>
App JS:
var app = angular.module('ourApp', ['ngTagsInput']);
IS there a way I can get around having to inject ngTagsInput on every view page even if i don't need it?
There are several different ways to handle Dependency Injection (DI) in angular. In this first example, you simply define ngTagsInput before declaring the controller. If ngTagsInput is a service, for example, you'll need to return an object with methods that allow you to access the data inside of that service.
For example:
app.service('ngTagsInput', function($scope) {
var data = {};
return {
getData = function() {
return data;
},
setData = function(val) {
data = val;
}
};
});
app.controller('HomeCtrl', function($scope, ngTagsInput) {
// ...
$scope.tags = ngTagsInput; // whatever you want to do with it
});
However, there's a problem...
In the example above, if you run that code through a minifier, you'll get $scope turned into some variable (a, b, x, etc...) and angular wont know what to do with that.
In this method, we use an array. The last item in your array is your lambda function for the controller, which takes 1 argument for each previous item in the array:
app.controller('HomeCtrl', ['$scope', 'ngTagsInput', function(scp, ngTagIn) {
// ...
}]);
This code can be run through a minifier just fine, since the dependencies are strings in the array and can be renamed to anything you want inside the function's parameters.
DI also works in directives, factories, filters, and services with the same syntax; not just controllers and modules.
app.directive('HomeCtrl', ['$scope', 'ngTagsInput', function(scp, ngTagIn) {
// ...
}]);
Couldn't you break down your application further into smaller modules instead of just one. Then you can inject the smaller modules into the app module and only inject the ngTagsInput dependency on the modules that actually need it. That's how I typically break up my application by function or area.
New in angularJS, I would like to know what are the pros and cons between the codes below?
Which is recommended to use?
$routeProvider.when('foo', {
templateUrl: 'foo.html',
controller: fooCtrl
function fooCtrl() {
//something here
}
});
or
$routeProvider.when('foo', {
templateUrl: 'foo.html'
});
app.controller("fooCtrl", function() {
//something here
});
//in html
<div ng-controller="fooCtrl"></div>
I prefer the second approach, and use it when developing our application.
It is the elegant way of coding , seperating your routes-configuration, module-wiring etc from the Controllers. we can write the routes-config in a main file say app.coffee [I use coffeescript] defining like
routesConfig = ($route) ->
$route.when('/employees',
{templateUrl: 'employee.employeeView.html'})
Define the routesconfig and wiring modules [eg: employee.employeeController] here.
modules = ['employee.employeeController', 'user.userController']
you can create, start your angular application from here,
m = angular.module('app', modules)
m.config['$route', routesConfig]
Now you can specify the Controllers seperately, say in employeeController.coffee
name = 'employee.employeeController'
mod = angular.module(name, [])
mod.controller(name, [
'$scope'
'$log'
($scope, $log) ->
$scope.name = 'java'
In your View, say employeeView.html
<div ng-controller="employee.employeeController">
<div class ="info">
Name is {{name}}
</div>
Basically we seperated Controllers, View , application configuration from each other.
To add something specific to your question,
If you are using the first approach, then probably you are using your controller as a Route Controller, and in
second approach , it is a View Controller.
In both cases, controller will be instantiated for the mentioned route.
For instance, I have a main page index.html and i am adding many views (ng-view) in the basic html template.
If you have two different sections of view in this template say 'section1' and 'section2' and each of them are included
with ng-view, then you probably need two different controllers, and is good to define them using the second approach.
Use this type of controller to initialize the scope with data,functions,watches etc and refer the controller in your view using ng-controller.
If you have a section, say 'section1' [representing the main html page] that is included via ng-view which encapsulates both section1 and section2,
then that view needs route controller.
Do not use both the approaches for a single view/route as it would result in creating two instances of same controller
for the same route.
I like to add two links here which eloborates this (your query) and address this query (problem in defining controllers in two places)
https://groups.google.com/forum/?fromgroups=#!topic/angular/52zE0ibOAgk
AngularJS can a $on method be called more than once for a single $broadcast?