How would I use button in the following situation in grails? - grails

I am returning a map value that contain multiple items. The map is as follows:
def myList = []
//Some code to obtain the List
List << ["Id": Id,"Name": Name,"Code": sCode, "RunType": x ]
return [items: List]
In my GSP page I am using the following code to print them out in my web page.
<%
def counter = 0
for (i in items) {
counter = counter + 1
println("<td>" + i + "</td>" + "\n")
if (counter == 1) {
println("</tr><tr>")
counter = 0
}
}
%>
And the output looks as follows:
[Id:i-d0f5, Name:es_test_1b_110.test.com, Code:16, RunType:On Demand]
[Id:i-7890, Name:namc-qc.test.com, Code:16, RunType:On Demand]
[Id:i-ee56, Name:abcdef.test.com, Code:16, RunType:On Demand]
[Id:i-c41e, Name:backup.grails.test.com, Code:80, RunType:On Demand]
What I need now is to add a button for each of the values printed (so if 6 servers are printed 6 buttons will show up and if 10 servers are printed 10 buttons will show up) i will be using the button to pass the Id to start and stop the server.

Grails has some powerful built-in GSP tags which come in quite handy here. For example:
<g:each in="${items}" var="i">
<tr>
<td>${i.Id}</td>
<td>${i.Name}</td>
<td>${i.Code}</td>
<td>${i.RunType}</td>
<td><a class="button" href="${createLink(controller: '<controllerName>', action: 'start', id: i.Id)}">Start</a></td>
</tr>
</g:each>
There are, of course, many other ways to approach this, but I hope this is a start. For more, see these sections in the Grails ref doc: each, createLink

Related

Struts + Back Button

My MVC is Struts 2
My requirement is to clear some error message in the leading page AFTER the back button is clicked in the browser.
The sequence is:
Page A --> Page B --> Page C
The Page B can be navigated from Page A or from Page C (by clicking the back button). If Page B has an error message, and back button from Page C is clicked, this error message has to be removed. But I have no way of determining how the user navigated to Page B (considering there are 2 ways as mentioned above).
Searched everywhere for a solution. Found out that window.history is not a reliable way to parse the previous page.
Also, the window.location is not changing because of the Struts 2 quirks with tiles.
The frustrating thing is browser back button does not hit the server. Neither does it process JSP or struts tag. Luckily, though, it runs the javascript.
So I came up with a custom solution that uses server and client timestamps. It seems to work in all browsers.
I would like to know if there is an easier way.
<%
if (session.getAttribute("myTime") != null) {
//server sends the time in millis on the first invocation
Long tmStamp = Long.parseLong((String) session.getAttribute("myTime"));
// out.println(tmStamp);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date javaDt = new Date();
javaDt.setTime(tmStamp);
// out.println(" " + sdf.format(javaDt));
Date clientDt = new Date();
Long clientMillis = clientDt.getTime();
Long timeDiff = clientMillis -tmStamp;
%>
<script>
var currDt = new Date().getTime();
var dt = new Date(<%= tmStamp%>);
dt.setTime(<%=tmStamp%>);
var jsDt = new Date();
jsDt.setTime(currDt);
//alert( "<%= tmStamp %>" + " javascript time=" + (currDt - dt.getTime()) + ' timeDiff=' + (1 * <%= timeDiff%>) + " " + jsDt + " " + dt);
//we expect the server to repond within 5 seconds
if (currDt > (<%= tmStamp %> + (1*<%= timeDiff %>) + 5*1000)) {
//alert("referer is summary" )
clearError();
} else {
//alert("referer is not summary");
<s:if test="flag == 'none'">
$('<tr><td align="center"> <font color="red" style="font-size:14px;font-family:"sans-serif, Arial, Helvetica";height:20px;width:180px; font-weight:bold;" ><blink>No Result found for the given Search Criteria</blink></font></td></tr>')
.insertAfter("#lastRow");
$('<tr><td align="center"> <font style="font-size:14px;font-family:"sans-serif, Arial, Helvetica";height:20px;width:180px; font-weight:bold;" ><blink>No Result found for the given Search Criteria</blink></font></td></tr>')
.insertBefore("#firstRow");
</s:if>
}
</script>
<% } else {
out.println("No session attribute for myTime");
}
%>

onFullSync action, show updated info - DHTMLX Grid + RoR

