SilverStripe 3: Alternate value in template within a loop - silverstripe

I am trying to create a page that displays the latest images from the child pages of the holder.
Each row will alternate between the example below:
Large Image | Small Image
Small Image | Large Image
Large Image | Small Image
and so on....
template.ss
<div class="row">
<div class="span8">
LARGE IMAGE
</div>
<div class="span4">
SMALL IMAGE
</div>
</div>
<div class="row">
<div class="span4">
Small Image
</div>
<div class="span8">
Large IMage
</div>
</div>
</div>
<div class="row">
<div class="span8">
Large Image
</div>
<div class="span4">
Small Image
</div>
</div>
How can I process that in the template file?
I've tried to write a custom function to process the latest images within the Holder Controller
controller.php
$works = WorkPage::get();
This only returns the image id, I;ve tried a left join but it doesn't return the file path.
$works = WorkPage::get()->leftJoin("File", "\"File\".\"ID\" = \"WorkPage\".\"FeaturedImageID\"");

File::get()->
leftJoin("SiteTree", "\"SiteTree\".\"ParentID\" = ".$this->ID)->
leftJoin("WorkPage", "\"WorkPage\".\"ID\" = \"SiteTree\".\"ID\"")->
where("\"File\".\"ID\" = \"WorkPage\".\"FeaturedImageID\"");
might be the sql query you are after (untested though)

This is how I did, not sure if it's the best way but it works.
$works = WorkPage::get();
foreach ($works as $work) {
//Build the IMage Object so we can add it to the Work Object
$ImageObj = File::get()->byID($work->FeaturedImageID);
$Image->ID = $ImageObj->ID;
$Image->Title = $ImageObj->Title;
$Image->Name = $ImageObj->Name;
$Image->Filename = $ImageObj->Filename;
$work->ImageObj = $Image;
$ImagePath = $work->ImageObj->Filename;
}

Your question isn't 100% clear. I'm not sure if you're having trouble with the template looping and conditionals or with getting the image objects from WorkPage so I'll attempt to answer both.
To create the alternating layout, the easiest way is to use a conditional based on whether the loop count is odd or even. A quick untested example:
<% loop $Works %>
<div class="row">
<% if $Odd %>
<div class="span8">LARGE IMAGE</div>
<div class="span4">SMALL IMAGE</div>
<% else %>
<div class="span4">SMALL IMAGE</div>
<div class="span8">LARGE IMAGE</div>
<% end_if %>
</div>
<% end_loop %>
Documentation reference is at http://docs.silverstripe.org/framework/en/reference/templates#position-indicators
To get different sized images within the loop you can simply use $FeaturedImage->CroppedImage(xxx,xxx). This assumes you have on 'Work' per row and each work has two images, but as I said the question isn't that clear so if my assumptions are incorrect you will need to provide more info about your model and what you're trying to achieve.

Just to comment on your join you tested:
this wont work:
$works = WorkPage::get()->leftJoin("File", "\"File\".\"ID\" = \"WorkPage\".\"FeaturedImageID\"");
That join doesn't get the data of the joined table; in essence you are requestion workpage objects which dont have that as the data. If you had done the join the otherway around you would be able to get the info you were after
Anyways as Columba allready mentioned you can get the relations correctly on by calling the field as "a function" has one and has many, example $this->hasmanyrelation() < returns the datalist (was that coorect term for ss3 :) ). When using $hasmanyrelation relation on the tempate it just magically gets the collection.
Also you should use the Link() to get the path tho the file in my oppinnion as that works for sitetree objects as well.

Related

Make variable amount of thumbnails in bootstrap same height using content from database

Screenshot of Page I am trying to recreate a pc part picker type of application using bootstrap and express. When trying to display all the cases available on "my" website, I am loading all of the cases from my database in an ejs file and then displaying them in a thumbnail with four in each row. I have used flexboxes on every four elements to make the heights for all thumbnails in one row the same, however it only works on the first row and does not work after. I have tried to add the code underneath, however this is my first time asking a question so if you have any suggestions to make it more readable please do let me know. I have also included the link to the picture of the page at the beginning of the paragraph.
<% var start = 0 %>
<% cases.forEach(function(qcase) { %>
<% if (start % 4 == 0) { %>
<div class="row text-center display-flex">
<% }; %>
<div class="col-md-3 col-md-6">
<div class="thumbnail">
<img src="<%=qcase.image%>">
<div class="caption">
<h4><%=qcase.name%></h4>
</div>
<div>
More Information
</div>
</div>
</div>
<% if (start % 4 == 3) { %>
</div>
<% }; %>
<% start += 1 %>
<% }); %>

