Phantom Js with Node and Angular - ng-repeat not rendering - angularjs

I have an angular App, using angular-ui-router.
Page I have built looks fine.
I am trying to take a snapshot of the html and create a pdf.
To do this, I am using Phantom.
I can trigger and create a PDF fine, but it doesn't render my ng-repeat section of the HTML.
My HTML:
<div>{{vm.hello}}</div>
<div class="row">
<div class="col-xs-2">Heading</div>
</div>
<div class="row" ng-repeat="record in vm.records">
<div class="col-xs-2">Space</div>
<div class="col-xs-2" ng-bind="record.id"></div>
</div>
My controller triggers on load to populate vm.records.
vm.hello is hardcoded and renders fine.
Nothing in my ng-repeat loads.
My Node js code:
var phantom = require('phantom');
function pdf(req, res) {
console.log('pdf function entered');
var url = "http://myapp.com/page";
phantom.create().then(function(ph) {
ph.createPage().then(function(page) {
page.viewportSize = {
width: 1400,
height: 1024
};
page.property('onLoadFinished', function(status) {
console.log("Load Finished with status " + status);
});
page.open(url).then(function(status) {
page.render('test.pdf', {
format: 'pdf',
quality: '100'
}).then(function() {
console.log("File Created");
page.close();
ph.exit();
});
});
});
});
res.status(200).send();
}
Can anyone shed some light as to why the ng-repeat won't work? I've tried adding delays in and such based on other questions/answers, but just can't seem to get it working.

In your controller you have to call
window.callPhantom();
when the render is completed
Next is you have to call it inside $timeout like this
$timeout(function(){
window.callPhantom();
return;
})
to avoid call it before binding

Related

Implement 'loading' icon while waiting for server response (AngularJS)

I am pulling data from an API, so that a template is populated with the response values. However, it sometimes takes around 6 seconds to successfully pull the data, at which point the template is blank except for the headings.
Loading...
Position:
Age:
Height:
Club:
Loading finished:
Anthony Lopes
Portugal
Position: Goalkeeper
Age: 26
Height: 184 cm
Club: Olympique Lyonnais
I want to apply a loading icon (a spinny wheel or something) to indicate that it is waiting for a response from the API.
I'm assuming I need to set the loading state and attach it to a variable, and then say ng-if="loadingContent()" or something, and then create a function to show the loading state.
Is that right? If so can flesh out the process a bit more for me?
If not, how do you do it?
Thanks in advance.
You can use the cgbusy directive:
https://github.com/cgross/angular-busy
Include the file in your HTML and add cgBusy as dependency to your app:
angular.module('myApp', ['cgBusy']);
You can setup a loading template and include it in your HTML like this:
<div cg-busy="{promise: myPromise.$promise, message: 'Loading...',backdrop:true}"></div>
You now have to bind $scope.myPromise to your API call (which should return a promise) like:
$scope.myPromise = $http.post("/echo/json/", data).success(function(data, status) {
$scope.hello = data;
});
This way the loading template (which you can edit) will be visibile as long as the API call is busy. You can add this to any API call you want or add an other template for other calls.
You can customize everything easy in the directive, read more on the provided Github page.
Simply just assign a boolean scope variable when your AJAX/HTTP call is completed. In my example, I just use a $timeout of 5 seconds.
JS/Angular:
$scope.isLoaded = false;
$timeout(function() {
$scope.isLoaded = true;
}, 5000)
}
HTML:
<!-- This element is displayed while loading -->
<div ng-if="!isLoaded" id="loading-container">
<div id="loading-inner">
<img src="http://loadinggif.com/images/image-selection/27.gif" />
</div>
<h4>Loading...</h4>
Position: <br/>
Age: <br/>
Height: <br/>
Club:<br/>
</div>
<!-- This element is displayed after loading is complete -->
<div ng-if="isLoaded">
<h4> Loading finished:</h4>
Anthony Lopes<br/>
Portugal<br/>
Position: Goalkeeper<br/>
Age: 26<br/>
Height: 184 cm<br/>
Club: Olympique Lyonnais<br/>
</div>
Honestly, I could spend hours styling the loading screen in the demo, but that's decent enough to get you on your way so that you can understand how it all works.
If you are using Angular's $http function to perform your API calls, you would set the variable like so:
$scope.isLoaded = false;
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
$scope.isLoaded = true;
});
WORKING EXAMPLE
Below approach you can use:
Template Code:
<div>
<div ng-if="isLoading" class="loaderIcon">
</div>
<div ng-if="!isLoading">
<div>
Main Content
</div>
</div>
</div>
Contorller Code:
app.controller('mainCntrl', function($scope, backendService) {
function successCallback(res) {
$scope.isLoading = false;
}
function failureCallback(res) {
$scope.isLoading = false;
}
$scope.fetchData = function() {
$scope.isLoading = true;
backendService.fetchData(successCallback, failureCallback);
}
});
Service Code:
app.service('backendService', function($http) {
this.fetchData = function(successCallback, failureCallback) {
$http.put('url', data).then(function(res) {
successCallback(res);
}, function(res) {
failureCallback(res);
});
};
});

