How do I keep focus on a textbox using Lift (the Scala framework)? - scala

Pardon my excruciatingly simple question, but I'm quite new to the world of Lift (and Scala, for that matter).
I'm following the "Getting Started" tutorial on the Lift website: http://liftweb.net/getting_started
I've got it up and running but I'd like to make a quick modification to the app so that every time I press enter in the textbox, it maintains focus. I've managed to get it to focus on page load using FocusOnLoad but I can't quite figure out how to get it to keep focus (using only Lift's classes, no custom JavaScript).
Here's what my def render method (the bind part) code looks like:
def render =
bind("chat", // the namespace for binding
"line" -> lines _, // bind the function lines
"input" -> FocusOnLoad(SHtml.text("", s => ChatServer ! s)) )
So that works for getting it to focus upon page load. But since this is a Comet app, the page only loads once.
All my other code looks exactly like the tutorial FWIW.

The render method in a CometActor only gets called when the CometActor is first initialized, which happens when the user first goes to the Chat page. Afterwards, the page updates usually happen inside of the lowPriority or highPriority methods. So if you want the text box to become focused after the user sends an AJAX update to the CometActor, you should add it to one of those methods. An example which uses JQuery would be this:
override def lowPriority = {
case m: List[ChatCmd] => {
val delta = m diff msgs
msgs = m
updateDeltas(delta)
partialUpdate((JqJE.Jq(JE.Str("input[type=text]")) ~> (new JsRaw("focus()") with JsMember)).toJsCmd)
}
}
I haven't tried compiling this, so it might need some slight tweaking. Essentially, it's just sending another JavaScript command to the browser that uses JQuery to find a text input on the page and then sets the focus on that control. If there are multiple text inputs, you'll need to modify the class on the HTML template for the control you want to set focus on, then make sure that you change your render method to be:
def render =
bind("chat",
"line" -> lines _,
"input" -%> FocusOnLoad(SHtml.text("", s => ChatServer ! s)) )
The -%> method instructs Lift to not ignore any attributes in the template during the bind phase. Then you can modify the JQuery selector to use that class to find the right control to focus on. Part of the form security in Lift obscures the ID assigned to forms and their controls to prevent XSS attacks, so it's generally better to use class selectors to find form controls using JQuery or some other Javascript framework.

Related

How do you inspect a react element's props & state in the console?

React Developer Tools give a lot of power to inspect the React component tree, and look at props, event handlers, etc. However, what I'd really like to do is to be able to inspect those data structures in the browser console.
In chrome I can play with the currently selected DOM element in the console using $0. Is there a way to extract React component info from $0, or is it possible to do something similar with the React Dev Tools?
Using React Developer Tools you can use $r to get a reference to the selected React Component.
The following screenshot shows you that I use React Developer Tools to select a component (Explorer) which has a state-object callednodeList. In the console I can now simply write $r.state.nodeList to reference this object in the state. Same works with the props (eg.: $r.props.path)
An answer to your question can be found here in a similar question I asked:
React - getting a component from a DOM element for debugging
I'm providing an answer here because I don't have the necessary reputation points in order to mark as duplicate or to comment above.
Basically, this is possible if you are using the development build of react because you can leverage the TestUtils to accomplish your goal.
You need to do only two things:
Statically store the root level component you got from React.render().
Create a global debug helper function that you can use in the console with $0 that accesses your static component.
So the code in the console might look something like:
> getComponent($0).props
The implementation of getComponent can use React.addons.TestUtils.findAllInRenderedTree to search for match by calling getDOMNode on all the found components and matching against the passed in element.
Open console (Firefox,Chrome) and locate any reactjs rendered DOM element or alternatively execute js script to locate it:
document.getElementById('ROOT')
Then check for element properties in object property viewer for attributes with name beginning like '__reactInternalInstace$....' expand _DebugOwner and see stateNode.
The found stateNode will contain (if it has) 'state' and 'props' attributes which is used heavily in reactjs app.
Though the accepted answer works, and is a great method, in 2020 you can now do a lot of inspection without using the $r method. The Components tab of React DevTools will show you props and detailed state when you select the relevant component (make sure you're on the right level), as well as let you do other things like suspend it or inspect the matching DOM element (little icons in the top right).
Assign the state or prop object to the window object:
window.title = this.state.title
And then from the dev tools console you can try different methods on the exposed object such as:
window.title.length
8
You can attach a reference to the window object like
import { useSelector } from "react-redux";
function App() {
// Development only
window.store = useSelector((state) => state);
return (
<div className="App">
</div>
);
}
export default App;
Then access it from the console
store
{states: {…}}
states:
someProperty: false
[[Prototype]]: Object
[[Prototype]]: Object
[Console][1]
[1]: https://i.stack.imgur.com/A4agJ.png

GTM Reduce number of tags

GTM up and running, main UA tag in place along with a ClickListener tag.
To reduce the number of macros, i use dataLayer variable Macros for event category, action, label, value & interaction, so they can be used for many rules and tags.
So i want to collect data from one link/button (Add to Fav), i add a rule to listen for the click using {{event}} equals gtm.click and {{Event Label}} equals Add_to_Fav (the label i push to the DL via onclick.
All good so far, but i need to create another UA tag (Track Type - event) that fires on the rule made previously. And this is my question, using this method seems to create many tags. If i have another 20 links that i want to collect data from, do i need to keep creating tags like this. Surely, this will affect page load speed with many tags firing on all pages.
Hope thats all clear.
If you need to retrieve the link text to use it as an event label you do not need many many event tracking tags, that would be horribly verbose. Instead you can use a custom javascript macro - the cool thing about them being that you can use existing macros inside your custom function.
If you create a click listener or link click listener this will create a few macros - one of them is {{element}}, which is the DOM element that received a click.
Now you create a macro of the type "custom java script", which must contain an anonymous function with a return value.
The barebones version of a function that retrieves the text of a clicked link would be
function() {
var el = {{element}};
return el.innerText;
}
(actually you do not need the variable assigment, you could use {{element}}.innerText directly).
You name the macro e.g. Linktext and use the macro {{Linktext}} in your single event tracking tag where it will dynamically be set to the value of the text of the clicked link (although you might want to check cross browser support for innerText, or maybe use innerHTML instead which serves in you use case probably the same purpose).

How to handle UI animation events with knockout

So right now I have a table that displays some values and I have an indicator for conflicts. When the user clicks the indicator a new div appears with some animation to list all the conflicts.
Here is my HTML:
<span data-bind="if: hasConflict, click: $parent.selectProperty" class="conflictWarn"><i style="color: darkorange; cursor:pointer;" class="icon-warning-sign"></i></span>
The data might look something like this:
{
name:Property 1,
id: 1,
hasConflicts: no,
name:Property 2,
id: 2,
hasConflicts: yes,
conflicts: {
name: conflict1,
name: conflict2
}
name:Property 3,
id: 3,
hasConflicts: yes,
conflicts: {
name: conflicta,
name: conflictb
}
So the first table is going to look like this:
Property 1
Property 2 !
Property 3 !
Where ! is a conflict indicator. Clicking on the ! would display the conflicts div and also display conflict1 and conflict2 or conflicta and conflictb depending on which was clicked.
Here is the model we are working with. It's a bit complex because of the mapping for the properties from signalr. the "selectProperty" and "selectedProperty" was our way of saying which one to display conflicts for, but I'm not convinced this is the best way to do it.
function ItemViewModel() {
var self = this;
self.name = ko.observable("");
self.itemType = ko.observable("");
self.propertiesArray = ko.observableArray([]);
self.properties = ko.mapping.fromJS({});
self.selectedPropertyName = ko.observable("");
self.getItem = function (name) {
$.connection.mainHub.server.getItem(name).then(function (item) {
ko.mapping.fromJS(item.properties, self.properties);
self.propertiesArray(item.propertiesArray);
self.itemType(item.itemType.name);
self.name(item.name);
});
self.selectProperty = function (a, b) {
self.selectedPropertyName(a);
};
};
}
Originally the click event directly called a javascript function that did all the animation, but my coworker thought that might violate best practices for separating data and viewmodel in MVVM. Does it? Should we leave it calling the viewmodel function of "selectProperty" which allows us to pass context for the "conflicts popup" div? If so, do I just call the javascript function to do the animation from within the selectProperty function?
p.s. I've edited this about 800 times so I apologize if it's impossible to follow.
update I have the bindings working now, so I really just want to know what is best practice when it comes to UI animations and Knockout. Change the viewmodel from the javascript function or call the javascript function from the viewmodel function?
Regarding UI animations in my opinion it is best practice to implement custom bindings. This way code is encapsulated and it is easy to find where it is used. Check Animated transitions example on knockout website.
i'm going to extends Thomas answer with one point, custom bindings don't work when you want to animate the rendering / unrendering of the 'if' or 'with' bindings. an animation binding that tries to run at the same time as an 'if' or 'with' won't be able to complete the animation before the other binding alters the DOM, possibly removing the elements being animated from the page. there is no way to defer the binding processing until after the event completes.
for these cases animations should be implemented via the 'afterAdd' and 'beforeRemove' callbacks of the 'foreach' binding when the desire is to animate an element being added and removed from the page. 'if' and 'with' bindings can be rewritten as 'foreach' with little effort, as 'foreach' can take a single argument instead of a list. i really wish the animation tutorial would be extended to include this workaround.

How do I add onsubmit or onclick validation logic to my Lift form using CSS binding?

I have a form working with CSS selector binding and Scala-side validation. Now I want to add javascript validation either via my own jQuery code that I call or else the built-in commands like JsIf.
Here is my current code:
def create = {
"#description" #> SHtml.textarea(maintRV.is.description.get,
name => maintRV.is.description(name) ) &
"#submit" #> SHtml.onSubmitUnit(processSubmit)
}
How can I add onclick or onsubmit behavior to the SHtml.onSubmitUnit binding? I have already used Ajax forms in other parts of my code, but for this page that seems like overkill. I don't mind a regular form submit. I just want to add client-side validation.
I've combed SimplyLift, Exploring Lift and the groups and can't find it.

CollapsiblePanelExtender: Can I initiate collapse/expand from client-side javascript? (AJAX Control Toolkit)

The CollapsiblePanelExtender seems primarily designed to collapse/expand things in response to user mouse events. Is there also a good way to get the extender to collapse/expand things in response to client-side javascript?
In my particular case, I have a number of CollapsiblePanelExtenders (and their corresponding Panels) on a page, and I'm wondering if I could implement an "expand all panels" button by doing something like this strictly on the client side:
for each CollapsiblePanelExtender on this page, call somethingOrOther(extender)
I can implement this logic server-side instead if I did a full postback, but my page takes a long time to load, and so this doesn't seem like it would provide a very slick user experience. Thus I am interested in doing expand/collapse client-side.
It seems like this isn't a use case the AJAX Control Toolkit people had in mind, but I thought I'd check.
Write the following code in the OnClick event of Image/button
<asp:Image ID="img1" runat="server" OnClick="ExpandCollapse()"/>
function ExpandCollapse() {
$find("collapsibleBehavior1").set_Collapsed(true);
$find("collapsibleBehavior2").set_Collapsed(true);
}
Hope this helps!
I have a partly working solution now.
I followed Ian's suggestion and looked through the toolkit source. In CollapsiblePanelBehavior.debug.js, you can that expandPanel() is apparently intended as part of the public interface for the behavior. There's also a get_Collapsed(). The key to accessing these behaviors in javascript seems to be setting the BehaviorID property on your CollapsiblePanelExtender tags in ASP.NET.
I modified the repeater on my page so that the BehaviorIDs are predictible, along these lines:
<ajaxToolkit:CollapsiblePanelExtender
BehaviorID="<%#'collapsebehavior'+DataBinder.Eval(Container.DataItem,'id')%>"
ID="CollapsiblePanelExtender" runat="server" />
This results with behaviors named collapsebehavior1, collapsebehavior2, collapsebehavior3, etc..
With this done, I'm able to expand all the collapsible panels on the client as follows:
function expandAll() {
var i = 0;
while (true) {
i++;
var name = 'collapsebehavior' + i;
var theBehavior = $find(name);
if (theBehavior) {
var isCollapsed = theBehavior.get_Collapsed();
if (isCollapsed) {
theBehavior.expandPanel();
}
} else {
// No more more panels to examine
break;
}
}
}
I'm sure using $find in a loop like that is really inefficient, but that's what I have so far.
Also, it doesn't work on Firefox for some reason. (On FF only the first element expands, and then there's a Javascript error inside the Control Toolkit code.)
This will all seem extremely ugly to all you javascript pros. Maybe I'll clean things up later, or you can help me out.
You can also just toggle the panels to switch between collapsed/expanded states:
function toggle() {
var MenuCollapser = $find("name");
MenuCollapser.togglePanel();
}