How do I bind a dynamic set of jQuery Mobile buttons using Knockout.js? - button

I'm using jQuery Mobile (jQM) and Knockout.js (ko) to develop an application. In this application, I need to generate a variable number of buttons that are defined by a constantly updating web service.
So, in my markup, I have:
<div id="answerPage-buttons" data-bind="foreach: buttonsLabels">
<button data-role="button" data-inline="true" data-theme="b" data-bind="text: text, click: $root.submitAnswer" />
</div>
buttonLabels is a list of short strings returned from the web service. It's defined as:
self.buttonLabels = ko.observableArray();
This all works fine when the buttons are not "jQM styled". However, when I style them using:
$("#answerPage-buttons").trigger("create");
problems arise during the update.
The issue seems to be that jQM wraps the buttons in a div (with a sibling span) to make them all nice and mobile looking. However, when the ko applies the updates via the bindings, it only removes the tags, leaving the surrounding stuff, and adds new button tags - which are then also styled by the jQM trigger call.
So, I end up with an ever-growing list of buttons - with only the last set being operational (as the previous ones are gutted by the removal of their button element, but all the styling remains).
I've managed to address this, I think, by placing the following call immediately after the observable is updated:
$("#answerPage-buttons div.ui-btn").remove();
However, my feeling is that there's probably a better approach. Is there?

I found a solution.
If I surround the buttons with a div, it seems to work - e.g.
<div id="answerPage-buttons" data-bind="foreach: buttonsLabels">
<div>
<button data-role="button" data-inline="true" data-theme="b" data-bind="text: text, click: $root.submitAnswer" />
</div>
</div>
I'm guessing this is because the markup added by jQM remains "inside" the markup replicated by ko. Without the div, jQM wraps the button tag, which was the immediate child of the tag that contains the ko foreach binding.

Related

Is there a proper way to wire up Trix editor with Livewire?

When wiring together Trix editor content with Livewire, I am stumbling into problems. I believe that the issue is that when Livewire receives content from Trix, the content is swapped out and Trix becomes disabled. Is there a better way?
What I have done, that works, is as follows. At the moment, the page is the redirected to itself in order to reboot Trix (defeating the whole point of Livewire, but it's being used for other things too).
<div>
<input
id="newCommentTextTrixContent"
type="hidden"
wire:model="newCommentText"
>
<trix-editor
id="newCommentTextTrixEditor"
input="newCommentTextTrixContent"
></trix-editor>
<button wire:click="addComment(document.getElementById('newCommentTextTrixEditor').innerHTML)">Add Comment</button>
</div>
I have tried
wire:model on the hidden input -- nothing happens
x-on:trix-change="$set('comment', $event.target.innerHTML) -- this works, but Trix goes grey and ceases to work after the first keypress (reboot problem?)
I'm sure something like the latter is better, but with Trix somehow being rebooted each time. It all seems a bit messy - so the question is, what's the right way to do this?
I got it working. With up to date Livewire and Alpine installations, the code is roughly as follows.
<div wire:ignore>
<trix-editor
class="formatted-content"
x-data
x-on:trix-change="$dispatch('input', event.target.value)"
x-ref="trix"
wire:model.debounce.60s="newCommentText"
wire:key="uniqueKey"
></trix-editor>
</div>
Why does this work?
You need wire:ignore on the parent node, because Trix inserts the toolbar above the text area. wire:ignore stops Livewire from worrying about it and therefore not removing it or messing with it on the next cycle.
You need a wire:key because the DOM moves around a bit, and this helps Livewire to keep track of it.
I propose the long debounce, which is a hack as the .lazy modifier doesn't work well with text. Also, waiting for Ajax on each key press is painful.
The alpine event ensures that Trix events (like bold, italics etc) are still fired
That's it. I use this above to repetitively submit comments onto the end of a comment stream, and everything seems to work fine. Good luck!
Note, I also have CKEditor working similarly, as described here.
As an extension on #Kurucu 's answer, and the comment under it from #Rehan;
This seems to work very well. But when I apply styles like li or bold
it doesnt retain in the wire:model. Ex:
<div>foo<br>bar<br>foobar</div> I applied the Bullets here the tags
are missing. Did you face this issue? – Rehan
To fix the issue of not having an updated value when pressing buttons bold, italic, or quote for example, add the following part to the trix editor (note the x-on:trix-change="$dispatch('input', event.target.value)"):
<div wire:ignore>
<trix-editor
class="formatted-content"
x-data
x-on:trix-change="$dispatch('input', event.target.value)"
wire:model.debounce.1000ms="newCommentText"
wire:key="uniqueKey"
></trix-editor>
</div>
The above option works but wasn't getting the data back from my field, Here's what worked for me with a little tweak using AlpineJS's #entangle.
Below is my working solution:
<div class="mb-2" x-data="{ description: #entangle('description').defer }"
x-init="$watch('description', function (value) {
$refs.trix.editor.loadHTML(value)
var length = $refs.trix.editor.getDocument().toString().length
$refs.trix.editor.setSelectedRange(length - 1)
}
)" wire:ignore>
<label class="form-label">Job Description <span class="text-danger">*</span></label>
#error('description')
<span class="error d-inline-block"><i class="mdi mdi-alert-circle"> </i> {{$message}}</span>
#enderror
<input name="description" id="description" type="hidden" x-model="description">
<div x-on:trix-change.debounce.1000ms="description = $refs.trix.value">
<trix-editor x-ref="trix" input="description" class="overflow-y-scroll"
style="height: 20rem;"></trix-editor>
</div>
</div>
I got it to work by using Trix's built-in events.
<input id="about" name="about" type="hidden" value="{{ $about }}">
<trix-editor input="about" class="wysiwyg-content"></trix-editor>
<script>
var about = document.getElementById("about")
addEventListener("trix-change", function(event) {
console.log(about.getAttribute('value'));
#this.set('about', about.getAttribute('value'))
})
</script>