I have a Ruby on Rails project where I use a DHTMLX Grid.
Is there a way of showing, using the event handler "onFullSync" provided by the grid API, to show updated data?
Let me explain a little better... I know I can do something like:
dp.attachEvent("onFullSync", function(){
alert("update complete");
})
But what I want is something more complex. I want to, after each completed update, alter a div adding the information like this:
Field 2 was updated to XYZ and field 3 was updated to XER on line X
Field 1 was updated to 123 and field 3 was updated to XSD on line Y
Is this possible?
Thanks
There is a onAfterUpdate event that can be used similar to onFullSync
http://docs.dhtmlx.com/api__dataprocessor_onafterupdate_event.html
It will fire after each data saving operation ( if you are saving 5 rows - it will fire 5 times )
Still, info about updated columns will not be available here.
Also, you can try onEditCell event of grid. It fires after changing data in db, but before real saving in database. Here you can get all necessary info - row, column, old value and new value.
http://docs.dhtmlx.com/api__link__dhtmlxtreegrid_oneditcell_event.html
So, what I end up doing was:
After creating the grid I created an array:
var samples = [];
Then, as per #Aquatic suggestion, I added to "onEditCell" event the following line:
samples[samples.length] = grid.cells(rId, 5).getValue();
This allowed me to add to the array the value present on column 5 of the row changed. Then, on "onFullSync" event I hide or show the div created on the view with the messages (I distinguish if it's on row or more changed on the grid).
//Deals with messages after update
dp.attachEvent("onFullSync", function(){
unique_samples = uniq_fast(samples.sort());
if (unique_samples.length == 1){
$('#updated-samples').text("");
$(".messages").show();
$('#updated-samples').text("A seguinte amostra foi actualizada: " + unique_samples[0]);
//to clear the array
samples = [];
} else if (unique_samples.length > 1){
$('#updated-samples').text("");
$(".messages").show();
$('#updated-samples').text("As seguintes amostras foram actualizadas: " + unique_samples.sort().join(", "));
//to clear the array
samples = [];
} else {
$('#updated-samples').text("");
$(".messages").hide();
//to clear the array
samples = [];
}
})
The problem with using "onEditCell" is that everytime a field is changed on that row I get a repeated value on my "samples" array, I I had to remove duplicate from that array. For that I used one of the suggestions at this answer
// remove duplicates in array
function uniq_fast(a) {
var seen = {};
var out = [];
var len = a.length;
var j = 0;
for(var i = 0; i < len; i++) {
var item = a[i];
if(seen[item] !== 1) {
seen[item] = 1;
out[j++] = item;
}
}
return out;
}
Then I have on the beginning of the view, to show the messages:
<div class="alert alert-success messages" id="updated-samples">
And that's it. I could be not the most efficient way but it works for what I wanted. I will leave the question open a few more days to see if any other option appears.

Angular pagination not updating when bound list changes due to filtering on an input text box

Here's the scenario:
I am using an ASP.NET MVC site with Angular JS and Boostrap UI. I have a dynamic ul list populated by data fed through a controller call to AngularJS, filtering on that list through an input search box. The list is also controlled through pagination (UI Bootstrap control) that I've setup to show 10 results per page for the list of 100 or so items. This list is filtered as the user types in the search box, however I would like the pagination to update as well so consider the following example:
The list has 10 pages of items (100 items), the user types some text in the input search box which filters the list down to 20 or so items, so the pagination should be updated from 10 pages to two pages.
I figure there must be a $watch setup somewhere, perhaps on the list items after it has been filtered and then update the pagination page count, however I'm pretty new to AngularJS so can someone please explain to me how this could be done?
Thanks very much. I have posted my code below:
<div data-ng-app="dealsPage">
<input type="text" data-ng-model="cityName" />
<div data-ng-controller="DestinationController">
<ul>
<li data-ng-repeat="deals in destinations | filter: cityName |
startFrom:currentPage*pageSize | limitTo:pageSize">{{deals.Location}}</li>
</ul>
<br />
<pagination rotate="true" num-pages="noOfPages" current-page="currentPage"
max-size="maxSize" class="pagination-small" boundary-links="true"></pagination>
</div>
var destApp = angular.module('dealsPage', ['ui.bootstrap', 'uiSlider']);
destApp.controller('DestinationController', function ($scope, $http) {
$scope.destinations = {};
$scope.currentPage = 1;
$scope.pageSize = 10;
$http.get('/Deals/GetDeals').success(function (data) {
$scope.destinations = data;
$scope.noOfPages = data.length / 10;
$scope.maxSize = 5;
});
});
destApp.filter('startFrom', function () {
return function (input, start) {
start = +start; //parse to int
return input.slice(start);
};
});
Because your pagination is a combination of chained filters, Angular has no idea that when cityName changes, it should reset currentPage to 1. You'll need to handle that yourself with your own $watch.
You'll also want to adjust your startFrom filter to say (currentPage - 1) * pageSize, otherwise, you always start at page 2.
Once you get that going, you'll notice that your pagination is not accurate, because it's still based on destination.length, and not the filtered sub-set of destinations. For that, you're going to need to move your filtering logic from your view to your controller like so:
http://jsfiddle.net/jNYfd/
HTML
<div data-ng-app="dealsPage">
<input type="text" data-ng-model="cityName" />
<div data-ng-controller="DestinationController">
<ul>
<li data-ng-repeat="deals in filteredDestinations | startFrom:(currentPage - 1)*pageSize | limitTo:pageSize">{{deals.Location}}</li>
</ul>
<br />
<pagination rotate="true" num-pages="noOfPages" current-page="currentPage" max-size="maxSize" class="pagination-small" boundary-links="true"></pagination>
</div>
JavaScript
var destApp = angular.module('dealsPage', ['ui.bootstrap']);
destApp.controller('DestinationController', function ($scope, $http, $filter) {
$scope.destinations = [];
$scope.filteredDestinations = [];
for (var i = 0; i < 1000; i += 1) {
$scope.destinations.push({
Location: 'city ' + (i + 1)
});
}
$scope.pageSize = 10;
$scope.maxSize = 5;
$scope.$watch('cityName', function (newCityName) {
$scope.currentPage = 1;
$scope.filteredDestinations = $filter('filter')($scope.destinations, $scope.cityName);
$scope.noOfPages = $scope.filteredDestinations.length / 10;
});
});
destApp.filter('startFrom', function () {
return function (input, start) {
start = +start; //parse to int
return input.slice(start);
};
});
The version shared on jsfiddle is compatible with ui-bootstrap 0.5.0 but from 0.6.0 onwards there have been breaking changes.
Here is a version that uses the following libraries:
angular 1.2.11
angular-ui-bootstrap 0.10.0
bootstrap 3.1.0
Here is a plunker for the same:
Angular UI Bootstrap Pagination
Hello I tried to hook this up with Firebase using Angular Fire and it only works after I type something in the search input. In the $scope.$watch method, I used Angular Fire's orderByPriorityFilter to convert the object to an array.
$scope.$watch('search', function(oldTerm, newTerm) {
$scope.page = 1;
// Use orderByPriorityFilter to convert Firebase Object into Array
$scope.filtered = filterFilter(orderByPriorityFilter($scope.contacts), $scope.search);
$scope.lastSearch.search = oldTerm;
$scope.contactsCount = $scope.filtered.length;
});
Initial load doesn't load any contacts. It's only after I start typing in the input search field.

ASP.NET MVC Razor extra whitespace rendered

In Asp.net MVC, Razor inserts extra space between text blocks. I want to render a list this way: "1, 2, 3" but get "1 , 2 , 3".
#for (int i = 1; i < 3; i++)
{
<text>#i</text>
if (i != 2)
{
<text>, </text>
}
}
Is there any ways to remove extra whitespace ?
I want to render a list this way: "1, 2, 3"
Quick and dirty:
#string.Join(", ", Enumerable.Range(1, 3))
Obviously a custom helper seems more appropriate to the job of formatting something in the view:
public static class HtmlExtensions
{
public static IHtmlString FormatList(this HtmlHelper html, IEnumerable<int> list)
{
return MvcHtmlString.Create(string.Join(", ", list));
}
}
and then simply:
#Html.FormatList(Model.MyList)
You are seeing the extra whitespace between the number and the comma because your razor template includes a line break (which displays as whitespace in the browser) between the number and the comma:
#for (int i = 1; i < 3; i++)
{
<text>#i</text> >LINE BREAK HERE<
if (i != 2)
{
<text>, </text>
}
}
I like Darin's answer, and I would suggest removing your loop and replacing it with a more declarative statement, but if you don't want to go that far, try at least removing the line break:
#for (int i = 1; i < 3; i++)
{
<text>#i</text>if (i != 2){<text>, </text>}
}
Instead of writing out bits of text in different places each time round the loop, you could accumulate all the text in a StringBuilder, then outside the loop do #stringBuilderObject.ToString().
The problem is with the source that is generated. When you look at the actual source you get:
1
,
2
,
3
As you can see there is a lot of white space in there which browsers collapse down to a single space (look at the definition for normal).
Using a StringBuilder or string.Join is the way to fix this if all you're doing is outputting these three numbers. But if you're trying to do something else and this is a simplified example then see this blog post for a way of doing it using ol/ul and lis.
I might assume that it is not issue of Razor, but rather element is rendered with some margings.
Open FireBug (or Chrome or whatever) and see that it is really markup issue.
In you css file try to add
text { margin: 0 }

ActionLink only if a certain condition is met

If s = "N/A" then I don't want to use the ActionLink. In other words, if the inventory item is not currently being used on a project, then don't provide the link (just show N/A instead). Also, how do I send the link to Projects/Details? Right now, it will go to "Nails/Projects/Details" instead, because I'm using the NailsController class.
<td class="table-normal-data">
<% Dim l As Integer = InStr(item.CurrentProject, " [")
Dim s As String = item.CurrentProject
Dim projectID As String = ""
If l > 0 Then
s = Mid(item.CurrentProject, 1, l - 1)
projectID = Mid(item.CurrentProject, l + 2, Len(item.CurrentProject) - l - 2)
Else
s = ""
End If
%>
<%: Html.ActionLink(s, "Projects/Details", New With {.id = projectID}) %>
</td>
I'm much more familiar with MVC3/Razor and C#, but I often do something like this in my views:
#if( Model.Flag )
{
<span>n/a</span>
}
else
{
#Html.ActionLink(....
}
Basically, you output different stuff through the view depending on the state of the model.

Resources