Combining Play! Framework 2.xx with Angular.js - angularjs

I am having trouble is this marriage of 2 seemingly powerful frameworks. It seems most things that can be done by 1 can be done by 2.How to best utilize the two? Are there any patterns of thinking?
Take a basic example of a CRUD application --
I can write a route mysite/listnames which maps to a controller in play! and this renders a template with the code --
#(names:List[String])
#main("Welcome") {
#for( name <- names ){
<p> Hello, #name </p>
}
Note that main is a typical bootstrapping template.
However now the output this produces seems to be of no use to Angular if say i want to add a input box for filtering these names, or i want to do anything with them at all.
What is a typical way of proceeding?
The base thing seems to be-
1) how to pass on data that arrives after rendering the template by Play to angular for later use at clientside.
2) Is it adviseable at all to use these two frameworks together for a large-level app involving a mathematical object oriented backend + server, and a fairly intensive UI at frontend?

There are many ways you can combine this two frameworks. All depends on how much you want to envolve each of them. For example your Play 2 application may only serve JSON request/respons from the one side(server side) and AngularJS would make all the other stuff from the client side. Considering your example for basic CRUD app :
A Play 2 controller:
def getNames = Action {
val names = List("Bob","Mike","John")
Ok(Json.toJson(names)).as(JSON)
}
Your Play root for it:
GET /getNames controllers.Application.getNames
an AngularJs controller:
app.controller('NamesCtrl', function($scope) {
// get names using AngularJS AJAX API
$http.get('/getNames').success(function(data){
$scope.names = data;
});
});
Our HTML :
<!doctype html>
<html ng-app>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js"> </script>
</head>
<body>
<div>
<ul>
<li ng-repeat=" name in names">{{name}}</li>
</ul>
</div>
</body>
</html>
This way you completely separate the concerns, for your client side, it doesn't matter how the server side is implemented, only thing you need is valid JSON as a response. It's considered to be a good practice.
But of course you can render most of your HTML from Play 2 and use AngularJS for some specific stuff where needed. All depends what conception you choose for your app.
...how to pass on data that arrives after rendering the template by
Play to angular for later use at clientside?
I don't think that it's a good idea, but you surely may do it using ngInit directive like this:
#(message:String)
#main("Welcome") {
<div ng-init="angular_message = #message">
<h1>Hello, {{angular_message}} !</h1>
</div>
}
and you will have angular_message in the scope initialised with #message value from Play 2 template.
Is it adviseable at all to use these two frameworks together for a
large-level app involving a mathematical object oriented backend +
server, and a fairly intensive UI at frontend?
From my point of view, yes, it's two great frameworks and they perfectly work in concert.

Related

Trouble implementing google sso in AngularJS application