How to display subsites of a page within grid columns of the grid layout on GetGridHtml() using Umbraco

I come across a issue on umbraco, suggest me how I can bind the subsites of a page within a grid layout of what I set as DataType. kindly do reply how to do it.
note:
I don't want to use strongly-typed model to implement grid layout.
#CurrentPage.GetGridHtml(Umbraco.AssignedContentItem,"postTiles",
"bootstrap3")
#CurrentPage.GetGridHtml(#Umbraco.RenderMacro("ArticlesList"),"postTiles","bootstrap3")
//ArticlesList
#inherits Umbraco.Web.Macros.PartialViewMacroPage
#{ var selection = CurrentPage.Children.Where("Visible").OrderBy("CreateDate desc"); }
<section class="container">
<div class="row">
#foreach (var item in selection)
{
<div class="col-md-3">
<img src="#Umbraco.Media(item.ArticleFeaturedImage).Url" style="width: 200px; height: 120px;" />
<h3>
#item.Name
</h3>
<p>
— posted under #item.ArticleCategory
· #String.Format("{0:dddd, MMMM d, yyyy}", #item.CreateDate)
· by #item.WriterName.ToString().ToLower()
</p>
<p>#Umbraco.Truncate(#item.ArticleDescription,100)</p>
</div>
}
</div>
</section>
If you want to have a custom rendering of some content in the Grid, you need to either:
Use a Macro and have content editors pick the Macro in the Grid
Create a custom property editor and specific the rendering partial in the package.manifest
Use a grid editor tool such as LeBlender or DocType Grid Editor to create a custom editor and assign a custom partial for rendering
I'm not sure exactly if that's what you're looking for, but I think these may get you on the right track.

Using ajax to load a certain number of items from a DataObject each time a button is clicked?

I have a See more button which when clicked I want to be able to load another 3 Past Events onto the default 3 past events that show on page load, and then if clicked again, add another 3 etc.
The code below works but is multiplying the number for some reason e.g if I press the "See more" button as the code is now, it returns 18 past events when it should only return 6 past events...
If I dd($amountOfCurrentPastEvents + 3) I get int(6) returned 3 times, 6 x 3 = 18. e.g:
Question is why is it getting called three times? What am I doing wrong here?
DashboardPage.php
class DashboardPage_Controller extends Page_Controller {
private static $allowed_actions = array(
'pastEvent',
'seeMorePastEvents',
'pasteventfilter',
'LimitedPastEvents'
);
public function LimitedPastEvents()
{
return PastEvent::get()->limit(3);
}
public function seeMorePastEvents() {
if (Director::is_ajax()) {
// Gets the amount of past events that are display on the page at present
$amountOfCurrentPastEvents = $_POST['events'];
// Adds 3 onto how ever many past events are currently showing
$PastEvents = PastEvent::get()->limit($amountOfCurrentPastEvents + 3);
return $this->customise(array(
'Results' => $PastEvents
))->renderWith('AjaxPastEvents');
}
}
...
}
DashboardPage.ss
<% loop LimitedPastEvents %>
<div class="past-event-results">
<div class="col-md-4 no-padding past-event">
<a href="$Link">
<div class="live-workouts-wrapper">
<div class="on-demand-image" style="background-image: url($ThemeDir/images/vid-3.jpg);">
<div class="play-icon"></div>
</div>
<div class="on-demand-info text-center">
<div class="on-demand-location">
<span class="pin-icon"></span><span>$BranchLocation.Name, $BranchLocation.City</span>
</div>
<div class="on-demand-date-time">
<span class="time-icon"></span>
<span>{$EventDate.Day} {$EventDate.ShortMonth} {$EventDate.Format(dS)} $Time.Nice</span>
</div>
<div>
<strong>$EventName</strong>
</div>
</div>
</div>
</a>
</div>
</div>
<% end_loop %>
<div class="col-md-12">
<div class="text-center">
<button class="btn btn-seemore">SEE MORE<b class="btn-icon btn-down"></b></button>
</div>
</div>
jQuery - Located on DashboardPage.ss
<script>
$(document).ready(function() {
var pastEventCount;
$('.btn-seemore').on('click', function(event) {
event.preventDefault();
// Gets the amount of past events that are currently displayed on the page
pastEventCount = $('.past-event').length;
console.log(pastEventCount);
$.post('/dashboard/seeMorePastEvents', {events: pastEventCount}, function(data) {
$('.past-event-results').html(data);
});
});
});
</script>
The problem is the code adds the new content to .past-event-results with $('.past-event-results').html(data);, but the template has 3 .past-event-results divs to begin with.
The data returned by the ajax function (6 PastEvent entries) is added to 3 divs, making the results show 18 items.
The following loop creates 3 past-event-results divs to begin with:
<% loop LimitedPastEvents %>
<div class="past-event-results">
...
</div>
<% end_loop %>
You might want to take this div outside of the loop:
<div class="past-event-results">
<% loop LimitedPastEvents %>
...
<% end_loop %>
</div>
This will mean there is only ever one past-event-results div.
I would also recommend changing the seeMorePastEvents() function to only return the new items, rather than all the existing items plus the new items each time. This will save on data that needs to be passed back and slightly improve the server fetch.
To make this change we first need to change the limit function to only fetch 3 items and start the fetch from the $amountOfCurrentPastEvents point.
$PastEvents = PastEvent::get()->limit(3, $amountOfCurrentPastEvents);
The DataList limit() function can take 2 parameters:
limit(integer $limit, integer $offset = 0)
We use offset to change the fetch start point.
We then change our JavaScript to append the retrieved data rather than overwrite the existing events:
$('.past-event-results').append(data);
At first glance it looks like a JS issue.
You might have x3 AJAX requests being sent - if you break out your browser's debugger, how many XHR requests are being sent to SilverStripe?
Also check the generated markup. I suspect your JS logic should be appending to the .past-event-results DOM node itself, and not appending to its contents which is essentially what the jQuery html method is doing for you.

