Instantiate WebComponent "easily" - javascript

I have created a simple Web Component to add in a parent (say "div#left" in this example). The component do nothing, except showing a text (it will be more elaborate later).
It's working if I do:
in html
<my-comp text="TEST"></my-comp>
in JS
document.getElementById("left").innerHTML += '<my-comp text="bar"></my-comp>';
or
var c = document.createElement("my-comp");
c.setAttribute("text", "buzz");
document.getElementById("left").appendChild(c);
But, I want something more "easy" to instantiate it (more "natural" for me...), via a method like
const gb = new MyComp({ text: "foo" });
gb.addInParent("#left")
or via a generic function (to instantiate any component), like:
const gb = new MyComp({ text: "foo" });
addInParent("#left", gb)
It may be simple, but I can't find how to implement the method/function addInParent... (all my research leads me to React or equivalent, which I don't use for this specific case)
Thank's in advance

I'm not sure if i got it right but you only want to change de inner part of the component to the received prop, right?
Have you tried slots?
https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_templates_and_slots
for example in your web component render method
<div>
<slot>Slotted text</slot>
</div>
and then in your page you call your component and pass the text like
<my-comp>My text here</my-comp>

Related

bracket notation does not work with aframe

When using A-Frame, it not not possible to access component named with a dash, like "orbit-controls".
I am trying to access aframe component "orbit-controls". Link below:
aframe-orbit-controls component
since this component's minAzimuthAngle and maxAzimuthAngle is not working so I have to access its source to use script to change it. But when I tried to access it, I cannot use
var componentAngle = el.components['orbit-controls'];
to get the component and it returns undefined. When I log
var componentAngle = el.components
, it returns:
So how can I access this "orbit-controls"? I also tried
var getAngle = el.getAttribute('orbit-controls');
which returns
and these are only numbers and changing them wont change the real minAzimuthAngle. So I am wondering if there is a way to access the property showed in the first image? Very much appreciated.
Below is the code link.
try to access "orbit-controls" component
You should wait until the entity is fully loaded before grabbing this.el.components['orbit-controls']:
this.el.addEventListener('loaded', e => {
console.log(this.el.components['orbit-controls']
})
The azimuth component is added before orbit-controls so when the first one is being initialized, the latter might not be ready yet.
Fiddle here.

create multiple vue-elements of the same component in js-script

my problem is the following:
I want to create some vue elements like this:
var rRow = document.createElement('rightrow');
rightRow is a vue Element and should be importet like this:
<rightRow></rightRow>
As I look into Chrome Developer Tools, the HTML code looks as it shoukd be, but the vue element isn't there. If I insert it 'by hand'(just write where it should be) it works, but I need it more often. Thanks to every helping hand :D
Sorry if my english isn't the best. I'm no native speaker xD
EDIT:
In my Chrome-Developer-Tools the component can be seen. I just need to know how I can render it again because the text is right but the Vue-View isn't
Vue uses the ES2015 class sytax and can instantiated via their constructors.
import RightRow from "./RightRow.vue"
const rrow = new RightRow();

RiotJS - How to pass events between subtags using Observable pattern?

Im not really sure if Im understanding correctly the way observables work and how to get references from mounted tags. I have a component. Within this component we have a component and a component. The purpose is to avoid coupling between components. Because of that, I would like that my search component triggers an event when a search is done(a button is clicked). This event should be caught by the component which will filter the collection data based on the search.
The index.html file load the tag by using:
index.html
riot.mount(".content", "page", null);
The page is defined as follow:
page.js
<page>
<!-- Search tag controls -->
<search id="searchTag"></search>
<!-- Collection data to display -->
<collection id="collectionTag"></collection>
</page>
The component script is briefly defined like:
search.js
var self = this;
riot.observable(self);
<!-- This function is called when the user click on the button. -->
self.filtering = function()
{
<!-- We get data from inputs -->
var info = Getting data from inputs;
<!-- Trigger the event hoping that someone will observe it -->
self.trigger("filterEvent", info);
}
How can I make the component observe for that event?
To me it seems that I should be able to get references from search tag and collection tag in the page.js. By doing so I could connect the events like follow:
searchComponent = riot.mount('search');
collectionComponent = riot.mount('collection');
searchComponent.on('filterEvent', function()
{
<!-- Trigger function to filter collection data -->
collectionComponent.trigger('filterData');
});
Right now I cannot make it work like that.
At the point of execution, searchComponent and collectionComponent are not defined.
I tried also getting references of these component by using this.searchTag and this.collectionTag instead of mounting them but at the time the code is executed, the components have not been mounted and so I dont get a reference to them.
Any ideas to make it work?
Inspired by the answer given by #gius, this is now my preferred method for sending events in RiotJS from one tag to another.. and it is great to work with!
The difference from #gius approach being that, if you use a lot of nested tags, passing a shared Observable to each tag falls short, because you would need to pass it again and again to each child tag (or call up from the child tags with messy this.parent calls).
Defining a simple Mixin, like this (below), that simply defines an Observable, means that you can now share that in any tag you want.
var SharedMixin = {
observable: riot.observable()
};
Add this line to your tags..
this.mixin(SharedMixin);
And now, any tag that contains the above line can fire events like..
this.observable.trigger('event_of_mine');
..or receive events like this..
this.observable.on('event_of_mine',doSomeStuff());
See my working jsfiddle here http://jsfiddle.net/3b32yqb1/5/ .
Try to pass a shared observable to both tags.
var sharedObservable = riot.observable();
riot.mount('search', {observable: sharedObservable}); // the second argument will be used as opts
riot.mount('collection', {observable: sharedObservable});
And then in the tags, just use it:
this.opts.observable.trigger('myEvent');
this.opts.observable.on('myEvent', function() { ... });
EDIT:
Or even better, since your search and collection tags are child tags of another riot tag (page) (and thus you also don't need to mount them manually), you can use the parent as the shared observable. So just trigger or handle events in your child tags like this:
this.parent.trigger('myEvent');
this.parent.on('myEvent', function() { ... });
Firstly I do not understand your file structure !
In your place I would change filenames :
page.js --> page.tag
search.js --> search.tag
And i dont see your search tag in search.js code.
So I dont see your Collection tag file ...
Are you sure that this one use this code ?
riot.observable({self|this});
Because it's him who will receive an Event.
For me when I use Riot.js(2.2.2) in my browser, if I use
searchComponent = riot.mount('search');
searchComponent will be undefined
But with this code you can save your monted tag reference :
var searchComponent ={};
riot.compile(function() {
searchComponent = riot.mount('search')[0];
});
Another option is to use global observables, which is probably not always best practice. We use Riot's built in conditionals to mount tags when certain conditions are met rather than directly mounting them via JS. This means tags are independent of each other.
For example, a single observable could be used to manage all communication. This isn't a useful example on its own, it's just to demonstrate a technique.
For example, in a plain JS file such as main.js:
var myApp = riot.observable();
One tag file may trigger an update.
var self = this;
message = self.message;
myApp.trigger('NewMessage', message);
Any number of other tag files can listen for an update:
myApp.on('NewMessage', function(message) {
// Do something with the new message "message"
console.log('Message received: ' + message);
});
Maybe overkill but simple. let riot self observable
riot.observable(riot);
So you can use
riot.on('someEvent', () => {
// doing something
});
in a tag, and
riot.trigger('someEvent');
in another.
It's not good to use global variable, but use an already exists one maybe acceptable.

Pass another controller when instantiating a fragment in SAPUI5

In the SAPUI5 / OpenUI5 xmlfragment documentation the third parameter is a controller for handling actions from the fragment.
This is critical for a dialog fragment where there are buttons to press etc.
Most of the time I have seen this instantiated as this or sap.ui.getCore().byId('<element>').getController())
See an example at Fragment not get correct Controller
Because of the complexity in a particular dialog I would like to have a separate controller for it.
I have looked around at this and had a few attempts but so far not successful.
I have put a working example on github of using this.
But I would like to instantiate Dialog.js as the controller for the Dialog.fragment.xml from initial.view.controller
Any takers?
Pull requests gladly received.
The Crux of the example is as follows (this is the initial.controller.js) :
sap.ui.controller("sc.test.view.initial", {
oDialog: null,
openTestDialog: function(){
console.log("in open dialog");
// instantiate the other controller
var oDialogController = new sc.test.view.Dialog();
// this next commented line is the 'normal' way to do it
// oDialog = new sap.ui.xmlfragment( "sc.test.view.Dialog", this); //oDialogController);
// this is what I would like to achieve
oDialog = new sap.ui.xmlfragment( "sc.test.view.Dialog", oDialogController);
oDialog.open();
},
onCartDialogCancel:function(oEvent){
// this function would then be in the other controller but how to get a handle on the dialog?
oDialog.close();
}
});
Thanks.
(Just got to SYD airport)
All you're missing is the
jQuery.sap.require("sc.test.view.Dialog");
in your initial.controller.js.
Pushed a quick fix in a branch to your repo and opened a PR
only example i could find close to yours was in the Material Shortage Fiori app
oCtrl = sap.ui.controller("myapp.fragments.DirectCallDialog");
oDirectCallDialog = sap.ui.xmlfragment("myapp.fragments.DirectCallDialog", oCtrl);
lots of examples of injecting a controller when the fragment was called from a helper class. The helper class promotes reuse, eg same dialog fragment can be called from multiple views/components. The helper class method for the dialog setup is called from within a controller and the oController parameter is set to 'this'.
hth
jsp
I copied an existing controller.js, and renamed it.
Then, instantiated that as a below, and passed it through with the fragment.
var oNewController = new sap.ui.core.mvc.Controller("myProject.DialogController");
this._oDialog = sap.ui.xmlfragment("myPopup","myProject.fragments.myPopup", oNewController);
All eventing is now handled in oNewController, rather than the previously used "this"...

Wicket + Javascript

I'm wrapping up a Javascript widget in a Wicket component. I want to let the JS side talk to the component. What I've got so far:
Component in question goes like
talker = new GridAjaxBehavior();
this.add(talker);
in constructor
and then, later on, puts something like
"var MyGridTalker = new talker(" + this.talker.getCallbackUrl() + ");";
into the JS.
where GridAjaxBehavior extends AbstractDefaultAjaxBehavior. I want GridAjaxBehavior to spit back some XML when the JS calls it.
Am I doing this the right way? What should GridAjaxBehaviour do to spit back the XML?
Thanks
Spit back some XML for what? Presumably to update the model or the view, yes?
The strength of Wicket is that you don't have to worry about the rendered HTML. In Model-View-Controller terms, you set up the Controller to correctly modify the Model, and Wicket takes care of the View.
The separation is not entirely clear: in fact you can show/hide view components, or change then, and that can be seen as altering the View.
But what you generally don't have to do is directly manage the browser or javascript. Wicket takes care of that, if you take care of making your changes in the Java code.
In Wicket, the Ajax will call a method on your AjaxBehavior with an AjaxRequestTarget target.
In that method (or in methods called from it), you do whatever you need to do, updating models or views, and then you add to the target any view component that that has changed. Wicket takes care of updating the browser.
Here's an example. It's taken from some code I did, but heavily altered just to make explication clearer. The idea is simple: "chained" dropdown choices, where the options in the child change when the select option in the parent changes, as in the series of [State] [County] [District].
(In the actual class, the Model change is passed to the child, which decides for itself if it has changed, and adds itself to the target if it has, then passes the target to its child. I've removed most of that to make a clearer example.)
Here's the ctor, which just adds to itself an anonymous subclass of an AjaxBehavior:
public AjaxChildNotifyingDropDownChoice(...code elided for clarity...) {
this.child = child;
// Ajax won't work without this:
setOutputMarkupId(true);
//
add( new OnChangeAjaxBehavior() {
#Override
public void onUpdate(final AjaxRequestTarget target) {
// tell child to update its list
// based on newly selected value
// when the Ajax is called,
// my owning component's model
// is already updated
// note we could just type getModel()
// I'm making explicit that we're calling it
// on the enclosing class
// (which a non-static inner class has a hidden ref to)
child.setNewModelBasedOnSelectionOf(
AjaxChildNotifyingDropDownChoice.this.getModel());
// now add the child to the target
// Wicket javascript will receive the new
// options and re-render the child dropdown
target.add(child);
}
});
}
We could also have hidden or un-hidden components, or added behaviors like CSS styles, or even swapped one Panel for another. As long as for each changed component we:
1) called setOutputMarkupId(true); so that the javascript can find it, and
2) added it to the AjaxRequestTarget
Note that different types (subclases) of Ajax Behavior have different callback functions, so be sure you're overriding the right one (add an #Override annotation so the compiler can complain if you got the name wrong).
But again, the basic wicket idea is that instead of sending raw data for the client to parse and act on, you update your model and view, and tell Wicket to re-render what you've changed, by adding the chnaged components to the target.
The only reason I can think of to send straight XML would to be to feed it to non-Wicket javascript. Let me know if that's your aim, and I completely missed the point. ;)
I don't really know what Wicket is or what it does, but there is a minor bug in your code (as it appears).
This:
"var MyGridTalker = new talker(" + this.talker.getCallbackUrl();
You seem to be missing your end parens:
"var MyGridTalker = new talker(" + this.talker.getCallbackUrl() + ")";
Anyway, not a big deal, but didn't know if it was intentional.

Categories