angular ng-repeat to always show even on empty object

Hi I want to post item to server, and with each successful addition, automatically add it to DOM with ng-repeat
<div class="" ng-repeat="book in books" >
<div id="eachBook">{{book.title}}</div>
</div>
to POST the data and also to upload an image file, I use Jquery ajax, and $state.go(".") to reload the current page:
var fd = new FormData();
fd.append("file", bookImage);
$http({
method: 'POST',
url: "/someurl,
data: fd,
headers: {
'Content-Type': undefined
}
}).success(function(Image){
var book_obj = {
bookTitle: bookTitle,
bookImage: Image._id
};
$http.post("url to owner book", book_obj)
.success(function(data){
$scope.bookImage = data.bookImage;
$timeout(function(){
alert("success", "successfully added your book");
$state.transitionTo('book', {}, { reload: true });
},2000);
})
})
The problem is with first addition, the DOM is still empty, and even though I use $state to reload the page, it still not working for the first addition. In the end I need to refresh the page manually by clicking refresh.
after the first addition, it works fine. With each book added, it automatically added to DOM..
Any idea how to automatically start the first one without manually rendering the page? using $timeout to delay the refresh has no effect.
Is it not just a simple post to list on success?
var app = angular.module('myApp', []);
app.controller('bookCtrl', function($scope, $http, $timeout) {
$scope.init = function(){
$scope.title = 'initial book?'
postBook();
};
$scope.books = [];
$scope.post = function() {
postBook();
};
function postBook(){
if (!$scope.title) return;
// timeout to simulate server post
$timeout(function() {
$scope.books.push({title:$scope.title});
$scope.title = null;
}, 1000);
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="bookCtrl" ng-init="init()">
<div class="" ng-repeat="book in books">
<div class="eachBook">{{book.title}}</div>
</div>
<input type="text" ng-model="title" /><button ng-click="post()">save</button>
</div>
EDIT: Not sure why your DOM isn't ready but how about ng-init to accomplish an initial book post?

Angularjs how can I reload the content

I have this code that loads the content when the page load,
Now I want to know how to reload the content by clicking the button.
Can you show me how to do it with example please?
Javascript code:
.controller('InterNewsCtrl', function($scope, NewsService) {
$scope.events = [];
$scope.getData = function() {
NewsService.getAll().then(function (response) {
$scope.events = response;
}), function (error) {
}
};
$scope.getData(); // load initial content.
})
Html code:
<ons-toolbar fixed-style>
<div class="left">
<ons-back-button>Voltar</ons-back-button>
</div>
<div class="right">
<ons-toolbar-button><ons-icon icon="ion-android-refresh"></ons-icon></ons-toolbar-button>
</div>
<div class="center">Internacional</div>
</ons-toolbar>
I think you're asking how to just retrieve new events from the backend. If that's correct, you don't need to reload the entire page.
You already have a function called getData which goes and retrieves you data via your service. Assuming your service doesn't cache the data, just call getData from your button:
<ons-toolbar-button ng-click="getData()"><ons-icon icon="ion-android-refresh"></ons-icon></ons-toolbar-button>
P.S. if you do explicitly have the cache set to true in your service, you can remove the cached data with $cacheFactory.get('$http').removeAll();.
For reloading same page in angular Js without post back
first remove that url from template cache if you call $route.reload() without removing it from $templateCache it will get it from cache and it will not get latest content
Try following code
$scope.getdata=function()
{
var currentPageTemplate = $route.current.templateUrl;
$templateCache.remove(currentPageTemplate);
$route.reload();
}
and Call it as following
<input type="button" ng-click="getdata();" value ="refresh"/>
Hope this will help
Please reffer this

Refreshing of observables in AngularJS

I'm using AngularJS and can't find way to resolve this Issue:
there is part from my controller:
$scope.$on('showMoreNotifications', function (event) {
$.ajax({
url: '/notifications',
data: {
notificationCount: 30
},
success: function (e) {
$scope.notifications = e.Messages;
}
});
});
and here is html which using this controller:
<div class="widget" id="widget-notifications" ng-controller="NotificationsCtrl">
<span class="title" ng-click="$parent.$broadcast('showMoreNotifications')">#*showMoreNotifications()*#
Notifikace
</span>
<div class="messages">
<div ng-repeat="item in notifications" class="message-item type-{{item.Entity}}" data-id="{{item.AuditLogId}}">
<span class="type"></span>
<div class="description">
<span class="date">{{item.Date}}</span> / {{item.Message}}
</div>
</div>
</div>
</div>
If I click on span class title on top, controller right call to server and receives JSON data. Unfortunately dont refresh html which is associated with it. When I click second time, html refresh data from first request.
Your template is not updating since your are making xhr calls using jQuery. Those calls are considered "outside of AngularJS" world so AngularJS is not aware of them and doesn't know that it should start it automatic refresh cycle.
You would be much better using excellent $http service from AngularJS to make xhr calls. You would write something like:
$http('/notifications', {params : {
notificationCount: 30
}}).success(function (e) {
$scope.notifications = e.Messages;
});
There was a similar question where the answer helps migrating from jQuery's $.ajax to AngularJS $http: https://stackoverflow.com/a/12131912/1418796
Next, something not directly related, but you really don't have to broadcast events to react on the click event. It would be enough to write:
<span class="title" ng-click="myClickHandler()">
#*showMoreNotifications()*#
Notifikace
</span>
and then in your controller:
$scope.myClickHandler = function(){
//call $http here
}
Now I resolved my issue... It needs apply on scope
like this:
$.ajax({
url: Escudo.UrlHelper.baseUrl + 'Widgets/Notifications/GetLastNotifications',
data: {
notificationCount: 30
},
success: function (e) {
$scope.$apply(function () {
$scope.notifications = e.Messages;
});
}
});

AngularJS ngRepeat Not Updating Display Properly

I have have a page where I am using plupload to upload files and having a weird issue with a ng-repeat not updating properly. Here is the relevant code:
<div ng:app>
<div name="myForm" ng-controller="Ctrl">
<input ng-model="test" type="text" />{{test}}<div id="container" class="controls">
<div id="filelist">
<div ng-repeat="file in filesToUpload">{{file.name}} ({{file.size}}) <b>{{file.percent}}</b></div>
</div>
<br />
<a id="pickfiles" href="#">[Select files]</a>
</div>
</div>
</div>​
function Ctrl($scope) {
$scope.test = '';$scope.filesToUpload = [{id: 1, name: 'test', size: '123kb'}];
$scope.addItem = function(object) {
$scope.filesToUpload.push(object);
}
$scope.uploader = new plupload.Uploader({
runtimes : 'html5,flash,browserplus,gears',
browse_button : 'pickfiles',
container : 'container',
max_file_size : '10mb',
url : 'upload.php',
flash_swf_url : '/plupload/js/plupload.flash.swf'
});
$scope.uploader.init();
$scope.uploader.bind('FilesAdded', function(up, files) {
$scope.filesToUpload = [];
$.each(files, function(i, file) {
$scope.addItem({
id: file.id,
name: file.name,
size: plupload.formatSize(file.size)
});
});
console.log($scope.filesToUpload);
up.refresh(); // Reposition Flash/Silverlight
});
}​
Here is a trimmed down jsfiddle showing the issue happening:
http://jsfiddle.net/9HuUC/
To reproduce this issue do the following:
Click on [select files] and selects a few files (notice how you don't see the files displayed anywhere on the output)
Type any character into the input box (magically the files that you select know appear)
What would cause this type of behavior? I mean I know that the data is properly being set in $scope.filesToUpload because I have the console.log() there and even checked it in Batarang and it loods good there but for some reason something else needs to be updated for the display to be updated.
Interestingly enough, I have another ng-repeat that is working fine on the same page. I am wondering if it has anything to do with where the code is (being inside the FilesAdded event on the uploader).
The issue is due to the fact that the FilesAdded callback is executed outside the scope of AngularJS (it's called by the uploader), therefore the scope updates won't be triggered.
To solve this, just add the $scope.$apply call in the callback, encapsulating your existing code:
$scope.uploader.bind('FilesAdded', function(up, files) {
$scope.$apply( function() {
$scope.filesToUpload = [];
$.each(files, function(i, file) {
$scope.addItem({
id: file.id,
name: file.name,
size: plupload.formatSize(file.size)
});
});
console.log($scope.filesToUpload);
up.refresh(); // Reposition Flash/Silverlight
});
});
With this update, it's working in the fiddle. For reference see the AngularJS official documentation, $apply method of the scope object:
http://docs.angularjs.org/api/ng.$rootScope.Scope

Resources