Print Angular processed HTML - angularjs

I'm trying to print a div that has been rendered using Angular. I'm using this answer as a starting point https://stackoverflow.com/a/30765875/285190, with the print function being pretty simple
$scope.printIt = function(){
var table = document.getElementById('printArea').innerHTML;
var myWindow = $window.open('', '', 'width=800, height=600');
myWindow.document.write(table);
myWindow.print();
};
The problem is the innerHTML contains all the items that would exist if the ng-show ng-hide directives hadn't been executed.
Is there a way to get the actual HTML the client is seeing, i.e. after Angular has performed its magic?

Please use $compile service to transform angularized HTML to actual HTML.
Demonstration of $compile
https://stackoverflow.com/a/26660649/5317329
$scope.printIt = function () {
angular.element('#tempHtml').html("");
var table = document.getElementById('printArea').html();
var compiledHTML = $compile(table)($scope);
$timeout(function () {
angular.element(document.querySelector('#tempHtml')).append(compiledHTML);//Please added <div id="tempHtml"></div> in page/View for temporary storage, Clear the div after print.
console.log(angular.element('#tempHtml').html());
var myWindow = $window.open('', '', 'width=800, height=600');
myWindow.document.write(angular.element('#tempHtml').html());
myWindow.print();
angular.element('#tempHtml').html("");//Clearing the html
}, 300); // wait for a short while,Until all scope data is loaded If any complex one
};
Hope this will help you

Related

How to get ui-sref working inside trustAsHtml?

