Yii2: Use Html::tag() with HTML content for tooltip Bootstrap 3 - twitter-bootstrap-3

Hint: Formating as raw won't fix my problem - See attachement respectively picture:
The following code will implement tooltip using attribute title of method tag().
Unfortunately, there is no possibility rendering HTML-tags inside the title. Any ideas(links are welcome) how to implement tooltip handling my intention to render HTML tags? Is there any other option implementing tooltip-box outside attribute
title, so that I am able to render HTML-tags
[
'attribute' => $dummy,
'label' => Yii::t('app', 'Charakterisierung'),
'format' => 'raw',
'vAlign' => 'middle',
'value' => function($model) {
if (!(empty($model->person->personentypDominant->typ_name))) {
$tag = Html::tag('span', 'Tooltip-Touch Me!', [
// html-tags won't be rendered in title
'title' => $model->person->personentypDominant->typ_empfehlung],
'data-toggle' => 'tooltip',
'data-placement' => 'left',
'style' => 'white-space:pre;border:1px solid red;'
]);
return $tag . "<br>" . $model->person->personentypDominant->typ_verhaltensmerkmal_im_team_1 . "," . $model->person->personentypDominant->typ_verhaltensmerkmal_bei_stress_3 . "," . $model->person->personentypDominant->typ_verhaltensmerkmal_am_arbeitsplatz_4;
}
}
],

The Html::tag() takes 3 parameters like below
<?= Html::tag($name, $content = '', $options = []) ?>
Then if you look into the documentation for the bootstrap Tooltip options there is an option named HTML
Insert HTML into the tooltip. If false, jQuery's text method will be
used to insert content into the DOM. Use text if you're worried about
XSS attacks.
Which has the default value as false and you have to set it manually to true.
So apparently, you have 3 problems
You are closing the options ] on the line 'title' => $model->person->personentypDominant->typ_empfehlung ],
There isn't any attribute with the name vAlign for yii\grid\DataColumn untill unless you are using kartik\grid\DataColumn then it's ok.
You are not initializing tooltip with the option HTML true.
First of all, add the following to the top of your view or inside the layout file if want to apply these setting allover
$js = <<<SCRIPT
/* To initialize BS3 tooltips set this below */
$(function () {
$('body').tooltip({
selector: '[data-toggle="tooltip"]',
html:true
});
});
SCRIPT;
// Register tooltip/popover initialization javascript
$this->registerJs ( $js );
Change your code for GridView to the following, i assume that $model->person->personentypDominant->typ_empfehlung , has the HTML that you are trying to render
[
'attribute' => $dummy ,
'label' => Yii::t ( 'app' , 'Charakterisierung' ) ,
'format' => 'raw' ,
'value' => function($model) {
if ( !(empty ( $model->person->personentypDominant->typ_name )) ) {
$tag = Html::tag ( 'span' , 'Tooltip-Touch Me!' , [
// html-tags won't be rendered in title
'title' => $model->person->personentypDominant->typ_empfehlung ,
'data-placement' => 'left' ,
'data-toggle'=>'tooltip'
'style' => 'white-space:pre;border:1px solid red;'
] );
return $tag . "<br>" . $model->person->personentypDominant->typ_verhaltensmerkmal_im_team_1 . "," . $model->person->personentypDominant->typ_verhaltensmerkmal_bei_stress_3 . "," . $model->person->personentypDominant->typ_verhaltensmerkmal_am_arbeitsplatz_4;
}
}
];
EDIT
You need to use the column format as raw when using the gridview otherwise the tooltip won't render.
"format"=>"raw"
Edit 2
Make Sure you are not Using AdminLTE theme with jquery UI as they have a conflict SEE ISSUE.
The causes of conflict on the jquery UI tooltip and the bootstrap tooltip are jQuery UI tooltip overwrite the Bootstrap tooltip maybe they use the same namespace and function name.
Add the following code in your javascript (solution from here)
var bootstrapTooltip = $.fn.tooltip.noConflict();
$.fn.bstooltip = bootstrapTooltip;
$('element').bstooltip();
var bootstrapTooltip = $.fn.tooltip.noConflict();
$.fn.bstooltip = bootstrapTooltip;
$('element').bstooltip();
Demo
$(document).ready(function() {
var bootstrapTooltip = $.fn.tooltip.noConflict();
$.fn.bstooltip = bootstrapTooltip;
$('#mybtn').bstooltip();
})
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<div style="margin:100px;">
<button id="mybtn" title="my tooltip showing now">hover me</button>
</div>
The point that needs attention is that we must put the jqueryui.js before bootstrap.js, so you have to rearrange your .js files in the $js array inside the AppAsset file you are using to load all the scripts.