Add CSS to ScalaHelpers

How do i add some CSS to the Scala Helpers, and is it possible to remove the "Required" and "Numeric" text under the textfield?
#inputText(advForm("weeknr"))
#inputText(advForm("jaar"))
#inputText(advForm("datum"))
--------------------EDIT 1------------------
When I add my own CSS, im not getting the error warnings that i used to get when I try to upload an empty form, the text used to turn red. This is the code I changed
MyPlainFieldConstructor.scala.html(only 2 lines of code):
#(elements: helper.FieldElements)
#elements.input
advPlaatsen2.scala.html:
Added this line of code
#implicitField = #{ FieldConstructor(myPlainFieldConstructor.f) }
and this is how i placed the CSS(Foundation 5):
<div class="row collapse">
<div class="small-2 columns">
<span class="prefix">Email</span>
</div>
<div class="small-4 left columns">
#inputText(advForm("email"),
'id -> "right-label",
'placeholder -> "")
</div>
</div>
This way the forms looks how I want it to look but it doesnt show me errors and it doesnt even upload my files
but when i remove this line of code:(which is above the #import helper._)
#implicitField = #{ FieldConstructor(myPlainFieldConstructor.f) }
the form works as it should but looks really bad:
To customize the html and styles of a field you can write your own field constructor. Take a look to play docs here.

Rename multiple css with identical name

I'm working inside a templated system where i can implement code, but i can't modified the core of the file. My layer are stacked like this:
<div class="layer1">
<div class="layer2">
<div class=" layer3">
<div class="layer4">
</div>
</div>
</div>
</div>
<div class="layer1">
<div class="layer2">
<div class=" layer3">
<div class="layer4">
</div>
</div>
</div>
</div>
<div class="layer1">
<div class="layer2">
<div class=" layer3">
<div class="layer4">
</div>
</div>
</div>
</div>
As you can see, my class all have the same name (layer1, layer2, etc...). I want to know if there's a way by using Javascript, Jquery or any other online client side library to modify the CSS class name so, for example, the first layer1 become level1 and the following layer1 become level 2?
Thank for your answer!
As other people already said, jQuery actually does what you want.
As long as you don't know the number of “layers” you have, you better find all elements by classname substring:
$('*[class^="layer"]')
Then you can get the list of the element classes and change old names to new ones.
Many different ways to do this:
Solution 1:
Use addClass() and removeClass()
$(".layer1").removeClass('old_class').addClass('new_class');
Replace old_class with your older class and new_class with your new class
Solution 2:
If you are able to get the element by ID
You can set the class by using .attr()
$("#id").attr('class', 'new_class');
an all around solution working with className :
var elem=document.querySelectorAll('[class^="layer"]') ;
for(i in elem){
x = elem[i].className;
var y=x.replace("layer" , "level");
elem[i].className=y||x;
}

Resources