I have a activity state, and when there are no activities I would like to display a message. So I created a if/else statement that checks if the $scope activities has any content, if not it injects a certain code into the template.
if(!$scope.activities.length){
var empty = function(){
$scope.renderHtml = function (htmlCode) {
return $sce.trustAsHtml(htmlCode);
};
$scope.body = '<div>There are no activities yet, <a ui-sref="home.users">click here to start following some friends!</a></div>';
}
empty()
}
The problem is that ui-sref doesn't work, a normal 'a href` does work though. Are there any solid work arounds for this problem?
To get this work I created a element with ng-show,
%div{"ng-show" => "activitiesHide"}
And this js,
activitiesService.loadActivities().then(function(response) {
$scope.activities = response.data;
if(!$scope.activities.length){
$scope.activitiesHide = response.data
}
})
I place the results from the service in the activities scope, and then check in the js if it has content. If not activate the activitesHide show.

angularjs ng-click getting info from child element

I am trying to get info from the ng-click element. When I log $event from clicked element I get the right info but when I click the child element of then I get the info about the child and not the parent where the ng-click is set. Here is the fiddle link
http://jsfiddle.net/g3x2ndyc/5/.
var app = angular.module('app',[])
app.controller('appCtr', function($scope) {
$scope.testThis = function(evt){
//evt.stopPropagation();
// evt.preventDefault();
console.log(evt.srcElement.offsetLeft)
console.log(evt.srcElement.offsetWidth)
console.log('____________________________')
}
});
use evt.currentTarget not evt.srcElement (that one is mainly for IE).
Look at fiddle http://jsfiddle.net/g3x2ndyc/11/
var app = angular.module('app',[])
app.controller('appCtr', function($scope) {
$scope.testThis = function(evt){
console.log(evt.currentTarget.offsetLeft)
console.log(evt.currentTarget.offsetWidth)
console.log('____________________________')
}
});
Hard to tell exactly what you are ultimately trying to do, but you can just pass the id in to the click event then grab the position based on that.
ng-click="myevent('idName')
myevent(id) {
jquery('#'+id).position();
}

Why will my twitter widget not render if i change the view in angularjs?

Hi and thanks for reading.
I have a angular app im making and ive stumbled on a problem. set up as so
index.html-
<html ng-app="myApp">
...
<div ng-view></div>
<div ng-include="'footer.html'"></div>
...
</html>
I wont bother putting my routes its pretty simple /home is shows the /home/index.html and so on...
/home/index.html (default view when you come to the site)
<div class="responsive-block1">
<div class="tweet-me">
<h1> tweet me </h1>
</div>
<div class="twitter-box">
<twitter-timeline></twitter-timeline>
</div>
twitter timeline directive
directives.directive("twitterTimeline", function() {
return {
restrict: 'E',
template: '<a class="twitter-timeline" href="https://twitter.com/NAME" data-widget-id="XXXXXXXXXXXXXX">Tweets by #NAME</a>',
link: function(scope, element, attrs) {
function run(){
(!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs"));
console.log('run script');
};
run();
}
};
});
So I have just created a basic twitter directive using the tag from twitter. But when I change the view example to /blog then go back to /home the twitter widget no longer renders at all.
Im also using an $anchorScroll and if i jump to anyway on the page with this the widget also disappears. Any info would be great thanks.
See this post: https://dev.twitter.com/discussions/890
I think that you may be able to get the widget to re-render by calling
twttr.widgets.load().
If you find that this does not work, you will need to wrap this code into $timeout in your controller:
controller('MyCtrl1', ['$scope', '$timeout', function ($scope, $timeout) {
$timeout = twttr.widgets.load();
}])
To build on Sir l33tname's answer:
In services declaration:
angular.module('app.services', []).
service('tweetWidgets', function() {
this.loadAllWidgets = function() {
/* widgets loader code you get when
* declaring you widget with Twitter
* this code is the same for all widgets
* so calling it once will reference whatever
* widgets are active in the current ng-view */
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
};
this.destroyAllWidgets = function() {
var $ = function (id) { return document.getElementById(id); };
var twitter = $('twitter-wjs');
if (twitter != null)
twitter.remove();
};
});
Then in controller declarations:
angular.module('app.controllers', []).
controller('view_1_Controller', tweetWidgets) {
// load them all
tweetWidgets.loadAllWidgets();
}).
controller('view_2_Controller', tweetWidgets) {
// now destroy them :>
tweetWidgets.destroyAllWidgets();
});
Now whenever you leave view #1 to go to view #2, your controller for view #2 will remove the widgets associated with view #1 and when you return to view #1 the widgets will be re-instatiated.
The problem is because when Angular switches views the script tag that was originally inserted is not removed from the document. I fixed this on my own website by removing the Twitter script element whenever my Twitter timeline directive is not in the view. See the code below with comments.
function (scope, el, attrs) {
el.bind('$destroy', function() {
var twitterScriptEl = angular.element('#twitter-wjs');
twitterScriptEl.remove();
});
// function provided by Twitter that's been formatted for easier reading
function (d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0], p = /^http:/.test(d.location) ? 'http' : 'https';
// If the Twitter script element is already on the document this will not get called. On a regular webpage that gets reloaded this isn't a problem. Angular views are loaded dynamically.
if (!d.getElementById(id)) {
js = d.createElement(s);
js.id = id;
js.src = p + "://platform.twitter.com/widgets.js";
js.parentNode.insertBefore(js, fjs);
}
}(document, "script", "twitter-wjs");
}
Basically it's what Loc Nguyen say.
So every time you recreate it you must remove it first.
var $ = function (id) { return document.getElementById(id); };
function loadTwitter() {!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");}
var twitter = $('twitter-wjs');
twitter.remove();
loadTwitter();
Answer by #b1r3k works without problems :
put this in your controller:
$timeout(function () { twttr.widgets.load(); }, 500);
For those trying to load twttr.widgets.load() inside their controller, you will most likely get an error that twttr is not defined AT SOME POINT in your UX, because the async call to load the twitter script may not be completed by the time you controller instantiates and references twttr.
So I created this TwitterService
.factory('TwitterService', ['$timeout', function ($timeout) {
return {
load: function () {
if (typeof twttr === 'undefined') {
(function() {
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');
})();
} else {
$timeout = twttr.widgets.load();
};
}
}
}])
and then call TwitterService.load() inside the controllers that require your widgets. This worked pretty well. It basically just checks if the twttw object exists and if it does, just reload the script... otherwise just reload the script.
Not sure if this is the best implementation, but it seems like all other solutions have edge cases where it will throw an error. I have yet to find one with this alternative.

Backbonejs: model not getting passed in underscore template

I am new to backbonejs. What I am trying to do is, render a template on page load and pass model as data parameter in _.template function. Here is my bacbone code:
var Trip = Backbone.Model.extend({
url: '/trips/' + trip_id + '/show'
});
var InviteTraveller = Backbone.View.extend({
el: '.page',
render: function () {
var that = this;
var trip = new Trip();
trip.fetch({
success: function(){
console.log(trip); //logs trip object correctly
var template = _.template($('#invite-traveller-template').html(), {trip: trip});
that.$el.html(template);
}
});
}
});
var Router = Backbone.Router.extend({
routes: {
'': 'fetchTrip'
}
});
var inviteTraveller = new InviteTraveller();
var router = new Router();
router.on('route:fetchTrip',function () {
inviteTraveller.render();
});
Backbone.history.start();
And here is my sample template:
<script type="text/template" id="invite-traveller-template">
<h3>Trip</h3>
<h3><%= trip.get('name') %></h3>
</script>
On running, I am getting the this in browser window and console shows:
trip is not defined
I am facing this issue since yesterday but could not figure out the solution yet. Not understanding what is going wrong, code also seems to be right. Any help would be greatly appreciated.
Update:
I removed
inviteTravellers.render();
from router.on() and then reloaded the page in browser. I still got same error which means that <script></script> (template) is being compiled before calling render() of InviteTraveller view. What can be the possible reason for this?
I had the same issue (underscore v1.8.2). My fix:
var template = _.template($('#invite-traveller-template').html());
var compiled = template({trip: trip});
that.$el.html(compiled);
You're passing the whole model to the template. Typically you would call model.toJSON and then pass its result to the template. Additionally using <%= in your template to render the attribute, which is meant for interpolating variables from that JSON object you're passing.
You can pass a whole model to the template and use <% ... %> to execute pure Javascript code and use print to get the attribute but it's probably overkill.
Have a look at this fiddle.
You code work perfectfly, here's it
I think that your problem came from another code, not the one you have posted, because there's no way for your view to render if you remove :
inviteTravellers.render();
Try to chaneg <h3><% trip.get('name'); %></h3> by <h3><%= trip.get('name') %></h3>
My code seems to be right but still my template was getting compiled on page load and I was getting trip is not defined error. I did not understand the reason of this behavior yet.
I solved this issue by using handlebarsjs instead of default underscore templates.

using twiiter tooltip with backbone.js

full sample here
I have a very simple backbone js structure.
var Step1View = Backbone.View.extend({
el:'.page',
render:function () {
var template = _.template($('#step1-template').html());
this.$el.html(template);
}
});
var step1View = new Step1View();
var Router = Backbone.Router.extend({
routes:{
"":"home"
}
});
var router = new Router;
router.on('route:home', function () {
step1View.render();
})
Backbone.history.start();
This works well however i am unable to get this simple jquery function called.
$(document).ready(function() {
$('.tip').tooltip();
});
Update
School boy error here. Jquery onload functions need to be placed in the route. I'm very new to backbone so i'm not sure if this is best practice. But the following works.
render:function () {
var that = this;
var savings = new Savings();
savings.fetch({
success:function () {
var template = _.template($('#step3-template').html(), {savings:savings.models});
that.$el.html(template);
// put your jquery good ness here
$('.tip').tooltip();
$(".step3-form").validate();
}
})
}
Looks like you found your answer! Just wanted to also share that you could scope down your jQuery a bit by doing this instead.
savings.fetch({
success:function () {
var template = _.template($('#step3-template').html(), {savings:savings.models});
that.$el.html(template);
that.$el.find('.tip').tooltip();
that.$el.find(".step3-form").validate();
}
What you have in your example works but it's also scanning the whole document every time for HTML with the class tip where you could use the element you just created to scan downward only for the tip you just created inside it. Slight optimization.
Hope this is helpful!
Looks like you found your answer! Just wanted to also share that you could scope down your jQuery a bit by doing this instead.
savings.fetch({
success:function () {
var template = _.template($('#step3-template').html(), {savings:savings.models});
that.$el.html(template);
that.$el.find('.tip').tooltip();
that.$el.find(".step3-form").validate();
}
What you have in your example works but it's also scanning the whole document every time for HTML with the class tip where you could use the element you just created to scan downward only for the tip you just created inside it. Slight optimization.
Hope this is helpful!

Resources