I'm creating a Dijit templated custom widget defined in a Html like this:
<div data-dojo-type="widgets/Test">Bla bla bla</div>
This is replaced with a template. For purpose of this example, let's say it's a simple div:
<div></div>
Now I have a problem. I can't seem to find a way to get this "Bla bla bla" (in other words, initial innerHTML) in a widget and I need it.
Any ideas?
If you are looking to modify the widget's template when the widget is being parsed, you can override your widget's markupFactory function. You can do this in your CustomWidget.js file as follows:
var CustomWidget = declare('path/to/CustomWidget', [], {
// Widget code here.
});
CustomWidget.markupFactory = function(props, node, clazz) {
// Modify the props defined in data-dojo-props.
// Modify the node that your widget is declared in.
// Don't forget to actually return the instance of your custom Widget!
return new clazz(props, node);
}
Related
I need to dynamically insert social embeds and in order to run scripts, I want to insert them into head, similar to how jQuery's .html() is doing. How would I go about writing component that I can inject into other components but will always reference head of document? I tried this:
#Decorator(
selector: 'head'
)
#Injectable()
class ScriptLoader {
Element element;
ScriptLoader(this.element) {
}
loadScript(String url) {
ScriptElement script = new ScriptElement();
script.src = url;
script.setAttribute("async", "");
script.onLoad.listen((Event e) {
script.remove();
});
element.append(script);
}
}
and when page is loaded, this.element is a reference to head but when it is injected into other component, it throws with error No provider found for Element! (resolving ExecuteBindHTML -> ScriptLoader -> ScriptLoader -> Element). How would I go about implementing such component? I don't want to use querySelectors (because Angular).
You can get the head element with querySelector directly in the constructor.
#Injectable()
class ScriptLoader {
Element element;
ScriptLoader() {
element = querySelector("head");
}
}
Then add the ScriptLoader class to the bootstrap method directly.
I would like to provide a possibility to show my components in a bit different look and feel and thought using the decorator for it. Something like:
<body>
<my-component my-decorator></my-component>
</body>
.
#Component(
selector: 'my-component',
templateUrl: '.../my-component.html',
cssUrl: '.../my-component.css',
publishAs: 'comp',
)
class MyComponent {
MyComponent(final Element element) {
Logger.root.fine("MyComponent(): element = $element, element.attributes = ${element.attributes.keys}");
}
}
#Decorator(selector: '[my-decorator]')
class MyDecorator {
final Element element;
#NgOneWay('my-decorator')
var model; // is not used
MyDecorator(this.element) {
Logger.root.fine("MyDecorator(): element = $element, element.nodeName = ${element.nodeName}");
Logger.root.fine("MyDecorator(): element.shadowRoot = ${element.shadowRoot}, element.parent = ${element.parent}");
}
}
Unfortunately, it seems that my-decorator is processed before my-component so it is getting null shadowRoot property in the injected Element object.
It would be possible to check on existence of the my-decorator attribute within the my-component backing class, but that is clearly polluting the design.
UPDATE: Thanks to replay from Marko Vuksanovic, the following is now returning the :
#Decorator(selector: '[my-decorator]')
class MyDecorator extends AttachAware {
final Element element;
#NgOneWay('my-decorator')
var model; // is not used
MyDecorator(this.element) {
Logger.root.fine("MyDecorator(): element = $element, element.nodeName = ${element.nodeName}");
Logger.root.fine("MyDecorator(): element.shadowRoot = ${element.shadowRoot}, element.parent = ${element.parent}");
}
void attach() {
Logger.root.fine("attach(): element.shadowRoot = ${element.shadowRoot}");
}
}
The question still remains how to modify the styling of the shadow DOM.
Thanks in advance for any comments/ideas/solutions.
You can try using AttachAware and it's attach method. You should implement AttachAware interface in your decorator and/or component.
Here's link to Angular.dart docs - https://docs.angulardart.org/#angular-core-annotation.AttachAware
To change the styling of a ShadowDom component you can use element.shadowRoot to get the root of your web component. Shadow root is almost like 'document' object. You can use shadow root to get reference to any element and then you can easily modify it by applying styles as needed.
You could use something like
this.element.shadowRoot.querySelector('[some-attr]').innerHtml = "Modified by decorator" // disclaimer: not tested, but I hope you get the idea.
You can add a style tag to the shadowDom programmatically:
shadowRoot.append(new StyleElement()..text = ':host{background: red;}');
or
shadowRoot.append(new StyleElement()..text = "#import url('some.css')");
I have a custom element like below:
<polymer-element>
<template if="{{primaryLoaded}}">
<template repeat="{{pData in primaryData}}">
<p>{{pData.propertyOne}}</p>
<p>{{someClass.someOperation()}}</p>
<template if="{{secodaryLoaded}}">
<p>{{secondaryData.someProperty}}</p>
<p>{{someClass.someOperation()}}</p>
</template>
</template>
</template>
</polymer-element>
and a corresponding dart file:
class CustomElement extends PolymerElement with ObservableMixin
{
#observable bool primaryLoaded = false;
#observable bool secondaryLoaded = false;
#observable var primaryData;
#observable var secondaryData;
#observable var someClass;
void created()
{
primaryData = toObservable(new List<var>());
secondaryData = toObservable(new List<var>());
}
void inserted()
{
someClass = new SomeClass();
loadPrimaryData().then((pData) {
primaryData = pData;
primaryLoaded = true;
loadSecondaryData().then((sData) {
secondaryData = sData;
secondaryLoaded = true;
});
});
}
}
Everything works fine for the primaryData. Its properties get printed and the call to someOperation() on someClass prints its data correctly.
The problem is in the nested template. Nothing under the <template if="{{secondaryLoaded}}"> gets displayed. Even the call to someClass.someOperation() fails to display anything.
Is there a problem with scope here? It looks like the outer template can reference the properties defined in the dart file without problem but the nested template can't.
I read about setting variables as globals in the template by setting a custom binding delegate here. However, I can't set the bindingDelegate in my custom element as isTemplate returns false.
Is there any other way of setting a global variable in a custom template? Or am I going about this all wrong?
It's a bug. You can star this issue to be notified of changes:
https://code.google.com/p/dart/issues/detail?id=12742
The only thing you can do for now is to turn nested templates into other polymer elements or figure out a way so the template isn't nested.
In your case, instead of using if="primaryLoaded" you could just set primaryData = null and the template shoud not display anything until primaryData is set, assuming primaryData is #observable.
We're currently looking at translating our JavaScript project to TypeScript. Our application relies heavily on custom developed jQuery UI widgets.
In our current code base, we're using a deep copy mechanism to inherit from widget definitions allowing us, for example, to declare a generic TableWidget as well as an OrdersTableWidget which defines more specific functions.
Therefore, I'd like to define my widget definitions as TypeScript classes and then bind an instance of these classes to jQuery.
For example
class MyWidget {
options: WidgetOptions;
_init(){
// general initialization
}
}
class MySecondWidget extends MyWidget {
_init(){
super._init();
// specific initialization
}
}
And then
$.widget("MyNameSpace.MyWidget", new MyWidget());
$.widget("MyNameSpace.MySeWidget", new MyWidget());
Furthermore, I'd like to denote my custom widgets as implementations of jQuery UI's Widget definition
class MyWidget implements Widget {
options: WidgetOptions;
_init(){
// general initialization
}
}
so I'm able to use the following syntax in TypeScript:
$(selector).MyWidget(options);
I know I have to work with the definition file (from DefinitelyTyped), however I have not yet found a reliable source explaining me how I should write custom jQuery UI Widgets in TypeScript. Has anyone got experience with this?
Any help greatly appreciated, as always!
I'm not sure you can write a class that implements the Widget interface, due to the lack of overloaded constructors. You could create a variable that is typed by the Widget interface.
A standard jQuery plugin would be represent in almost pure JavaScript and wouldn't use modules or classes as it ends up being wrapped up as part of jQuery, which itself isn't a module or class.
Here is an empty plugin called plugin that looks like any standard jQuery plugin, but you can see it takes advantage of the TypeScript type system and extends the JQuery interface to allow it to be called.
/// <reference path="jquery.d.ts" />
interface JQuery {
plugin(): JQuery;
plugin(settings: Object): JQuery;
}
(function ($) {
function DoSomething(someParamater: string) : void {
}
$.fn.plugin = function (settings) {
var config = {
settingA: "Example",
settingB: 5
};
if (settings) {
$.extend(config, settings);
}
return this.each(function () {
});
};
})(jQuery);
This would be called in the normal way.
$('#id').plugin();
So really, my answer is - you can't really do what you want because you are adding to the declared interfaces for jQuery rather than exposing them as modules. You could wrap the usage in a module, like an adaptor that abstracts the jQuery aspect away from the use in your TypeScript, or you can call your classes from inside the plugin, but the plugin or widget doesn't really fit into a module or class.
It might help to have a base class in typescript from which other widget classes may derive.
Its only purpose is to provide the base class semantic so you can access the base class'es members without having to resort to weak typing.
The trick is to remove all the members at runtime (in the constructor) -- otherwise you run into problems with the inheritance provided by the widget factory. For example, the option method would override the widget's original method which is not desired: we just want to be able to call it (in a statically typed way).
class WidgetBase {
public element:JQuery;
constructor() {
// remove all members, they are only needed at compile time.
var myPrototype = (<Function>WidgetBase).prototype;
$.each(myPrototype, (propertyName, value)=>{
delete myPrototype[propertyName];
});
}
/**
* Calles the base implementation of a method when called from a derived method.
* #private
*/
public _super(arg1?:any, arg2?:any, arg3?:any, arg4?:any) {
}
/**
* #private
*/
public _superApply(arguments) {
}
/**
* Gets or sets the value of the widget option associated with the specified optionName.
*/
public option(optionName:string, value?:any):any {
}
// ... further methods from http://api.jqueryui.com/jQuery.widget/
}
Then you can implement your own widget like this:
class SmartWidget extends WidgetBase {
constructor(){
super();
}
public _create() {
var mySmartOption = this.option('smart'); // compiles because of base class
this.beSmart(mySmartOption);
}
public _setOption(key:string, value:any) {
if (key === 'smart') {
this.beSmart(value);
}
this._super(key, value); // compiles because of base class
}
private beSmart(smartOne:any){
// ...
}
}
// register
jQuery.widget("myLib.smartWidget", new SmartWidget());
// assuming you are using https://github.com/borisyankov/DefinitelyTyped
declare interface JQuery{
smartWidget();
smartWidget(options:any);
smartWidget(methodName:string, param1?:any, param2?:any, param3?:any, param4?:any);
}
And finally, you can use your widget:
$(".selector").smartWidget({smart:"you"});
The taglib provided by the Grails bean fields plugin uses a naming convention to determine the label key that should be used for each <input> element it generates. I would like to change the details of this convention without changing the plugin's source code directly.
The approach I'm considering is to create my own tag lib
class MyBeanTagLib {
static namespace = 'mybean'
private void setLabelKey (attrs) {
if (!attrs.labelKey) {
// in reality calculation of the default key is a bit more complicated :)
attrs.labelKey = 'my.default.key'
}
return attrs
}
// renders a combo box
def select = { attrs ->
attrs = setLabelKey(attrs)
// Now call the bean-fields select tag, passing along attrs
}
// renders a datePicker
def date = { attrs -
attrs = setLabelKey(attrs)
// Now call the bean-fields date tag, passing along attrs
}
}
My first question is how to invoke the tag I'm trying to decorate. In other words, what code should replace the comment
// Now call the bean-fields...
I could do this:
new BeanTagLib().select(attrs)
But I doubt this is the correct way to invoke one taglib from another.
Secondly, is there a more elegant way to decorate a taglib than this? In reality there are a lot more tags than just select and date that I need to decorate and the code in each decorating tag will be almost identical. I'd like to eliminate this duplication if possible?
Invoke other taglibs' tags by their namespace, like g.link([controller: 'one'], { 'link text' }), or bean.select(attrs).
You can try writing getProperty in your taglib and return proper closures - I'm not sure.