So I have been trying to implement google single sign on into my angular application; however, sometimes when I reload the page the button disappear. My angular application is using angular routing. If I were to put my button outside of this it would work as expected. It just runs into problem when its loaded through a partial. Any idea how I can fix this?
<div class="g-signin2" data-onsuccess="onSignIn"></div>
<div ng-view></div>
As #agektmr said, the problem is related to the way angular and platform.js interact with each other.
In order to use the auto rendered button you need to trigger the library when the DOM is loaded.
What I did is calling the following code in the onComplete method (I'm working with AngularMaterial dialogs, but you should be able to find a similar method quite easily):
$timeout(function() {
$window.gapi.signin2.render('g-signin2');
});
The only difference is in your html you should change your div and instead of adding it a g-signin2 class you should add an g-signin2 id:
<div id='g-signin2' data-onsuccess='yourMethod'></div>
If you're willing to learn more about Google's implementation you could take a look here.
I'd recommend using imperative approach for implementing the button for this.
<div id="signin">
<button>sign-in</button>
</div>
<script>
document.querySelector('#signin').addEventListener('click', function() {
var auth2 = gapi.auth2.getAuthInstance();
auth2.signIn();
});
</script>
Find more concrete example here
https://github.com/GoogleChrome/google-sign-in
The code you indicated didn't work because of timing. platform.js library tries to take care of it but fails because it's before angular renders DOM.

AngularJS: How to scale repetitive but slightly-dynamic content

I need to implement a browser-like banner warning system so that the user can only access certain features after they've acknowledged certain warnings. The warning messages will depend on data sent from the server and I have to implement different styles and links.
Edit: There could be any number of warnings displayed at the same time. One for each feature. The user must individually acknowledge each warning before the corresponding feature is enabled. Some of the warning texts are static, some are dynamic. Some can have different acknowledge links instead of the standard "okay".
Traditionally, I would package each kind of warning into a class (in the OO sense) and push them to screen through a centralized method, e.g.
displayWarning(new InfoBanner("You must ... before you can modify " + data.name, function onclick() { ... }));
Here the InfoBanner class will have a method that creates the banner elements and attach the event handler.
The Angular way of doing this, on the other hand, seems to be you write the banner entirely in HTML with ng-if and ng-click, etc. E.g.:
<p style="info banner" ng-if="...">You must ... before you can modify {{...}}. <a href ng-click="...">Okay</a></p>
However, this seems quite unfocused and messy because there will now be a large blob of banner code dwarfing the functional part of the page. (There are hundreds of error types defined!)
Is there any way to resolve this without reverting to the fully imperative code?
(Note: a custom directive is probably not the answer as <p style="info banner" is almost like a directive and there's little sharable code among these warnings beyond this.)
(Edit: One can see this question in another way: in the imperative world, the warning-adding logic are scattered in the code but close to the feature they're protecting, so they're easy to understand and maintain. In the declarative world, they must be centralized to the place where they're displayed. I would like a solution where they're declared close to the component they're protecting but displayed centrally.)
What I understand from your question your problem is that since you are in an Angular application you need / should include your banners as markup in your HTML view, since however whether each banner is displayed or not depends on the data you are getting from the server you only know if a specific banner should be displayed in your controller (hence the ng-if you have included in your banner HTML example).
What I would propose in this case would be to create a BannerService which would hold a list of all the banners that should be displayed at any given time. In your controller you can use the functions exposed by the service to add banners to the list when the data you got from the server indicates that you should do so. Each banner in the list would be an object containing all the information that might be different between different banners (ex. banner text, type, etc.) meaning that your HTML view doesn't really need to "know" anything about specific banner details and can just display all the banners available in the BannerService using an ng-repeat.
You can see below a quick-and-dirty example to better understand how this would work.
var app = angular.module('TestApp', [])
app.service('BannerService', [function(){
var banners = [];
this.getBanners = function() {
return banners;
}
this.addBanner = function(banner) {
banners.push(banner);
}
}])
app.controller('TestCtrl', ['BannerService', function(BannerService) {
// Add banners to the banner service depending on your data etc.
BannerService.addBanner({text: "This is a banner", type: "info"});
}])
app.controller('BannerCtrl', ['$scope', 'BannerService', function($scope, BannerService) {
$scope.banners = []
$scope.$watch('BannerService.getBanners', function (newVal, oldVal, scope) {
$scope.banners = BannerService.getBanners();
});
}])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="TestApp">
<div ng-controller="BannerCtrl">
<div ng-repeat="banner in banners"><p class="banner {{banner.type}}">{{banner.text}}</p></div>
</div>
<div ng-controller="TestCtrl">Page content</div>
</div>
 In my opinion what you are looking to implement fits perfectly with Aspect Oriented Programming. If you've never used AOP before be prepared for some light reading. The concept is simple and works very well with Angular's patterns. There is an AngularAOP project, but before you dive into it I suggest running through this article first:
http://www.bennadel.com/blog/2425-decorating-scope-methods-in-the-angularjs-prototype-chain.htm

Is it possible to "angularize" data already in the page? (AngularJS + Drupal)

I have an HTML page that's been generated on the server. It contains data similar to this:
<ul>
<li>Banana</li>
<li>Apple</li>
<li>Pear</li>
</ul>
Is it possible to "angularize" (or "post-compile") such data to obtain the same behavior as if the list had been generated with:
<ul>
<li ng-repeat="item in items">{{item.name}}</li>
</ul>
That way, my list would be sortable, filterable, etc.
Why would I want to do that?! ;-)
I'm using Drupal as my page generator and for multiple reasons I'd like to keep it that way (my content is translatable, themable with Drupal's theme functions, etc.)
Having the initial version of the page fully rendered by the server makes it indexable by search engines. The AngularJS behaviors are mostly just UI enhancements.
I'd like to avoid an additional roundtrip to the server just to re-transfer the same data.
Caveats:
I'm not just asking how to implement the desired behavior with AngularJS + Drupal (I could expose Drupal's data via an endpoint to which AngularJS would send requests). Instead, I'm asking how to recycle data that's already in the HTML to turn it into an AngularJS model (without resorting to ngInit, ideally).
What I am asking breaks the MVC pattern, I know.
In case you're curious, the site is http://ng-workshop.com/ (it's a collection of AngularJS resources and tutorials).
Thanks!
One way to provide angular the data is to echo out the php data to the page as a javascript object above the other included scripts on the page.
$dbResult = ['f1', 'f2', 'f3'];
echo "<script type='text/javascript'> var php_data = " . json_encode($dbResult) . ";</script>";
then somewhere in your angular js code...
$scope.items = php_data || [];
You may want to place the data in a namespaced javascript object to prevent any kind of stomping on.
Example:
PHP -> echo "myApp.page.data =" . json_encode($dbResult) . ";"
Angular -> $scope.items = myApp.page.data || [];

AngularJS register controller once

That's what I'm doing. There is application with pages and different controls that may be put on pages by site admin/editor. All pages share one ng-app defined on master page. All controls are supplied with .js files with angular controllers. Let's suppose that I have an image gallery block:
<div ng-controller='imageGalleryCtrl'>
do something amazing here
</div>
<script src='imageGallery.js'></script>
Inside script there is a simple controller registration like:
angular.module('myApp').controller('imageGalleryCtrl', ... );
So. If I have 10 image galleries, I'll execute controller registration 10 times. It looks like this will work, but hell - I don't want it to be so =)
For now I just have all controls' scripts registration on a master page, but I don't like it as well, because if there is no image gallery on a page, I don't want it's script be downloaded during page load.
The question is - is there any proper way to understand if controller have been registered in a module already and thus prevent it from re-registering?
---------------
Well, though I've found no perfect solution, I must admit that the whole idea isn't very good and I won't think about it before my site will grow too big to assemble whole angular app on master page.
You should declare your controller but once. Instead of having one controller per gallery, have your single controller handle all image galleries. The controller should make a request to the REST backend to fetch the images of the desired gallery.
I see that instead of ng-view, you're using the ng-controller directive, indicating that probably you're not using Angular's routing. Try switching to using routes.
Have a look at Angular.js routing tutorial. It shows you how to use the ngRoute module. Then, in the next chapter, the use of $routeParams is described. Via the $routeParams service, you can easily say which gallery should be displayed by providing its ID in the URL; only one controller will be necessary for all your galleries.
If you really must check whether a given controller has been declared, you can iterate through the already declared controllers (and services... and pretty much everything else) by checking the array angular.module("myApp")._invokeQueue. The code would probably look something like this (not tested!):
var isRegistered = function(controllerName)
{
var i, j, queue = angular.module("myApp")._invokeQueue;
for (i = 0, j = queue.length; i < j; ++i) {
if (
queue[i][0] === "$controllerProvider"
&& queue[i][1] === "register"
&& queue[i][2][0] === controllerName
) {
return true;
}
}
return false;
};
Bear in mind however that while this may (or may not) work, it's far from being the correct thing to do. It's touching Angular's internal data that's not meant to be used in your code.

