Custom attribute not working on dynamic content - aurelia

I'm using w2ui grid, and the template column generated like so:
{ field: 'TableCards', caption: 'Table cards', size: '10%', sortable: true ,
render:function(record, index, column_index) {
let html = '';
if (record.TableCards) {
record.TableCards.forEach(function(card) {
html += `<div class="card-holder" style="width: 12%; display: inline-block; padding: 0.5%;">
<div class="poker-card blah" poker-card data-value="${card.value}"
data-color="${card.color}"
data-suit="&${card.suit};"
style="width: 30px;height: 30px">
</div>
</div>`;
});
}
return html;
}
},
poker-card as u can see is a custom attribute. and it's not get rendered in the grid.
any other way?

You can use the TemplatingEngine.enhance() on your dynamic HTML.
See this article for a complete example: http://ilikekillnerds.com/2016/01/enhancing-at-will-using-aurelias-templating-engine-enhance-api/
Important note: based on how your custom attribute is implemented, you may need to call the View's lifecycle hooks such as .attached()
This happened to me, when using library aurelia-material, with their attribute mdl.
See this source where the MDLCustomAttribute is implemented, and now see the following snippet, which shows what I needed to do in order for the mdl attribute to work properly with dynamic HTML:
private _enhanceElements = (elems) => {
for (let elem of elems) {
let elemView = this._templEngine.enhance({ element: elem, bindingContext: this});
//we will now call the View's lifecycle hooks to ensure proper behaviors...
elemView.bind(this);
elemView.attached();
//if we wouldn't do this, for example MDL attribute wouldn't work, because it listens to .attached()
//see https://github.com/redpelicans/aurelia-material/blob/5d3129344e50c0fb6c71ea671973dcceea14c685/src/mdl.js#L107
}
}

Related

Vuejs same function on multiple divs need to run seperately

