How to extend dijit.form.button - dojo

I am trying to extend dijit.form.Button with an extra attribute but this is not working.Code is given below
In file1.js
dojo.require('dijit.form.Button');
dojo.extend(dijit.form.Button,{xyz: ''});
In file2.jsp
<script type="text/javascript" src="file1.js"></script>
<div dojoType="dijit.form.Button" xyz="abc"></div>
However when I look at the HTML of the created button (In chrome seen by right click and then selecting 'inspect element' option), it doesn't show xyz attribute.

You need to keep in mind that there's a distinction between the widget object and its HTML representation. When you extend dijit.form.Button, the xyz attribute is added to the widget class, but not automatically to the HTML that the widget will render. So in your case, if you do
console.debug(dijit.byId("yourWidgetId").get("xyz"));
.. you'll see that the button object does have the xyz member, but the HTML (like you point out) does not.
If you also want it do be visible in the HTML, you have to manually add it to the HTML rendering of the button. One way to do that is to subclass dijit.form.Button and override the buildRendering method.
dojo.declare("my.Button", dijit.form.Button, {
xyz: '',
buildRendering: function() {
this.inherited(arguments);
this.domNode.setAttribute("xyz", this.xyz);
}
});
If you add an instance of your new Button class in the HTML, like so:
<div dojoType="my.Button" xyz="foobar" id="mybtn"></div>
.. then the HTML representation (after Dojo has parsed it and made it into a nice looking widget) will contain the xyz attribute. Probably something like this:
<span class="..." xyz="foobar" dir="ltr" widgetid="mybtn">
<span class="..." dojoattachevent="ondijitclick:_onButtonClick">
<input class="dijitOffScreen" type="button" dojoattachpoint="valueNode" ...>
</span>

Related

DebugElement.query does not work with elements added dynamically to the dom in a spec

I have an app that is using ngx-bootstrap to show a tooltip on mouseover. I want to test that the content, which is dynamically added, shows properly. In order to do this I have a test that looks like this:
it(shows the right tooltip', fakeAsync(() => {
fixture.debugElement.query(By.directive(TooltipDirective))
.triggerEventHandler('mouseover', null);
tick();
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('.tooltip-inner')).nativeElement)
.toBe('the tooltip text');
}
This results in an error that indicates that fixture.debugElement.query(By.css('.tooltip-inner')): "Cannot read property 'nativeElement' of null"
If I print out the content of fixture.debugElement.nativeElement I get this:
<div id="root1" ng-version="5.2.9">
<my-component>
<div ng-reflect-tooltip="the tooltip text">
<img src="images/test.png">
</div>
<bs-tooltip-container role="tooltip" class="tooltip in tooltip-right">
<div class="tooltip-arrow arrow"></div>
<div class="tooltip-inner">the tooltip text</div>
</bs-tooltip-container>
<my-component>
</div>
The important take away is that the html exists - it is just not accessible by the DebugElement.query.
My current solution to get the spec passing is to change the expect to:
expect(fixture.debugElement.nativeElement.textContent.trim())
.toBe('the tooltip text');
This works, but it is a hack that will fall to pieces if I run into a similar situation with multiple tooltips (for example). Has anyone been able to handle this in a better way? Am I not setting this spec up correctly?

angular2 bootstrap4 tooltip doesn't render html, while popover does

I'm using angular2 and bootstrap4. Popover correctly renders raw html as bold text asdf
<img src="assets/images/1.jpg"
data-container="body" data-toggle="popover" data-html="true" data-placement="top"
[attr.data-content]="getM()"/>
However, tooltip renders as plain <b>asdf</b> text including tags
<img src="assets/images/2.jpg"
data-container="body" data-toggle="tooltip" data-html="true" data-placement="top"
[attr.title]="getM()"/>
Component method getM:
public getM(): string {
return '<b>asdf</b>';
}
Both tooltip and popover are initialized the same way
$(function () {
$('[data-toggle="tooltip"]').tooltip({container: 'body'});
$('[data-toggle="popover"]').popover({container: 'body'});
})
Could someone explain why is that and how to solve? It seems this is connected with initialization order, but I just don't know where to look further.
Well, the issue was that my element (which tooltip was attached to) was created dynamically.
In exact, I had a 1 sec delayed service. When new data arrived, the <img> element in my component was recreated, but $('[data-toggle="tooltip"]') selector doesn't work with dynamic elements.
Instead, I had to use this selector
$("body").tooltip({selector: '[data-toggle="tooltip"]'});
Now it works as intended.
PS I'm not a front-end developer, so anyone who can explain it in better terms is welcome.

