Passing a List to View in Play Framework - list

So, I'm using the Play Framework in conjunction with MongoDB. I'm attempting to pass a List to my view to loop through and display the rows.
Controller:
public static Result find() {
List<DBObject> results = MongoController.find(MongoController.getLocalConnection("test"), "jobs");
if (results == null) {
redirect("/");
}
return ok(find_job.render(results));
}
The MongoController called above uses the following function (functioning - tried and tested):
public static List<DBObject> find(DB database, String collectionName) {
DBCursor cursor = database.getCollection(collectionName).find();
List<DBObject> items = new ArrayList<DBObject>();
while (cursor.hasNext()) {
items.add(cursor.next());
}
return items;
}
View:
#(results : List[DBObject])
The error I'm receiving is:
Compilation error:
Not found: type DBObject
Does anybody have any pointers please? I apologise - I'm new to this framework.

Twirl templates don't work quite the same way when importing things into the local namespace. By default only the models package, and a few other Play packages are imported into the view namespace. Imports made afterward are not used for the template parameters.
You need to either use the fully-qualified package name:
#(results: List[com.mongodb.DBObject])
Or you can add to the templateImports key in build.sbt. i.e., you'd drop this line into your build.sbt file:
TwirlKeys.templateImports += "com.mongodb.DBObject"
This will add the previous import to all compiled templates. Note that it's for Play 2.3.x. If using 2.2.x, then you'd use the following instead:
templatesImport += "com.mongodb.DBObject"

Related

How to inject app.context into Loopback 4 controller