Alright, I have these two divs with a mouseover and they have the same function. Now the problem is that if I mouse over one of them then BOTH shines. How to solve this? So shines one by one when I hover them.
DIVS:
<div class="latestItemBody" #mouseover="shineItemIcon"
#mouseout="shineOff" :style="{background: activeCardBg}">
<div class="latestItemBody" #mouseover="shineItemIcon"
#mouseout="shineOff" :style="{background: activeCardBg}">
Functions:
methods: {
shineItemIcon() {
this.activeCardBg = '#7a00ff';
this.bounce = 'animated bounceIn';
},
shineOff() {
this.activeCardBg = '';
this.bounce = '';
}
The reason why they both "shine" is because you have activeCardBg bound to both of them, which changes the background.
You could add the shine effect with pure CSS like this instead.
// CSS
.latestItemBody:hover {
background-color: #7a00ff
}
If you want to do this with JS, it could be done like this.
// Template
<div
class="latestItemBody"
#mouseover="shineItemIcon"
#mouseout="shineOff">
</div>
<div
class="latestItemBody"
#mouseover="shineItemIcon"
#mouseout="shineOff">
</div>
// Methods
shineItemIcon(e) {
e.target.style.backgroundColor = '#7a00ff';
this.bounce = 'animated bounceIn';
},
shineOff(e) {
e.target.style.backgroundColor = '';
this.bounce = '';
}
pass the div id as parameter to the shineitemicon and shineoff function. depending upon the condition set 'activeCardBg' value. give activecardBg1 to first div and activeCardBg2 to second div.

Twitter typeahead.js not working in Vue component

I'm trying to use Twitter's typeahead.js in a Vue component, but although I have it set up correctly as tested out outside any Vue component, when used within a component, no suggestions appear, and no errors are written to the console. It is simply as if it is not there. This is my typeahead setup code:
var codes = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('code'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: contextPath + "/product/codes"
});
$('.typeahead').typeahead({
hint: true,
highlight: true,
minLength: 3
},
{
name: 'codes',
display: 'code',
source: codes,
templates: {
suggestion: (data)=> {
return '<div><strong>' + data.code + '</strong> - ' + data.name + '</div>';
}
}
});
I use it with this form input:
<form>
<input id="item" ref="ttinput" autocomplete="off" placeholder="Enter code" name="item" type="text" class="typeahead"/>
</form>
As mentioned, if I move this to a div outside Vue.js control, and put the Javascript in a document ready block, it works just fine, a properly formatted set of suggestions appears as soon as 3 characters are input in the field. If, however, I put the Javascript in the mounted() for the component (or alternatively in a watch, I've tried both), no typeahead functionality kicks in (i.e., nothing happens after typing in 3 characters), although the Bloodhound prefetch call is made. For the life of me I can't see what the difference is.
Any suggestions as to where to look would be appreciated.
LATER: I've managed to get it to appear by putting the typeahead initialization code in the updated event (instead of mounted or watch). It must have been some problem with the DOM not being in the right state. I have some formatting issues but at least I can move on now.
The correct place to initialize Twitter Typeahead/Bloodhound is in the mounted() hook since thats when the DOM is completely built. (Ref)
Find below the relevant snippet: (Source: https://digitalfortress.tech/js/using-twitter-typeahead-with-vuejs/)
mounted() {
// configure datasource for the suggestions (i.e. Bloodhound)
this.suggestions = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('title'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
identify: item => item.id,
remote: {
url: http://example.com/search + '/%QUERY',
wildcard: '%QUERY'
}
});
// get the input element and init typeahead on it
let inputEl = $('.globalSearchInput input');
inputEl.typeahead(
{
minLength: 1,
highlight: true,
},
{
name: 'suggestions',
source: this.suggestions,
limit: 5,
display: item => item.title,
templates: {
suggestion: data => `${data.title}`;
}
}
);
}
You can also find a working example: https://gospelmusic.io/
and a Reference Tutorial to integrate twitter typeahead with your VueJS app.

how to put html tag in label columnheader?

I use dgrid to make a simple grid (http://dojofoundation.org/packages/dgrid/tutorials/defining_grid_structures/).
My question is simple : how to put html tag in label columnheader's ? Because if I put an img tag for example, label contains the string img src=...
Thanks
The column definition can provide a function that builds the column header.
var column = {
//...
renderHeaderCell: function(node) {
domConstruct.create('img', {src: ''}, node);
return node;
}
};
See the documentation of the renderHeaderCell() function in the DGrid wiki:
renderHeaderCell(node)
An optional function that will be called to render the column's header
cell. Like renderCell, this may either operate on the node directly,
or return a node to be placed within it.
One-line answer using put-selector:
renderHeaderCell: function(node) {
return put("img[src=/your/image]");
}
Note this function won't work if your column happens to be a selector - because selector.js defines his own renderHeaderCell(node) function.
#craig Thanks for the answer, in my case I only needed to know how to add HTML into the header cell and the renderHeaderCell(node) was definitely the answer.
For anyone else simply needing to add a <br>, <span>, <div> etc to the header cell, here's a couple of simple examples to compare:
Example without using renderHeaderCell(node):
{
label: 'Title',
field: appConfig.fields[0],
sortable: false
}
Example using renderHeaderCell(node):
{
renderHeaderCell: function(node) {
node.innerHTML = '<span class="headerCell">Title<br><br></span>'
},
field: appConfig.fields[0],
sortable: false
}
Then you can target with CSS as normal:
.headerCell {
font-size: 9px;
}

How to show a checkbox in a dojo datagrid?

How to show a checkbox in a dojo datagrid?
I would suggest setting cellType to dojox.grid.cells.Bool, instead of the formatter.The formatter gives you much freedom, but also the responsibility of gathering the data from all the checkboxes (for all the rows) afterwards. Something like this as a structure-entry should do the trick:
{
name: "is awesome?",
width: "auto",
styles: "text-align: center",
type: dojox.grid.cells.Bool, editable: true
}
Please make sure to use a write-store (like ItemFileWriteStore) and not just a read-store, otherwise you will be disabled to actually check the checkbox :)
Use formatter function as described in Widgets Inside dojo.DataGrid
You can return new dijit.form.Checkbox from formatter function in dojo 1.4
You need the IndirectSelection plugin for the EnhancedGrid, here's a fiddle: http://jsfiddle.net/raybr/w3XpA/5/
You can use something like this, with Json
HTML
<table id="myGrid" dojoType="dojox.grid.DataGrid"
clientSort="true" autoHeight="true" autoWidth="true">
<script type="dojo/method">
showFields();
</script>
</table>
DOJO
showFields:function () {
dojo.xhrPost({
url:"/getFields.do",
timeout:2000,
handleAs:"json",
load:dojo.hitch(this, "displayInGrid")
});
},
displayInGrid:function (jsonResult) {
var dataStore = new dojo.data.ItemFileReadStore(
{ data:jsonResult }
);
var checkboxLayout = [
[
{name:'ID', field:"id" },
{name:'Value', field:"id", formatter:this.addCheckBox}
]
];
var grid = dijit.byId("myGrid");
grid.setStructure(checkboxLayout);
grid.setStore(dataStore);
},
addCheckBox:function (val) {
var checkbox = "<input type='checkbox' name='myfields' value='" + val + "/>";
return checkbox;
},
If you are trying to show a checkbox selector on each row of the grid you can follow this tutorial
http://dojotoolkit.org/documentation/tutorials/1.8/working_grid/demo/selector.php
If the type of the cell is a boolean, then its value is displayed as either the string true or false. If a check box is desired, setting the cellType to be dojox.grid.cells.Bool and marking it as editable will make a checkbox appear.
http://dojotoolkit.org/reference-guide/1.9/dojox/grid/DataGrid.html#editing-cells
From markup, do like this for the desired result:
<th field="booleanField" cellType="dojox.grid.cells.Bool" editable="true">Checkbox field</th>

How to position a dijit.menu relative to its trigger?

I've got a couple menus like this:
// Contextual Menu
// triggers
<div id="contextMenuTrigger0">0</div>
<div id="contextMenuTrigger1">1</div>
// menu
<div dojoType="dijit.Menu"
targetNodeIds="contextMenuTrigger0, contextMenuTrigger1"
leftClicktoOpen="true" style="display:none">
<div dojoType="dijit.MenuItem" class="first">Item One</div>
<div dojoType="dijit.MenuItem">Item Two</div>
<div dojoType="dijit.MenuItem">Item Three</div>
<div dojoType="dijit.MenuItem">Item Four is really, really long item.</div>
</div>
and this:
// Tools Menu
// trigger
<div id="toolsButton">Tools</div>
// menu
<div dojoType="dijit.Menu" class="toolsMenu"
targetNodeIds="toolsButton"
leftClicktoOpen="true" style="display:none">
<div dojoType="dijit.MenuItem" class="first">Item One</div>
<div dojoType="dijit.MenuItem">Item Two</div>
<div dojoType="dijit.MenuItem">Item Three</div>
<div dojoType="dijit.MenuItem">Item Four</div>
</div>
Right now, when the menu opens, it appears under the mouse. I want it to appear in a specific position relative to the trigger*. I found the startup and onOpen events and tried writing a function that sets the style of the menu's domNode in there, but they didn't seem to take effect.
Also, I didn't see a way of finding out which node was the trigger in the context case where there are multiple ones.
I saw this & this, but wasn't able to get much further with 'em.
* FWIW, I want them positioned so that the top-left corner of the menu is aligned with the top-right corner of the context triggers, and with the bottom-left corner of the Tools menu.
I found the following css override works nicely, if you just want a relative difference in the automated positioning:
.dijitMenuPopup {
margin-left: -25px !important;
margin-top: 15px !important;
}
It turns out that dojo.popup.open (which I guess Menu inherits from) has a parameter (orient) that you can use to orient a menu relative to a node. I wound up defining a custom trigger class that knows how to take advantage of that. (I also created sub-classes for other menu-types that have different orientations, but I'll leave those out for clarity's sake.)
UPDATE: according to this page, the variable substitution method I was using in the templateString isn't recommended. Instead, you're supposed to create an attributeMap, which I've done below.
http://docs.dojocampus.org/quickstart/writingWidgets
// Define a basic MenuTrigger
dojo.declare("my.MenuTrigger", [dijit._Widget, dijit._Templated], {
// summary:
// A button that shows a popup.
// Supply label and popup as parameter when instantiating this widget.
label: null,
orient: {'BL': 'TL', 'BR': 'TR'}, // see http://api.dojotoolkit.org/jsdoc/1.3.2/dijit.popup.__OpenArgs (orient)
templateString: "<a href='#' class='button enabled' dojoAttachEvent='onclick: openPopup' onClick='return false;' ><span dojoAttachPoint='labelNode'></span></a>",
disabled: false,
attributeMap: {
label: {
node: "labelNode",
type: "innerHTML"
}
},
openPopup: function(){
if (this.disabled) return;
var self = this;
dijit.popup.open({
popup: this.popup,
parent: this,
around: this.domNode,
orient: this.orient,
onCancel: function(){
console.log(self.id + ": cancel of child");
},
onExecute: function(){
console.log(self.id + ": execute of child");
dijit.popup.close(self.popup);
self.open = false;
}
});
this.open = true;
},
closePopup: function(){
if(this.open){
console.log(this.id + ": close popup due to blur");
dijit.popup.close(this.popup);
this.open = false;
}
},
toggleDisabled: function() {
this.disabled = !this.disabled
dojo.toggleClass(this.domNode, 'buttonDisabled');
dojo.toggleClass(this.domNode, 'enabled');
dojo.attr(this.domNode, 'disabled', this.disabled);
},
_onBlur: function(){
// summary:
// This is called from focus manager and when we get the signal we
// need to close the drop down
// (note: I don't fully understand where this comes from
// I couldn't find docs. Got the code from this example:
// http://archive.dojotoolkit.org/nightly/dojotoolkit/dijit/tests/_base/test_popup.html
this.closePopup();
}
});
// create some menus & triggers and put them on the page
dojo.addOnLoad(function(){
// MENU
cMenu = new dijit.Menu();
cMenu.addChild(new dijit.MenuItem({ label: "First Item" }));
cMenu.addChild(new dijit.MenuItem({ label: "Second Item" }));
cMenu.addChild(new dijit.MenuItem({ label: "Third Item" }));
cMenu.addChild(new dijit.MenuItem({ label: "Fourth Item is truly a really, really, really long item" }));
// TRIGGER
cTrigger = new my.MenuTrigger({
id: "cTrigger",
popup: cMenu
}).placeAt(dojo.body());
cTrigger = new my.MenuTrigger({
id: "cTrigger2",
popup: cMenu
}).placeAt(dojo.byId('contextTriggerContainer2'));
});
As I can see from dijit.Menu source code the feature you want isn't supported out of box.
What I can think of is declaring a new widget inheriting from dijit.Menu and override bindDomNode method. It binds _openMyself handler to onClick event like this:
dojo.connect(cn, (this.leftClickToOpen)?"onclick":"oncontextmenu", this, "_openMyself")
_openMyself handler takes coords from the event object that comes in as an argument.
So the idea is to pass a fabricated event object with the desired coords.
dojo.connect(cn, (this.leftClickToOpen)?"onclick":"oncontextmenu", this, function(){
var e = { target: desiredTarget, pageX: desiredX, pageY: desiredY };
this._openMyself(e);
});