Change element type at runtime

Is it possible to dynamically define the type of an element inside a custom components template at runtime?
I'd like to avoid duplication of the inner contents of the button and a element in the following example:
<template>
<button if.bind="!isLinkBtn">
<span class="btn-icon">${icon}</span>
<span class="btn-text">${contentText}</span>
</button>
<a if.bind="isLinkBtn">
<!--
The content is a 1:1 duplicate of the button above which should be prevented
somehow in order to keep the view DRY
-->
<span class="btn-icon">${icon}</span>
<span class="btn-text">${contentText}</span>
</a>
</template>
Is it possible to write something like this:
<template>
<!--
The type of element should be defined at runtime and can be a standard HTML "button"
or an anchor "a"
-->
<element type.bind="${isLinkBtn ? 'a' : 'button'}">
<span class="btn-icon">${icon}</span>
<span class="btn-text">${contentText}</span>
</element>
</template>
I'm aware of dynamic composition with <compose view="${widget.type}-view.html"></compose> but as far as I know, this won't allow me to create default HTML elements but only custom components, correct?
I've asked this question on the Aurelia Gitter where Erik Lieben suggested to use a #processContent(function) decorator, replace the content within the given function and return true to let Aurelia process it.
Unfortunately I don't know how to actually apply those instructions and am hoping for some alternative approaches here or some details about how to actually accomplish this.
Edit
I've created a corresponding feature request. Even though possible solutions have been provided, I'd love to see some simpler way to solve this ;)
When you want to reuse HTML snippets, use compose. Doing so does not create a new custom element. It simply includes the HTML at the location of each compose element. As such, the view-model for the included HTML is the same as for the element into which it is composed.
Take a look at this GistRun: https://gist.run/?id=36cf2435d39910ff709de05e5e1bedaf
custom-link.html
<template>
<button if.bind="!isLinkBtn">
<compose view="./custom-link-icon-and-text.html"></compose>
</button>
<a if.bind="isLinkBtn" href="#">
<compose view="./custom-link-icon-and-text.html"></compose>
</a>
</template>
custom-link.js
import {bindable} from 'aurelia-framework';
export class CustomLink {
#bindable() contentText;
#bindable() icon;
#bindable() isLinkBtn;
}
custom-link-icon-and-text.html
<template>
<span class="btn-icon">${icon}</span>
<span class="btn-text">${contentText}</span>
</template>
consumer.html
<template>
<require from="./custom-link"></require>
<custom-link content-text="Here is a button"></custom-link>
<custom-link is-link-btn.bind="true" content-text="Here is a link"></custom-link>
</template>
You may want to split these into separate elements, like <custom-button> and <custom-link> instead of controlling their presentation using an is-link-btn attribute. You can use the same technique to reuse common HTML parts and composition with decorators to reuse the common code.
See this GistRun: https://gist.run/?id=e9572ad27cb61f16c529fb9425107a10
Response to your "less verbose" comment
You can get it down to one file and avoid compose using the techniques in the above GistRun and the inlineView decorator:
See this GistRun: https://gist.run/?id=4e325771c63d752ef1712c6d949313ce
All you would need is this one file:
custom-links.js
import {bindable, inlineView} from 'aurelia-framework';
function customLinkElement() {
return function(target) {
bindable('contentText')(target);
bindable('icon')(target);
}
}
const tagTypes = {button: 'button', link: 'a'};
#inlineView(viewHtml(tagTypes.button))
#customLinkElement()
export class CustomButton {
}
#inlineView(viewHtml(tagTypes.link))
#customLinkElement()
export class CustomLink {
}
function viewHtml(tagType) {
let result = `
<template>
<${tagType}${tagType === tagTypes.link ? ' href="#"' : ''}>
<span class="btn-icon">\${icon}</span>
<span class="btn-text">\${contentText}</span>
</${tagType}>
</template>
`;
return result;
}
Sorry, I was doing 2 things at once while looking at gitter, which I am not good at apparently :-)
For the thing you wanted to accomplish in the end, could this also work?
I am not an a11y expert or have a lot of knowledge on that area, but from what I understand, this will accomplish what you want. The browser will look at the role attribute and handle it as a link or button and ignores the actual element type itself / won't care if it is button or anchor it will act as if it is of the type defined in role.
Then you can style it like a button or link tag with css.
<a role.bind="type"><span>x</span><span>y</span></a>
where type is either link or button, see this: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_link_role