Please note that now bootstrap 3.4.1 and 4.3.1 has a sanitizer that block most of HTML tags by default.
If your tooltip (or popover) data-content contain one or more HTML tags not listed in the default whitelist, you need to add them manually:
var myDefaultWhiteList = $.fn.tooltip.Constructor.DEFAULTS.whiteList
// To allow table elements
myDefaultWhiteList.table = []
// To allow td elements and data-option attributes on td elements
myDefaultWhiteList.td = ['data-option']
// You can push your custom regex to validate your attributes.
// Be careful about your regular expressions being too lax
var myCustomRegex = /^data-my-app-[\w-]+/
myDefaultWhiteList['*'].push(myCustomRegex)
// initialize your tooltip
$(function () {
$('[data-toggle="tooltip"]').tooltip({
// set your custom whitelist
whiteList: myDefaultWhiteList
});
});

Related

In Cypress how to found count a selection with same ID and get the length?

I have a such HTML code.
<div id ='pages'>
<div id='wrapper'>1 </div>
<div id='wrapper'>2 </div>
</div>
I am want to find elements count with id wrapper.
I using Cypress. I'm starting to learn Cypress.
If I try:
cy.get('div#wrapper').should('have.length', 2)
I get AssertionError:
CypressError: Timed out retrying: expected 1 to equal 2
As jonrsharpe pointed out, it's invalid HTML to have multiple elements with identical id attribute.
That being said, DOM is quite smart and can recover and work even with invalid HTML. Duplicate-id elements shouldn't cause much trouble.
If you e.g. try doing document.querySelectorAll('#wrapper') it should return list of 2 elements (in your case).
Problem is, Cypress is using jQuery to query the DOM instead of using native DOM methods and I guess jQuery isn't as smart (or it's more pedantic).
That being said, I can't reproduce that error when running:
// succeeds
cy.get('div#wrapper').should('have.length', 2)
Only when querying #wrapper directly (without the preceding div):
// fails
cy.get('#wrapper').should('have.length', 2)
I reckon this is because jQuery uses a heuristic of exiting early when a selector string (#wrapper) contains only a single id (and that's why div#wrapper returns both elements).
Also, your solution in comments (cy.get('#pages') .find('div#wrapper') .should(($div) => { expect($div).to.have.length(2) })), while working, isn't ideal because it won't retry. Let me demonstrate:
In the following code, the 2nd #wrapper will appear in the DOM only after 1 sec.
describe( 'test', () => {
beforeEach(() => {
cy.document().then( doc => {
doc.body.innerHTML = `
<div id='pages'>
<div id='wrapper'>1</div>
</div>
`;
setTimeout(() => {
doc.body.innerHTML = `
<div id='pages'>
<div id='wrapper'>1</div>
<div id='wrapper'>2</div>
</div>
`;
}, 1000 );
});
});
// will fail
it('solution A', () => {
cy.get('#pages') // <- won't be retried
.find('div#wrapper') // <- only this command will be retried
.should( $div => expect($div).to.have.length(2) );
});
// will pass
it('solution B', () => {
cy.get('#pages #wrapper') // <- will be retried and succeed in 1sec
.should( $div => {
expect($div).to.have.length(2);
});
});
// will pass
it('solution C', () => {
cy.get('#pages')
.should($pages => {
// using native DOM querying
expect($pages[0].querySelectorAll('#wrapper').length).to.eq(2);
});
});
});
Thus, you should go with solution similar to B or C.

Instant Search with keyup vuejs and laravel

I tried to build an instant search using vuejs and laravel 5.3 but somehow It wont work with no errors showing
Controller (fullcode https://pastebin.com/6mQ4eWTf) :
public function index(Request $request) {
$search = $request->search;
$items = Staff::where('nama', 'LIKE', '%'.$search.'%')->paginate(5);
$response = [
'pagination' => [
'total' => $items->total(),
'per_page' => $items->perPage(),
'current_page' => $items->currentPage(),
'last_page' => $items->lastPage(),
'from' => $items->firstItem(),
'to' => $items->lastItem()
],
'data' => $items
];
staff.js method (fullcode https://pastebin.com/NDxzqsyp) :
methods: {
getVueItems: function (page) {
this.$http.get('/staffitems?page=' + page + '&search=' + this.search).then((response) => {
this.$set('items', response.data.data.data);
this.$set('pagination', response.data.pagination);
});
setTimeout(this.getVueItems, 5000);
},
Blade (fullcode https://pastebin.com/6uDZRryE) :
<input v-on:keyup.enter="getVueItems" type="text" class="form-control" name="search" placeholder="Cari..." v-model="search"/>
Routes :
Route::get('/staffcrud', 'StaffController#Crud');
Route::resource('/staffitems', 'StaffController');
The data correctly shown (tested by getting json response from /staffitems?page=1&search=jon with or without search value), but somehow when I do type words to search in input search column, nothing happened as soon as i finished typing, probably event handling in blade are wrong or my method in staff.js any solution for this?
You should add the debounce in your mounted() method:
mounted() {
this.getVueItems = _.debounce(this.getVueItems, 5000); // i'm using lodash here.
}
To build a really effective instant database search you should consider using pusher and laravel echo together with vuex
You can check out this out >>> Ethiel Adiassa's Live Search Tutorial with laravel and pusher
In your blade template use only:
v-on:keyup='vueGetItems'
Because your instant search is firing only on enter key up. I hope this will work or contact me to get full working code.

: yii ajaxSubmitButton won't recognize my action url

I have an update form, and within that form there is a table where I want to populate using ajax after filling in a couple of fields. I have tried using ajaxSubmitButton but somehow it just doesn't trigger the action that I want.
Here is my view:
<?php
echo CHtml::ajaxSubmitButton('Insert', array('myController/insertProgress'), array(
'type' => 'POST',
'success' => 'function(){
alert("success");
}',
'data' => array(
'progress' => 'js:$("#progress").val()',
),
)
);
?>
myController:
public function actionInsertProgress() {
$data = $_POST['progress'];
//do stuff here, including echoing the table row
}
When I click the submit button, it doesn't trigger the insertProgress action, but instead the main form action which is actionEdit. It's as if the URL that I provided is being ignored.
The url for this form goes something like this:
(sitename)/(modulename)/myController/edit/id/57
Thank you.
EDIT: I do have another submit button to update the whole form, which triggers the actionEdit action.
EDIT2: this is what the widget produces:
<script type="text/javascript">
/*<![CDATA[*/
jQuery(function($) {
jQuery('body').on('click','#yt0',function(){jQuery.ajax({'type':'POST','data':{'progress':$("#progress").val()},'url':'http://inarac.id/adm/topikkajian/insertProgress','cache':false});return false;});
});
/*]]>*/
</script>
If you are using module you should add the modele path to your link
$myLink = Yii::app()->getBaseUrl(true) .
'/index.php/moduleName/myController/insertProgress';
<?php
echo CHtml::ajaxSubmitButton('Insert',
$myLink, array(
'type' => 'POST',
'success' => 'js:function(){
alert("success");
}',
'data' => array(
'progress' => 'js:$("#progress").val()',
),
)
);
?>

Always show Pager on CGridView?

I've build a CGridView menu, and I want to always display the pager
(even when it's showing all the data and the navigation is not needed)
This is the current code I have:
$this->widget('zii.widgets.grid.CGridView',
array('dataProvider'=>$search,
'columns' => $columns,
'itemsCssClass' => 'list_table',
'template' => '{pager}{summary}{items}',
'pager' => array(
'cssFile'=>false,
'class'=>'CLinkPager',
'firstPageLabel' => '<<',
'prevPageLabel' => '<',
'nextPageLabel' => '>',
'lastPageLabel' => '>>',
'header' => '',
'footer' => $footer_btns,
),
'pagerCssClass' => 'pagination',
));
You could do this by overriding the renderPager() method -- however, it seems that the pager gets put together in a few files so one way to do it by only overriding one class would be to:
override zii.widgets.grid.CGridView to add your custom renderPager() method with something like:
Yii::import('zii.widgets.grid.CGridView');
class MyGrid extends CGridView {
public function renderPager() { ... }
}
the default renderPager() function is here.
What you want to do is look for the line that tests for pager content:
if($pager['pages']->getPageCount()>1) {
and change the "else" statement to put in your default "empty" pager content, which could use the same <ul> structure. Since you are not providing any navigation for the blank view, you don't need to worry about that data if this is used in multiple places. That could look something like:
else {
echo '<div class="'.$this->pagerCssClass.'">';
## YOUR CUSTOM "EMPTY PAGER" HTML HERE ##
echo '</div>';
}
You might need to define a couple extra css classes as well. On pages where only part of the pagination is showing (e.g., the first and last page), you can use CSS to redefine the ".hidden" class(es).

Dragging dnd items generates "this.manager.nodes[i] is null"

I use Dojo 1.3.1, essentially under FF3.5 for now.
I have a dnd source which is also a target. I programmatically add some nodes inside, by cloning template items. The aim for the user is then to use dnd to order the items. It is ok for one or two actions, then I got the "this.manager.nodes[i] is null" error in Firebug, then no more dnd action is taken into account.
My HTML (jsp), partial:
<div id="templates" style="display:none">
<div class="dojoDndItem action" id="${act.name}Template">
<fieldset>
<legend class="dojoDndHandle" >${act.name}</legend>
<input id="${act.name}.${parm.code}." type="text" style="${parm.style}"
dojoTypeMod="dijit.form.ValidationTextBox"
/><br>
</fieldset></div>
</div>
My Javascript for adding/removing dnd items nodes, partial :
function addActionFromTemplate(/* String */actionToCreate, /* Object */data) {
// value of actionToCreate is template id
var node = dojo.byId(actionToCreate + "Template");
if (node) {
var actNode = node.cloneNode(true);
// make template id unique
actNode.id = dojo.dnd.getUniqueId();
// rename inputs (add the action nb at the end of id)
// and position dojo type (avoid double parsing)
dojo.query("input[type=text], select", actNode).forEach( function(input) {
input.id = input.id + actionsCount;
dojo.attr(input, "name", input.id);
dojo.attr(input, "dojoType", dojo.attr(input, "dojoTypeMod"));
dojo.removeAttr(input, "dojoTypeMod");
});
// insert the action at script's tail
actionList.insertNodes(true, [ actNode ]);
dojo.parser.parse(actNode);
// prepare for next add
actionsCount++;
}
}
function deleteAction(node) {
var cont = getContainerClass(node, "action");
// remove the fieldset action
cont.parentNode.removeChild(cont);
}
Thanks for help ...
OK, it seems that, finally, simply using :
actionList.insertNodes(false, [ actNode ]);
instead of
actionList.insertNodes(true, [ actNode ]);
fixed the pb .