Template preparation in AngularJs - angularjs

Is it possible to render template with AngularJs not on Page, but probably in memory? I need to prepare html to be send as email.
I guess i could render something in hidden div, then in some way assign it content to variable , but for me it looks ugly :(

You can take a look at $compile function: http://docs.angularjs.org/api/ng.$compile
Example:
function MyCtrl($scope, $compile){
// You would probably fetch this email template by some service
var template = '</div>Hi {{name}}!</div></div>Here\'s your newsletter ...</div>'; // Email template
$scope.name = 'Alber';
// this produces string '</div>Hi Alber!</div></div>Here\'s your newsletter ...</div>'
var compiledTemplate = $compile(template)($scope);
};

Sure, you can use the $compile service to render a template. The rendered template will be a DOM node that isn't attached to the DOM tree. And you don't have to attach it to get its content. You could do something like this:
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.angularjs.org/1.1.2/angular.min.js"></script>
<script type="text/javascript">
var myApp = angular.module('myApp', []);
myApp.controller('MainCtrl', ['$scope', '$compile', function($scope, $compile){
var compiled;
$scope.compileTemplate = function() {
var template = '<ul><li ng-repeat="i in [1, 2, 3]">{{i}}</li></ul>';
var linker = $compile(template);
compiled = linker($scope);
}
$scope.sendEmail = function() {
alert("Send: " + compiled[0].outerHTML);
}
}]);
</script>
</head>
<body ng-controller="MainCtrl">
<button ng-click="compileTemplate()">Compile template</button>
<button ng-click="sendEmail()">Send email</button>
</body>
</html>
The reason that I've divided them into two different functions here is because when you compile and link it to the scope, the template isn't filled with data until after the next digest. That is, if you access compiled[0].outerHTML at the end of the compileTemplate() function, it won't be filled (unless you use a timeout...).

Related

Pass value from one controller to another in angular js1

I am using $rootScope for passing value from one controller to another but i did not get the value in second controller(cart).
x.controller("first",function($scope,$http,$rootScope){
$scope.cart1=function(x,y){
$rootScope.img=x;
$rootScope.login=y;
}
});
x.controller("cart",function($scope,$rootScope){
$scope.cart2=$rootScope.img;
console.log($scope.cart2)
});
$rootScope does not get the value assigned if you simply define in controller, you need to transfer the data from controller1 to controller2 based on some event
DEMO
var x =angular.module('app', [])
x.controller("first",function($scope,$rootScope){
$scope.cart1=function(x,y){
console.log(x);
$rootScope.img=x;
$rootScope.login=y;
}
});
x.controller("cart",function($scope,$rootScope){
$scope.getData=function(){
$scope.cart2=$rootScope.img;
console.log($scope.cart2)
};
});
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="angular.js#1.4.3" data-semver="1.4.3" src="https://code.angularjs.org/1.4.3/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body >
<div ng-controller="first">
<button ng-click="cart1('test','test2')">transfer</button>
</div>
<div ng-controller="cart">
<button ng-click="getData()">getStoredValue</button>
{{cart2}}
</div>
</body>
</html>
NOTE
Using $rootScope is not a recommended way to transfer variable across controllers, instead try using services.
why not using $broadcast, considering your controller structure $scope.$parent is enough, in oter case inject $rootScope from where you are firing the $broadcast
var x =angular.module('app', [])
x.controller("first",function($scope){
$scope.cart1=function(x,y){
$scope.$parent.$broadcast('value changed', {x: X, y: y});
}
});
x.controller("cart",function($scope){
$scope.$on('value changed', function(data){
$scope.login = x;
$scope.cart2 = y;
});
});
Now your values are set in scope, use it whenever needed (in case of binding it will automatically reflect in UI)
(function() {
"use strict";
angular
.module('app')
.factory("cartFactory", cartFactory);
function cartFactory($http) {
return {
login: '',
cart2:''
};
}})();
This is your factory
you can use multiple controller like this...
var x =angular.module('app', [])
x.controller("first",function($scope,cartFactory){
cartFactory.login=x;
cartFactory.cartFactory=y;
}
});
x.controller("cart",function($scope,cartFactory){
$scope.login = cartFactory.login;
$scope.cart2 = cartFactory.cart2;
});
This is very simple to pass data one controller to another multiple controller.

AngularJS - two way binding not working using service

