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

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 || [];

Related

angularjs with <portlet:renderURL/> and spring mvc portlet

I have this code for my table :
##
<tr ng-repeat="elt in tabDemandes">
<td>{{elt.id}}</td>
<td>{{elt.name}}</td>
<portlet:renderURL var="maj">
<portlet:param name="action" value="maj"/>
<portlet:param name="idD" value="elt.id"/>
</portlet:renderURL>
</tr>
##
I want recover a value of my param "idD" in my controller but it take elt.id as a value of a param and he give me error
can someone help me ?
To have access to the idD in your controller just set a $scope that can hold it. Angularjs is built with 2-way data-binding in mind so really what this lets you do is make changes in the view that will go to the controller as well as changes in the controller being pushed out to the view.
In your case just make sure you are storing the idD as an ng-modle that links back to a scope you declared in the controller. Once you have that linked correctly you can get the idD in your controller no problem.
Obviously, the difference is that the JSTL taglib (<portlet:renderUR>L) runs on the server, while AngularJS and the ng-repeat runs on the client. At the moment AngularJS is looping over your content, the URL has already been rendered.
For more information about the differences of server- and clientside, look at this answer: What is the difference between client-side and server-side programming?
What we did was quite simple, we attached the URLs to our model (in your case tabDemandes). This is an example we used for creating an action URL with the ID as a parameter:
PortletURL detailURL = response.createActionURL();
detailURL.setParameter(ActionRequest.ACTION_NAME, "detailTask");
detailURL.setParameter("id", Long.toString(task.getId());
task.setDetailURL(detailURL.toString());
In this case we were using a list of tasks, and we used this action URL to open a detailed page of the task, so we had to pass the ID to the action URL. In stead of using the JSTL taglib we used the Java API.
In your case you'll probably need to use response.createRenderURL().

not able to dynamically update the view when data is changing

I have a set of files in a server which I am looping though and constructing a JSOn and saving it as a separate file. I am using python for this. Works quite well. Now the scope is the number of files in the directory will increase/ change throughout the day..and I am running the script every 10 min to rewrite the json...the file name stays same and i am calling it in a single page html document using angular.js..Again fairly simple...But now I am having problem when the JSON is changing I am not seeing any change on the page unless I reload the page. Could I do something about this?
With angular I am using
$http('something.json').success(callback function with some argument data)
and in the markup something like
<ul>
<li ng-repeat="x in data">{{x.id}}</li>
</ul>
Your call to $http is one-time operation which happens after page load like this:
$http('something.json').success(function(data){
$scope.data = data;
});
angular is kickstarted
ng-controller containing $http request is evaluated and request for 'something.json' is sent
...
when your json arrives, your success function is called with data from json
view (html template) is updated with new data
Angular keeps your model (eg $scope.data) and UI (expressions in template) up to date, but it doesn't update external resources.
If you want to periodically poll for changes in 'something.json'
you can use $timeout service as suggested in JaKXz's comment.

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.

Combining Play! Framework 2.xx with Angular.js

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.

Backbone Marionette modules as Widgets similar to Twitter Flight

I'm reading up in choosing the correct client-side framework to segment/modularize my frontend code in Widgets.
Basically what I have/want is:
a complex website with multiple pagetypes, so no single-page application.
all pages are able to render a complete page WITHOUT the use of javascript. IOW: javascript is used as enrichment only.
Lots of pages have a very dynamic way in which widgets can be shown on screen. To overcome complexity at the server-side I've modularized my code into widgets (composite pattern), where each widget is responsible for it's own:
server-side controller code
server-side templating (using hogan/mustache)
routing endpoints, should it need to be called from the client
structural css (css converning the structure of the widget as opposed to the look&feel)
a server-side RegionManager ultimately decides which widgets are rendered and where they are rendered on screen. Endresults is that the RegionManager spits out the entire html (server-generated) as the composite of the rendering of all of it's widgets.
Now, some of these widgets DO have client-side logic and need rerendering on the client. Take a searchpage for instance, which needs to be able to update through ajax. (I've described this process, which uses DRY templating on client and server, here)
What I ultimately want is that, given I already use the composite pattern on the server, to extend this to the client somehow so that a Widget (1 particular logic block on the screen) contains all mentioned server-side code, plus all needed client-side code.
I hope this makes sense.
Would Marionette be suited to be used as a client side framework in this scenario? I'm asking since I'm not 100% sure if the concept of a Marionette Module is what I describe as being a Widget in above scenario. (I'm mentioning Twitter Flight in my question, since I believe this would be a fit, but it currently is so new that I'm hesitant to go with it at the moment_
I think basically what I'm asking is if anybody has some experience doing something along these lines.
I think just using Backbone.js is perfect for this type of application you are describing. You have probably already read this, but most of the backbone literature is focused around your views having associated server generated JSON models and collections, then using the View's render function to generate (on the client) the HTML UI that represents the model/collection.
However it doesn't have to be used this way. In fact there is nothing stopping you attaching views to existing elements that contain content already, which gives you all of the benefits of Backbone's modularity, events system and so on. I often use views that have no model or collection, purely because I like the conformity of style. I have also used an approach like I describe below in the cases where I have had to work with older, existing applications that have not yet got, or never will have a nice REST API, but they do provide content in HTML.
Firstly, lets assume the following HTML represents one of your widgets:
<div id="widget">
<div class="widget-title"></div>
<div class="widget-body">
<!-- assume lots more html is in here -->
Do something!
</div>
</div>
In this case, you could use backbone with a single Widget Model. This would be a very simple model, like this:
App.WidgetModel = Backbone.Model.extend({
intialize: function () {
this.url = this.options.url;
}
});
Take note of the fact the Widget receives a URL as a parameter to its constructor/initialize function. This widget model would represent many of your widgets (and of course you could adopt this general approach with more complicated models and pluck different data from the rendered HTML). So next for your views. As you probably know, normally you pass most views a model or collection when you instantiate them. However in this case, you could create the Widget model in your View's initialize Function and pass it a URL from the pre-rendered HTML as follows:
App.WidgetView = App.View.ComboboxView = Backbone.View.extend({
initialize: function () {
this.model = new App.WidgetModel({}, { url: this.$("a").attr("href") });
}
// rest of the view code
});
So instantiating the view would be something like:
new App.WidgetView({el: $("#widget")})'
By doing all of the above you can do pretty much everything else that backbone offers you and its modular and encapsulated nicely, which is what you are after.
The end result of this whole approach is:
You have rendered the Widget UI as pure HTML which (I assume) is functional without JavaScript.
You attach a View to the existing HTML.
You pass into the View as options, content by extracted (such as a URL) from the rendered HTML with jQuery.
The View is responsible for instantiating the Model passing on the relevant options the model needs (such as a URL).
This means all dynamic server side content is intially contained in the rendered HTML and your View is a modular JavaScript component that can do stuff to it, which I think is the end result you're after.
So you mentioned that you would like to have AJAX functionality for your widgets and that fine with this approach too. Using this approach, you can now use the standard Backbone fetch and save functions on the Widget model to get new content. In this example it is from the URL retrieved from the rendered HTML. When you get the response, you can use the view's, render function, or other finer grained functions to update the HTML on the page as required.
A few points:
The only thing to look out for is that you'll need to change the content type of the fetch and save functions to "text/html" if that's what the server is providing. For example:
this.model.fetch({
type: "POST",
contentType: "text/html"
});
Lastly, the model I have proposed is instantiated with no content. However if your ajax calls are a content type of "text/html", you may need to play around with you model so it can store this content in its attributes collection properly. See this answer for more information.

Resources