backbone: how to pass bootstrap data without server side tags

The normal way to bootstrap your app with backbone as described in the docs is this
var Accounts = new Backbone.Collection;
Accounts.reset(<%= #accounts.to_json %>);
Here, we are using the server side tags <%= ... %>, <?php echo ... ?>, etc.
But in my app I am passing very thin HTML from the server. Something like this
<html><head></head><body></body>
<script src="init.js"></script>
<html>
In this case how should I bootstrap my data for my backbone models and collections?
Backbone recommends against using fetch
Note that fetch should not be used to populate collections on page load — all models needed at load time should already be bootstrapped in to place. fetch is intended for lazily-loading models for interfaces that are not needed immediately.
But I wonder if that's the right thing to do in cases like mine?
I didn't want to put my opinion as an answer, but I think I can say, "there is no reason, technical or otherwise, not to use fetch to load your models on page load in this use case". 8)
You need your JS code to be parsed by your server side platform and that it inserts the data on it.
Two approaches can be:
1. Make you init.js to be parsed by your server side
<html>
<script src="init.js.php"></script>
<html>
In your initi.js.php you can use the interpolate tags.
2. Load the data in a separate interpretable JS file
<html>
<script src="data.js.php"></script>
<script src="init.js"></script>
<html>
The data.js.php can be some thing like this:
MyApp.data = <?php echo ... ?>
In your init.js you can use MyApp.data to reset your Collections.

Resources