vue.js - Change text based on default/clicked class

Given the following:
<div id="#my-container">
<div class="title">Companies</div>
<div class="tab active tab-apple">Apple</div>
<div class="tab tab-google">Google</div>
</div>
When page is loaded without any tab clicks yet, whichever tab with the default active class, needs to go in the .title div. For the example above, <div class="title">Apple</div>
On click of a tab, the class is switched to active, and vue.js needs to update the .title div once again.
How can this be done with vue.js? I've tried but not able to get it to work as intended.
The answer by David is one way to do it. But Vuejs offers in-line computations for this. So, no need to hook into any CSS event. Here's some code to explain:
Create a data property active_tab, just like David mentioned. And then bind it's value just like he's done it. In your tabs, add an click event and at that event, assign appropriate value to active_tab.
<div class="tab active tab-apple" #click="active_tab = Apple">Apple</div>
<div class="tab tab-google" #click="active_tab = Google">Google</div>
Now, to dynamically assign the active class to the respective tab, make the class attribute, a computed property, like this:
<div
:class="['tab', active_tab == 'Apple' ? 'active' : '', 'tab-apple']"
>
Apple
</div>
What this code is basically doing is, :class makes class a computed property. Then the commas in the array divide the statement. So, the computation will always add tab and tab-apple classes. But, only if active_tab == 'Apple' then ? add 'active' else : add ''
Not sure which CSS framework you are using, but normally I hook into the events thrown by the tab switching (many CSS frameworks provide this access). Once hooked into it, you can write a Vue custom directive that will take that event and use it to update a VM attribute that indicates which tab is active.
Then you can use normal mustache templating to get it into your template:
<div class="title">{{ active_tab }}</div>

JavaScript .innerHTMLworking only when called manually

I've got a very simple function, of replacing the innerHTML of a element. I've been trying to debug this for hours but simply can't, and it's infuriating.
When called from a button press the JavaScript (as follows) works well, but when called from another function it doesn't work. I am totally lost as to why this might be, and its a fairly core part of my app
// This loaded function in my actual code is a document listener
// checking for when Cordova is loaded which then calls the loaded function
loaded();
function loaded() {
alert("loaded");
changeText();
}
function changeText() {
alert("started");
document.getElementById('boldStuff').innerHTML = 'Fred Flinstone';
}
Button press and HTML to replace
<div id="main">
<input type='button' onclick='changeText()' value='Change Text'/>
<p>Change this text >> <b id='boldStuff'> THIS TEXT</b> </p>
</div>
It is also here in full on JSFiddle
You are already changed the innerHTML by calling the function loaded(); on onLoad.
Put this in an empty file and same as .html and open with browser and try. I have commented the function loaded();. Now it will be changed in onclick.
<div id="main">
<input type='button' onclick='changeText();' value='Change Text'/>
<p>Change this text >> <b id='boldStuff'> THIS TEXT</b> </p>
</div>
<script>
//loaded();
function loaded() {
alert("loaded");
changeText();
}
function changeText() {
alert("started");
document.getElementById('boldStuff').innerHTML = 'Fred Flinstone';
}
</script>
The problem here is, that the element you're trying to manipulate is not yet existing when you are calling the changeText() function.
To ensure that the code is only executed after the page has finished loading (and all elements are in place) you can use the onload handler on the body element like this:
<body onload="loaded();">
Additionally you should know, that it's very bad practice to manipulate values by using the innerHTML property. The correct way is to use DOM Manipulations, maybe this can help you.
You script loads before the element (boldStuff) is loaded,
Test Link - 1 - Put the js in a seperate file
Test Link - 2 - put the js at the very end, before closing the <body>