I am learning Angular using W3Schools.
I just modified an example about "Services"... The following is the code:
<!DOCTYPE html>
<html>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
<p><input type="text" ng-model="num"></p>
<h2>{{num}}</h2>
<h1>{{hex}}</h1>
</div>
<p>A custom service whith a method that converts a given number into a hexadecimal number.</p>
<script>
var app = angular.module('myApp', []);
app.service('hexafy', function() {
this.myFunc = function (x) {
return x.toString(16);
}
});
app.controller('myCtrl', function($scope, hexafy) {
$scope.num = 200;
$scope.hex = hexafy.myFunc($scope.num);
});
</script>
</body>
</html>
When I update the textbox, the "HEX" part is not updating. Why?
Your hexafy.myFunc is called only once when the controller is initialized, hence only the initial value is converted to hex. If you want a function to be called on the value change of a scope variable in runtime, you need filters. AngularJS has a lot of inbuilt filters that are ready to use.
You can also define a custom filter, just like you define services or controllers.
<!DOCTYPE html>
<html>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
<p><input type="text" ng-model="num"></p>
<h2>{{num}}</h2>
<h1>{{num | hexafy}}</h1>
</div>
<p>A custom filter that converts a given number into a hexadecimal number.</p>
<script>
var app = angular.module('myApp', []);
app.filter('hexafy', function() {
return function (x) {
return Number(x).toString(16); // Also convert the string to number before calling toString.
}
});
app.controller('myCtrl', function($scope) {
$scope.num = 200;
//$scope.hex = hexafy.myFunc($scope.num);
});
</script>
</body>
</html>
Further reading: AngularJS Filters
NOTE: A filter is a good idea if you're gonna be using the hexafy functionality at multiple places in/across views. Otherwise, it is just an overkill and the $scope.$watch method will work fine for you, as described in other answers
$scope.hex is not updating because there're no way for it update itself.
The hexafy.myFunc is called only once when the controller is loaded for the first time.
If you want want the $scope.hex property to change with num, you might need a watch on the num property.
$scope.$watch('num', function(newVal, oldVal) {
$scope.hex = hexafy.myFunc($scope.num); /// or newVal
}
The function passed in $scope.$watch will be called everytime the value of $scope.num changes.
for more info see https://docs.angularjs.org/api/ng/type/$rootScope.Scope (the watch section)
Hope it helps.
No need of a service here, you can simple use $watch on the num. See below code snippet, it will update your ui, I have updated your controller code, please check.
app.controller('myCtrl', function($scope, hexafy) {
$scope.num = 200;
$scope.hex = "some default val";
$scope.$watch('num', function(newValue, oldValue) {
$scope.hex = newValue.toString();
});
});
Your Text box is only bind to 'num'. '$scope.hex is not binded to your text box'. So that it is not update when you typing text. You could use '$watch' on 'num'. Read here
app.controller('myCtrl', function($scope, hexafy) {
$scope.num = 200;
$scope.$watch('num', function() {
$scope.hex = hexafy.myFunc(parseInt($scope.num));
});
});

View is not updated after the $http async call when using controllerAs syntax

I am learning angularjs and I am trying to use the controllerAs syntax as I am from Java background and this would make more sense to me but I am having trouble understanding the digest loop.
I am trying to do a http call and update the variable in the controller.When I am using $scope in controller the view is updated after the data is received but when I am using the controllerAs syntax the view is not updated.
Codepen with $scope Syntax
http://codepen.io/eternal15/pen/BzANEw?editors=1111
<html>
<head>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.2/angular.min.js"></script>
</head>
<body ng-app="Test" ng-controller="testCtrl">
{{output}}
<button ng-click="onClick()">Test</button>
</body>
</html>
//JS FILE
angular.module("Test", []).controller('testCtrl', ['$scope','$http', function($scope, $http){
$scope.output = "Loading";
$scope.onClick = function(){
console.log('clicked');
$http.get('http://jsonplaceholder.typicode.com/posts').then(function(data){
$scope.output = "worked!!";
console.log($scope.output);
})
}
}]);
Codepen with controllerAs Syntax (View not updated)
http://codepen.io/eternal15/pen/yJKoaZ?editors=1011
<html>
<head>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.2/angular.min.js"></script>
</head>
<body ng-app="Test" ng-controller="testCtrl as test">
{{test.output}}
<button ng-click="test.onClick()">Test</button>
</body>
</html>
//JS File
angular.module("Test", []).controller('testCtrl', ['$http', function($http){
this.output = "Loading";
this.onClick = function(){
console.log('clicked');
$http.get('http://jsonplaceholder.typicode.com/posts').then(function(data){
this.output = "worked!!";
console.log(this.output);
})
}
}]);
I have read about the controllerAs syntax and I think it would add the object (test in the example above) under scope and thus the variables are accessible using (test) object.
So the digest loop runs after $http call because the view is updated in the first example using $scope. Since the digest loop is executed the object test in the second example should also be updated right?
Also i tried to inject $scope and do $scope.$apply() and that also didn't work and it gave me this error
Error: [$rootScope:inprog] http://errors.angularjs.org/1.5.2/$rootScope/inprog?p0=%24digest
I would like to know what I am doing wrong. Although i could go back to using the $scope format, I would like to know if I am doing something wrong or should I add other statements to watch the variables and update the values manually.
Thanks in advance
this has a different meaning inside function. Assign this to a variable and use it. Try:
angular.module("Test", []).controller('testCtrl', ['$http', function($http){
var vm = this;
vm.output = "Loading";
vm.onClick = function(){
console.log('clicked');
$http.get('http://jsonplaceholder.typicode.com/posts').then(function(data){
vm.output = "worked!!";
console.log(vm.output);
})
}
}]);
This is because of javascripts closures. When defining a new function you're creating a new scope, hence the keyword this has a new meaning for each new scope.
To solve this, define the controllers scope at the top of your controller. Common names used are either vmor $ctrl.
Your controller would then look somehting like this:
angular.module("Test", []).controller('testCtrl', ['$http', function( $http){
var $ctrl = this;
$ctrl.output = "Loading";
$ctrl.onClick = function(){
console.log('clicked');
$http.get('http://jsonplaceholder.typicode.com/posts').then(function(data){
$ctrl.output = "worked!!";
//$scope.$apply();
})
}
}]);

AngularJS: Binding JavaScript Received in XHR to the View

Working on a reporting application where reports are generated (in HTML) from a BIRT Report Engine object. The report data comes as a JSON string is recieved from XHR. The JSON string contains a combination of HTML and javascript (a function call, specifically). Once received, the report data is stuffed into a for display in the view. The view is put together using AngularJS.
I did some research and found that binding the HTML/javascript to the view in Angular requires the use of $compile. Using that as a basis, i put together some code that will include data and execute code bound from a string defined explicitly in the $scope. But - unless i'm going overlooking something after staring at the same stuff for a couple hours, the approach i'm using does not work with $scope data defined by XHR. Here's a plunkr to show the general idea implemented. Any suggestions would be greatly appreciated.
The HTML
<!DOCTYPE html>
<html data-ng-app="example">
<head lang="en">
<meta charset="UTF-8">
<title></title>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.11.0/ui-bootstrap-tpls.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular-sanitize.js"></script>
<script src="script.js"></script>
</head>
<body ng-controller="controller" >
<h1>Static Content</h1>
<p><button href="javascript: void(null)" role="link" ng-click="loadSubreport('recv_po_detail.rptdesign', 'static')">PO000007</button></p>
<h1>HTML from static string</h1>
<div compile="snippet"></div>
<h1>HTML from server string</h1>
<div compile="html.body"></div>
<hr />
<button ng-click="alert()">Show XHR Data</button>
</body>
</html>
The Javascript
var app = angular.module('example', []);
app.controller('controller', ['$scope', '$compile', '$http', function ($scope, $compile, $http){
$scope.snippet="<p><button href=\"javascript: void(null)\" ng-click=\"loadSubreport('recv_po_detail.rptdesign', 'compiled')\">PO000007</button></p>";
$http.get('data.json')
.success(function (data) {
$scope.html = data;
});
$scope.loadSubreport = function (filename, source) {
alert("Called from " + source);
};
$scope.alert = function () {{
alert($scope.html.body);
}}
}]);
app.directive('compile', ['$compile', function ($compile) {
"use strict";
return function (scope, element, attrs) {
var ensureCompileRunsOnce = scope.$watch(
function (scope) {
return scope.$eval(attrs.compile);
},
function (value) {
element.html(value);
$compile(element.contents())(scope);
ensureCompileRunsOnce();
}
);
};
}]);
Your watch goes off right at the start, when html.body still is undefined.
Then you run ensureCompileRunsOnce() and unwatch the scope. So the proper report, once loaded, never gets compiled.
I uncommented the line ensureCompileRunsOnce() and get a nice view of the report.
DEMO

data-binding doesn't work after using "appendChild()" to add new element

when i write like this, angular works:
<html ng-app="mptmanager" class="ng-scope">
<body>
<input ng-model = "test" /><br />
{{test}}
</body>
</html>
<script src="js/angular.min.js"></script>
<script src="angular/angularModules.js"></script>
but when i run JS code like this:
var temp = document.createElement('div');
temp.innerHTML = '<input ng-model = "test2"><br />{{test2}}';
document.body.appendChild(temp);
angular has no effect to the new element, so i can only see "{{test2}}" on my page...
how can i make angular work when i try to add or change some element on my page with JS?
You should never do DOM manipulation outside of directives when using Angular. Just to be complete, I'll show you how to make that work anyway. Just inject the $compile service and compile the new markup with the scope.
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, $compile) {
$scope.foo = 'Default value';
var elem = document.createElement('div');
elem.innerHTML = '<input ng-model="foo">{{foo}}';
document.body.appendChild(elem);
$compile(elem)($scope);
});
Here are proper approaches using directives:
<div my-directive></div>
<div my-directive2></div>
JavaScript:
app.directive('myDirective', function() {
return {
template: '<input ng-model="foo">{{foo}}'
};
});
app.directive('myDirective2', function() {
return {
compile: function(element) {
element.html('<input ng-model="foo">{{foo}}');
}
};
});
In your directive, if you use the link function, which has access to scope, you will have to manually $compile the markup as I demonstrated above, but be careful to avoid an infinite loop. You will usually do this: $compile(element.contents())(scope); If you $compile the element itself, you would create an infinite loop.
Live demos (click).
As dskh briefly mentioned, directives are used to do that kind of stuff in Angular.
The issue here is that Angular doesn't know about the new element, as the routine which adds it lives outside of the Angular scope. You could call $scope.$apply() manually, but i won't consider this best practice.

Resources