I cannot find any suitable example on how to inject an app.context object into a Loopback 4 controller being in a separate file
This inline example from the documentation works fine
import {inject} from '#loopback/context';
import {Application} from '#loopback/core';
const app = new Application();
app.bind('defaultName').to('John');
export class HelloController {
constructor(#inject('defaultName') private name: string) {}
greet(name?: string) {
return `Hello ${name || this.name}`;
}
}
but I cannot find a way to obtain the same having my controller in a separate file.
I am trying to do something like this:
export class PingController {
constructor(#inject(app.name) private name: string)
app.name being a simple binding in my app-context.
Solution was quite simple.
Since all context values on app level is available throughout the application, no reference to app is required.
I just needed to replace (app.name) with ('name') in the constructor injection.

Loopback include unrelated lists

In Loopback it is easy to include relational objects when querying for data. For example, one can include all the comments that belong to a blog post in a single call using the include filter.
But in my case I want to get data that doesn't have a relation.
I have a User Detail page. On that page a user can choose a username and there's also a dropdown list where a user can choose from what country he is.
So from the client side I do something like:
Country.find().$promise.then(function(countryData) {
$scope.countries = countryData;
});
Player.find().$promise.then(function(playerData) {
$scope.player = playerData;
}
But what if I get more lists that I want to fill? Like, city, state, colors etc.
Then I'd have to make a lot of separate calls.
Is there a way to include all this data in one call, eventhough they have no relation? Something like this:
Player.find({ filter: { include: ["countries", "colors"] } }).$promise.then(function(data) {
// some stuff
}
You may want to try using the Where filter as documented here
An example of this for querying two specific things would be:
Post.find({where: {and: [{title: 'My Post'}, {content: 'Hello'}]}},
function (err, posts) {
...
});
You could create a remote method on one of your models that makes the calls internally and packages them back up for you.
Use some promise library if not using ES6 to wait for all and then return
Model.getAll = function(next) {
var promises = [];
promises.push(Model.app.models.Country.find());
promises.push(Model.app.models.Player.find());
promises.push(Model.app.models.Color.find());
Promise.all(promises)
.then(function(results) {
next(results);
});
}
/**
Register your remote method here
*/
You could create a remote method on one of your models that makes the calls internally and packages them back up for you.
Use some promise library if not using ES6 to wait for all and then return
Model.getAll = function(next) {
var promises = [];
promises.push(Model.app.models.Country.find());
promises.push(Model.app.models.Player.find());
promises.push(Model.app.models.Color.find());
Promise.all(promises)
.then(function(results) {
next(results);
});
}
/**
Register your remote method here
*/
I have problem and try with this solution but i get error "Failed with multiple errors, see details for more information.". It seems like there is bug on Loopback while using promise.all

Reusable Scala code for all views in Play

I know I can declare a reusable pure Scala block like this in a template:
#title(text: String) = #{
text.split(' ').map(_.capitalize).mkString(" ")
}
I can now call #title("someString") in the template but this code block is not accessible from outside this template.
How can I declare such a block that is accessible from other templates as well?
I've tried to create a new template title.scala.html like this:
#(text : String)
#{
text.split(' ').map(_.capitalize).mkString(" ")
}
I can now call #title("someString") from any template I want, but this doesn't give me the exact same result as the first block, inside the template (I assume in the first case it returns a String whereas it returns Html in the second case).
I'm using Play framework 2.0.4 and I'm coding in Java (hence my limited Scala knowledge).
Using tags is targeted for building reusable blocks of HTML code, therefore it returns Html
To work easily with common types of data you can easily add a custom Java class (for an example in freshly created utils package (in app directory), and prepare in it all required formatters as a static methods:
utils.MyFormats.java:
package utils;
import org.apache.commons.lang3.text.WordUtils;
public class MyFormats {
public static String capitalize(String str) {
return WordUtils.capitalize(str);
}
public static int sumElements(int a, int b) {
return a + b;
}
}
In template:
<h2>Capitalized each word: #(utils.MyFormats.capitalize("foo bar"))</h2>
<h3>Sum of two integers, 2+3 = #(utils.MyFormats.sumElements(2, 3))</h3>

PlayFramework 2.0 - Not able to call functions from other templates

I want to place some helper functions in another file, since they will be overly reused. I took the Computer-Databse sample's listing file:
https://github.com/playframework/Play20/blob/master/samples/scala/computer-database/app/views/list.scala.html
I created a new file, called "listing.scala.html" under the app/views package, and moved the #link function from the original file to it. This new file looks like this:
#(currentSortBy: String, currentOrder: String, currentFilter: String)
#****************************************
* Helper generating navigation links *
****************************************#
#link(newPage:Int, newSortBy:String) = #{
var sortBy = currentSortBy
var order = currentOrder
if(newSortBy != null) {
sortBy = newSortBy
if(currentSortBy == newSortBy) {
if(currentOrder == "asc") {
order = "desc"
} else {
order = "asc"
}
} else {
order = "asc"
}
}
// Generate the link
routes.Application.listPerfil(newPage, sortBy, order, currentFilter)
}
So, on my original file, I replaced the #link call, with this one:
#title
And the problem is, when I try to compile I get this error:
value link is not a member of play.api.templates.Html
But according to the documentation (http://www.playframework.org/documentation/2.0.4/ScalaTemplateUseCases) it seems to be ok.
Any guess?
Play's templates aren't the best place for placing advanced conditions, most probably you'll get better flexibility by processing it in some controller (or other method) which will return you only required link
ie.:
#title
In your case proposed link(...) function of Application controller can also return a reverse-route.
Keep in mind that including other templates is best option for repeating blocks of HTML but sometimes it's hard to get specified string (mainly because of not trimmed spaces). As you can see there is also problem with calling nested functions. Most probably you can generate whole A tag in the listing.scala.html however using it isn't comfortable enough (IMHO).

How to generate media item link with id instead of path in Sitecore

Anyone knows how to generate links in sitecore with ID instead of item path?
If you use GetMediaUrl method from the API, I can get this URL:
/~/media/Images/Archive/content/News and Events/News_and_Events_Level2/20070419162739/iwhiz3.jpg
The problem with this approach is that if someone changes the media item name, removes it somewhere or deletes it, the above link will break.
I notice if I insert a media link from rich text editor, I get the link as below:
/~/media/14BDED00E4D64DFD8F74019AED4D74EB.ashx
The second link is better because it's using the item id, so if the actual media item is renamed, removed, or deleted, all related links will be updated too. On top of that, when Sitecore renders the page, it will actually convert the above link and display the item path so it's readable.
I'm using Sitecore 6.5 and currently doing content migration so I need to make sure all internal links are updated properly.
May I know if there is a method to generate the second link by using sitecore API?
Thanks!
The GetMediaItemUrl extension method seems to give you what you want.
public static class ItemExtensions
{
public static string GetMediaItemUrl(this Item item)
{
var mediaUrlOptions = new MediaUrlOptions() { UseItemPath = false, AbsolutePath = true };
return Sitecore.Resources.Media.MediaManager.GetMediaUrl(item, mediaUrlOptions);
}
}
[TestFixture]
public class when_using_items_extensions
{
[Test]
public void a_url_based_on_media_item_id_can_be_generated()
{
// Arrange
Database db = global::Sitecore.Configuration.Factory.GetDatabase("master");
Item item = db.GetItem("/sitecore/media library/Images/MyImage");
// Act
var mediaUrl = item.GetMediaItemUrl();
// Assert
Assert.That(mediaUrl, Is.EqualTo("/~/media/17A1341ABEEC46788F2159843DCEAB03.ashx"));
}
}
These are called dynamic links and you can normally generate them using the LinkManager e.g:
Sitecore.Links.LinkManager.GetDynamicUrl(item)
.. but I'm not sure of the method to do this with Media links (there probably is one but I cant seem to find it and its not on MediaManager) but the basic syntax is:
"/~/media/" + item.ID.ToShortID() + ".ashx"
If you always want to use ID's instead of paths, you can change this setting in webconfig to false (like this):
<setting name="Media.UseItemPaths" value="false"/>`
Here is what the webconfig describes about it:
MEDIA - USE ITEM PATHS FOR URLS
This setting controls if item paths are used for constructing media URLs.
If false, short ids will be used.
Default value: true
Then you can use the default implementation (without additional parameters):
Sitecore.Resources.Media.MediaManager.GetMediaUrl(item);
This is what I use:
var imgField = ((Sitecore.Data.Fields.ImageField)currentItem.Fields["Icon"]);
MediaUrlOptions opt = new MediaUrlOptions();
opt.AlwaysIncludeServerUrl = true;
// Absolute Path works as well. So either use AbsolutePath or AlwaysIncludeServerUrl
opt.AbsolutePath = true;
string mediaUrl = MediaManager.GetMediaUrl(imgField.MediaItem, opt);