How can I trigger Template#onRendered when Blaze reuses a template? - jquery-ui

How can I trigger Template#onRendered, or equivalent, when Blaze reuses a template?
I have a jQuery UI selectmenu widget based on a <select> element whose <option> children are created using {{#each}} (targeting a helper which draws on Session in order to get reactivity) and a Blaze template. I'm refreshing the selectmenu onRendered.
The Blaze template is correct. ( <option>{{text}}</option> )
Session is getting changed (checked with both console.log() and manually setting it in the browser console.
Session reactivity, the helper, and {{#each}} are working (inspecting the hidden <select> reveals that its options have been correctly updated)
It's just that templates are getting reused, so although everything works fine as long as the Session options context changes to have more options (onRendered() gets called after the new template instance is created), the jQuery UI selectmenu is inaccurate (not up to date with the current state of the context) if the Session options context changes to have the same or fewer options (fewer options only calls onDestroyed() and same number of options doesn't even do that).

Solution: Tracker.afterFlush(callback) used inside the helper (which is getting called every time I expected) instead of Tempate.on_[anything]_().
https://docs.meteor.com/api/tracker.html#Tracker-afterFlush
e.g.
myContextOptionsHelper: function () {
let context = Session.get('myContext');
let contextOptions = app.client.tempdata.contextOptions[context];
Tracker.afterFlush(() => {
if ($('#mySelectmenu').is(':ui-selectmenu')) {
$('#mySelectmenu').selectmenu('refresh');
}
});
return contextOptions;
}

Related

Passing edited value contenteditable field from view to controller in Rails

I am using Rails 3.1 to build a web form which, among other fields, contains a table whose cells can be edited. I require to save the text of edited cell in a database.
I am thinking of using HTML5 attribute 'contenteditable'. I can use the value of innerHTML of the cell . How can I pass this value to Rails controller (from the view)? Is it possible to use params to pass the data? Any suggestions?
HTML containers marked as contenteditable won't automatically be passed along to the server with a form submission, so you'll likely have to use javascript to pull the content out of those containers and pass them along in some fashion.
One strategy would be to add a handler to the form that fires before submit, which iterates over contenteditable containers and injects hidden form elements. The below example uses jQuery, but you could probably replicate the thought process without.
var form = $('#my_form');
var element;
$('[contenteditable=true]').each(function(){
element = $(this);
form.append($('<input/>', {
type:'hidden',
name:element.attr('id'),
value:element.html()
}));
});

Context parameter not working in Tapestry 5.3.2 for Submit

I have a loop displaying records, and I want to add submit buttons to all the rows. The reason they need to be submit is because there is a form at the bottom I would like to have persisted when the user selected one of the buttons.
I've seen the comments about using defer, etc, but nothing seems to work for me. The current submit code I'm trying is:
<input t:id="deleteItem" t:type="submit" t:context="item.number.content" value="deleteItem" class="deleteItem" />
To expand on the context:
The current context I have listed, is just a string within the number object within the item object. In fact, it's being displayed in the code above perfectly fine.
To test this differently, I've replace item.number.content with a getContext() method and had it return a hard-coded 1. I then debug this method and see it being called when the page is SUBMITTED, not when the page is rendered as I would have expected.
The context is never populated until after the button is pushed. Am I misunderstanding something??
Edit:
So my issue is with getting the context value. Take for instance my code:
<t:loop source="itemsList" value="item" formState="none">
<!-- Display info here -->
<input t:id="deleteItem" t:type="submit" t:context="myContext" value="deleteItem" class="deleteItem" />
</t:loop>
The definition for getMyContext is:
public String getMyContext() {
String context = item.getNumber().getContent();
return context;
}
The problem is, the method is not called until after the submit has been pressed at which time the variable "item" is null. I was expecting getMyContext to be called each time the submit button is rendered, and then when the submit is selected, the event would be triggered with the appropriate context. Does that make sense?
I finally figured out what my problem was:
formState="none"
I originally did this because it was having trouble translating between my Objects and a String. When this is selected, the submit buttons don't work properly. I ended up changing it to formState="iteration" and now it is working exactly as I expected it to (and I needed the defer as expected too).
Try adding the following to the page/component you add this t:submit to:
#OnEvent(component = "deleteItem")
private void handleSubmit(Integer contextValue) {
//Do whatever you need to do with the passed context value here.
//Most commonly you would store the context in a java page/component field to
//be used by the form eventhandler to do some sort of CRUD
}
The context value will not get written out while rendering the page, it will be passed as a parameter to your event handler while submitting the form. So what you are seeing is correct behavior.

JSF2 + IceFaces 2 - Retrieve UIComponent from ViewRoot

I've got hard time resolving the following. My problem is quite simple : I would like to highlight in red the forms fields that triggered validation errors. The error messages are placed correctly in the FacesContext using a context.addMessage(...) line.
I'd like my system to be generic. All form fields having a message attached are automatically highlighted.
I've found on this site a link to this excellent article :
http://www.jroller.com/mert/entry/how_to_find_a_uicomponent
With it, I did implement a PhaseListener for the RENDER_RESPONSE phase, which do the following :
#Override
public void beforePhase(PhaseEvent event) {
// get context
FacesContext context = event.getFacesContext();
// iterate on all the clientIds which have messages
Iterator<String> clientIdsWithMessages = context.getClientIdsWithMessages();
while (clientIdsWithMessages.hasNext()) {
// get the clientId for the field component
String clientIdWithMessage = clientIdsWithMessages.next();
// split on ":"
String[] splitted = clientIdWithMessage.split(":");
UIComponent component = findComponentInRoot(splitted[splitted.length - 1]);
if (component != null) {
Map<String, Object> attributes = component.getAttributes();
if (attributes.containsKey("style")) {
attributes.remove("style");
}
attributes.put("style", "background-color: #FFE1E1;");
}
}
}
This perform perfectly well for almost all my usage.
Now, where it becomes a bit tricky, is that some of my forms have such code :
<ice:dataTable id="revisionDocuments" value="#{agendaBean.agenda.revisionsDocuments}" var="revision">
<ice:column>
<ice:inputText value="#{revision.sequenceAdresse}" id="revisionSequenceAdresse" />
</ice:column>
....
The generated form has several lines (one for each object of the revisionsDocuments list), and each element has a unique identifier (clientId) which looks like :
contentForm:revisionDocuments:0:revisionSequenceAdresse
With 0 changed for 1, 2, ... for each iteration.
Consequently, the code provided to search the UIComponent from ViewRoot does not work properly. All forms fields have the same "id". What surprise me more is : they have the same "clientId" in FacesContext too :
contentForm:revisionDocuments:revisionSequenceAdresse
I cannot distinguish, while going through the tree, if I do see the right form field or any of the others.
Does anyone have a hint to solve this ? Or another suggestion to implement the highlight of my fields ? I have to admit, I dont really like my code, I consider dirty to manipulate the viewRoot like I'm doing, but I could not figure out a better solution to have a generic highlight of my fields.
I'm running IceFaces 2.0.2 with JSF-Impl 2.1.1-b04 on JBOss AS 7.0.2.Final.
Thank you in advance for the answers.
Best regards,
Patrick
You should apply this in the client side instead. You've got a collection of client IDs with messages. One of the ways is to pass this information to JavaScript and let it do the job. You can find an example of such a PhaseListener in this article: Set focus and highlight in JSF.
Since JSF 2.0 there is however another way without the need for a PhaseListener. There's a new implicit EL variable, #{component} which refers to the UIComponent instance of the current component. In case of UIInput components, there's an isValid() method. This allows you to do something like:
<h:inputText styleClass="#{component.valid ? '' : 'error'}" />
with this in a CSS file:
.error {
background: #ffe1e1;
}
(yes, you can also do this in a style attribute, but mingling style with markup is a poor practice)
To abstract this away (so that you don't need to repeat it in every input), you can just create a composite component for this, something like <my:input>.
For completeness, here is the solution I finally found to highlight the fields that do have error messages with IceFaces 2.0.2 :
The basic idea is strictly the same than proposed by BalusC on http://balusc.blogspot.com/2007/12/set-focus-in-jsf.html
The piece of code I had to change with IceFaces is the small Javascript call :
<script>
setHighlight('${highlight}');
</script>
I could not find any IceFaces component which is re-rendered at each JS call. I found that placing the script into a panelGroup works most of the time. However, there was a case that did not work :
submitting the form with errors do trigger the JS.
then, re-submitting the form with errors on the same field than previous validation do NOT trigger the JS.
then, re-submitting the form with any error field having no more errors do trigger JS.
then, re-submitting the form with any non-errored field having an error do trigger JS.
For some reason, IceFaces do not render the panelGroup that contains the JS when the set of fields having errors is the same between two calls.
I tried to use the Javascript API with code like Ice.onAsynchronousReceive(), using Prototype library to attach an event to the AJAX completion of the commandButton, but had not much success with it. Some of my tests could run (with errors but did the job) and I could observe similar behavior.
Here is the trick I finally used (ugly but working) :
<ice:panelGroup>
<script type="text/javascript">
var useless = '#{testBean.time}';
setHighlight('${highlight}');
</script>
</ice:panelGroup>
The getTime() function simply return the current timestamp. The value is then always different and trigger the JS execution at any AJAX request.
Sadly, IceFaces do not have the RichFaces useful "oncomplete" attribute, which I do regret highly for this case.
Ugly solution, but funny and working.

Telerik MVC Grid fails to bind after calling .ajaxRequest

I have something of a weird situation going on. I'm trying to build a Telerik MVC grid in a custom HTML helper which implements some other custom functionality. (Amongst other things, it renders a form to the right of the grid when a row is selected. We're not using the in-box editing features of the grid due to UI standardization. The whole requirement is that, for simple list-of-values tables in a database, we'd like a minimal-code approach. One line in the HTML, a few lines of Javascript at most, and boom, done.)
Everything works -- except rebinding the data dynamically. The grid renders, its selection works, the form displays, the form saves at blur events. The grid hits the OnDataBinding event, but nothing happens after that. It never gets to the OnDataBound event, and it never hits the internal bindTo nor bindData methods on the grid object itself.
"Enough" of the code (a lot of it can't be revealed) is thus (HTML helper):
public static void ListOfValuesEditorFor<TModel, TModelCollection>(this HtmlHelper<TModelCollection> htmlHelper, string gridName, string refreshAction, string refreshController, string loadItemUrl, IEnumerable<TModel> model) where TModel : class where TModelCollection : IEnumerable<TModel>
{
var factory = HtmlHelperExtension.Telerik<TModelCollection>(htmlHelper);
var grid = factory.Grid(model);
grid = grid.Name(gridName).Pageable(pager => pager.Enabled(false)).Selectable(select => select.Enabled(true)).Filterable(filter => filter.Enabled(false)).Scrollable().Sortable(sort => sort.Enabled(false));
grid = grid.DataBinding(binding => binding.Ajax().Select(refreshAction, refreshController));
grid = grid.ClientEvents(events =>
{
events.OnDataBound("Telerik.ListOfValues.OnDataBound");
events.OnDataBinding("Telerik.ListOfValues.OnDataBinding");
events.OnRowSelect("Telerik.ListOfValues.SelectRow");
});
var textControls = new List<string>();
int idColumn = -1;
grid = grid.Columns(columns =>
{
int cellCount = 0;
foreach (var prop in typeof(TModel).GetProperties())
{
// Populates columns, creates text entry controls in the list,
// handles some other proprietary work.
// SNIP
}
});
// Container for the form
var formDivBuilder = new TagBuilder("div");
// Build out the form
// SNIP
// Render to the response
var response = HttpContext.Current.Response;
response.Write("<input type=\"hidden\" id=\"loadItemUrl\" value=\"" + loadItemUrl + "\" />");
response.Write("<input type=\"hidden\" id=\"idColumnIndex\" value=\"" + idColumn.ToString() + "\" />");
grid.Render();
response.Write(formDivBuilder.ToString(TagRenderMode.Normal));
}
That HTML helper is called thusly:
<% using (Html.BeginForm()) {
Html.ListOfValuesEditorFor("JobTitleGrid", "RefreshJobTitles", "Home", "/Home/LoadJobTitle", Model);
} %>
On the Javascript side of the world, all OnDataBound and OnDataBinding do is display messages indicating that they've been hit. In fact, they won't even make it to the production version of the code; they're in there for debugging purposes now.
OnSelect displays and populates the form. This is happening correctly.
The form itself updates the object any time a text field's onChange event fires. This portion is validated as functional. This is done via a $.ajax() call, which again, is validated to function.
The success callback from that $.ajax() call is thus:
function onSubmitComplete(responseData, callbackData) {
// Some irrelevant junk here
$('#JobTitleGrid').data('tGrid').ajaxRequest();
}
The call to ajaxRequest functions. At the server, my grid action functions, returning an IList of the IJobTitle objects. At the client, OnDataBinding fires, displaying its message. OnDataBound never fires, and the grid display does not update.
I know this is somewhat outside the bounds of the way Telerik controls are normally used, but the sheer amount of code necessary to use them encourages my team to try to create reusable entities (such as these custom HTML helpers) wherever possible. For the simpler controls (text boxes, calendars, etc), our custom helpers have always "just worked." The grid, though, is presenting this problem.
Any ideas on why we never get to binding the data? More importantly, how to fix that?
After coming up with the solution, I'd briefly considered deleting the question -- Telerik's grid is only minimally involved here. However, I know first-hand how hard it is to troubleshoot code when you're building on top of frameworks, which are built on top of frameworks, which are further built on top of frameworks. :) So hopefully this answer will help the next guy down the line.
The actual issue turned out to be a serialization exception from the DAL call in the grid action. This seemed odd to me, since I used the exact same call to populate both the pre-loaded view in the Index action and the response from the GridAction, but sure enough, if I debugged down in the Javascript deeply enough, I eventually found it. The exception wasn't being handled (pro-tip: implement an OnError handler for the grid), and thus client-side rebinding failed, as it had no data to bind.
Once I resolved the serialization issue, everything just magically worked, and we were down to about 20 lines of code to implement an entire generic entity data entry screen.

Knockout and jQuery Mobile: Checkboxes

I'm trying to dynamically add checkbox and label elements to the document. Checkbox element has Knockout's data-bind attribute to bind its value to an observable value in the ViewModel. However when I try to style the checkboxes with jQuery Mobile by executing
$('input[type="checkbox"]').checkboxradio();
data-bind attributes will be removed. If I leave out the above line, data-bind attributes are properly set and the binding works.
Is there a way to have both jQuery Mobile styling and Knockout bindings at the same time?
I'm using jQuery Mobile RC1 and Knockout 1.2.1.
I have also encountered this problem. Unfortunately, all the suggestions here either did not work for me or had other issues. So I have created a simple custom binding that works in all versions of KO (including the latest v3):
ko.bindingHandlers.jqmChecked = {
init: ko.bindingHandlers.checked.init,
update: function (element, valueAccessor) {
//KO v3 and previous versions of KO handle this differently
//KO v3 does not use 'update' for 'checked' binding
if (ko.bindingHandlers.checked.update)
ko.bindingHandlers.checked.update.apply(this, arguments); //for KO < v3, delegate the call
else
ko.utils.unwrapObservable(valueAccessor()); //for KO v3, force a subscription to get further updates
if ($(element).data("mobile-checkboxradio")) //calling 'refresh' only if already enhanced by JQM
$(element).checkboxradio('refresh');
}
};
Should be used like this:
<input type="checkbox" data-bind="jqmChecked: someValue" id="checkbox1"/>
See a complete working example here:
http://jsfiddle.net/srgstm/ub6sq/
See: https://gist.github.com/1006808
Then you can do something like the following:
var $checkbox = $('input[type="checkbox"]');
$checkbox.checkboxradio();
$checkbox.dataBind({
your options..
});
Hope this'll help!
There is a problem with using knockouts default checked binding with styled objects like jQuery mobile does. It has the same issues that jQueryUi's Button/Buttonset functions. There is a label over the checkbox that indicates what is happening and it doesn't get updated properly via standard knockout checked binding.
It is discussed at http://therunningprogrammer.blogspot.com/2011/10/how-to-use-jquery-uis-button-with.html.
To use knockout directly with these styled objects from jQuery Mobile, the demonstrated code will have to be modified to handle the different DOM context. I'll post an update to the code when I can get some free time to do it.
EDIT
In Google Groups - Knockout, luv2hike posted a solution. You can see it working at http://jsfiddle.net/luv2hike/nrJBC/. Looks like a working fix for your problem.
I created a simple binding that works with jQuery Mobile 1.2.0 and Knockout 2.2.1 and works with default jQuery mobile checkboxes. This binding has no dependency on custom icons or JQuery Mobile's CSS styles. It also allows the use of regular checkbox markup in your HTML (<input type="checkbox" ... />) as opposed to using an alternate markup element like a div.
Here's the fiddle: http://jsfiddle.net/thedude458/52baX/
Note: Presently, the example only supports a single checkbox, not a list, as that is all I currently have a need for. It also assumes that the bound property is an observable.
Here is my heavily commented code on a custom handler I built for jQueryMobile checkboxes:
ko.bindingHandlers.checkbox = {
init: function(element, valueAccessor) {
// set the dom element to a checkbox and initialize it (for jquerymobile)
var checkbox = $(element);
checkbox.checkboxradio();
checkbox.attr('type', 'checkbox');
// register change event to update the model on changes to the dom
ko.utils.registerEventHandler(element, "change", function() {
valueAccessor()(
// off because it is before the ui has refreshed
$(this).siblings('label.ui-checkbox-off').length > 0
);
});
},
update: function(element, valueAccessor) {
// update the checked binding, i.e., check or uncheck the checkbox
ko.bindingHandlers.checked.update(element, valueAccessor)
// and refresh the element (for jquerymobile)
var checkbox = $(element);
checkbox.checkboxradio('refresh')
}
};

Resources