How to locate Button followed by card_header-title containing specific text in sample html

How to select Actions Button followed by the Div class containing known Text ( for example, card_header-title"Addresses" in this case) in Robot Test Framework?
The page contains several span table sections and each of them has its own actions and show-history buttons. To select the specific Actions button, I could use its xpath, but I am trying to access all sections in a for loop and the xpath of actions button in one section changes from the other, so hard coding is not an option for me. Would someone please help.
<div class="attribute-group-header card__header">
<h3 class="attribute-group-title card__header-title">Addresses</h3>
<div class="floatright">
<input type="button" class="action small btn" value="Actions">
<input type="button" class="showHistory action small btn" value="ShowHistory">
</div>
</div>
I know you say you don't want to use Xpaths but maybe one of these examples could help. I don't see any other way of achieving what you're asking for other than having id's supplied on the buttons.
You could use an xpath locator that first finds the text of the "attribute-group-title card__header-title" element and then selects the following sibling div, followed by the input:
//*[contains(text(),'Addresses')]/following-sibling::div[1]//input[#value='Actions']

Knockout css bindings within a foreach

I'm trying to do some templating for the site I'm working on the make it more reusable. To do this I'm using knockout to help with data-binding to transfer info from a json fine.
However, I'm having quite a bit of trouble passing a css property object to a span element within my template.
My html template kind of looks like this
<div data-bind="cssProperties: properties, css: { hidden : EvalDisplay() == false }">
<p>
<!-- ko foreach: options -->
<label class="btn">
<input type="checkbox" />
**<span class='optionText' data-bind="cssProperties: $parent.properties, html: Value"></span>**
</label>
<!-- /ko -->
</p>
</div>
The span with the asterisks next to it is what's giving me trouble. If I move that outside of the foreach loop then it works fine and the property is added, but if I keep it inside that loop where I need it to be it's just not being applies.
Any help would be great. I am very new at knockout so I don't know all the cool little tricks yet.
And before you ask, yes the css property has to be in the json and not in a css sheet. It needs to be where non-technical people can access it to change it.

AngularJS view rendering issue

I am using ng-view to render dynamic data on my page. When the page loads, if I use static html I get this (top):
When Angular loads the page the data is there but it's like the element is still empty (bottom).
If I make even the slightest adjustment in Chrome dev tools, the items snap into place but I cannot get them to prefill without using CSS to make them static sizes, which doesn't work because text is different sizes. The elements in question have CSS of inline-block.
As you can see in this screenshot, I have tried two ways of populating the data, both with the same result:
<div class="cd-rating" ng-class="caseData.scoreClass">
<span class="score" ng-bind="caseData.adjustedScore | number:0" ng-cloak>N/A</span>
<span class="verdict">{{caseData.recommendation}}</span>
</div>
Here is the what the HTML looks like in the browser:
<div class="cd-rating medium" ng-class="caseData.scoreClass">
<span class="score ng-binding" ng-bind="caseData.adjustedScore | number:0">349</span>
<span class="verdict ng-binding">review</span>
</div>
If I hard-code that HTML identically, then it renders perfectly. It's only when Angularjs renders it that the elements are collapsed, appearing if there is not content.
I would also like to add that I am using RequireJS and manually bootstrapping the app instead of using the ng-app directive, in case that matters.
Any ideas on how to make the view fill the elements?
EDIT: Here is a video that demonstrates the behavior: http://youtu.be/zTwv-o6mWRM
I am not able to figure out what exactly you mean by the "..data is still there but the element is empty.." - the only issue that I find with the rendering by AngularJS is that the "Review" (button?) is overwritten with the number.
Looking at your code (which, as #Wingman4l7 suggests needs to be posted in the question rather than as a image), I see that you are using bindings to define a class dynamically. Instead, can you use the ng-class directive and see if it resolves the issue?
That is, replace:
<div class="cd-rating {{caseData.scoreClass}}">
with
<div class="cd-rating" ng-class="caseData.scoreClass">
instead and check if the issue gets resolved?

Hide Alt text when Hover

I have a DIV tag. Inside the DIV, I have a Table and in a row, I have placed a script code which displays random images which on a click leads to a url.
This is how the script renders inside the Div Tag
<div>
<table>
<tr>
<td>
<script />
<a href="some random url">
<img></img>
</a>
...
When the user hovers over these images, the anchor url shows as a message on browser status bar. This is very misleading for users. I want to know how to use CSS to hide this status message - Cross Browser and display a custom message instead. On the Div, I have given onmouseout and onmouseover, however it does not help.
Can this be done?
See https://developer.mozilla.org/en/DOM/window.status :
This property does not work in default configuration of Firefox and some other browsers: setting window.status has no effect on the text displayed in the status bar. To allow scripts change the the status bar text, the user must set the dom.disable_window_status_change preference to false in the about:config screen.
This is a security feature that you can't realistically bypass.
common users dont know that they should look at that place in the browser window.
but you can hide that message... you can maybe just redirect with javascript
something like this:
<a href="javascript:void(0);" onclick="someredirectfunction('someurl');return false;" >
<img />
</a>
onmouseout and onmouse over are used for events for client side scripting. Those are used "mostly" for a language called ecmascript(javascript). You unfortunately will not be able to do what you are asking with CSS, css is desinged to represent the appearance of a site, HTML the form, and javascript (other scripting sources) the function.

Resources