have a custom module with a custom observer. i also added a disable/enable option for this functionality to a store config value for admin backend.
is there a way of completely disabling the observer if the store config value is disabled thorugh a xml file for example? right now i only have an if statment after the execute function of the observer so the functions inside the observer are not executed when the backend config is set to disabled.
i guess it would be more elegant to disable the observer completely if the config value in backend is set to disabled?
i hope this makes sense. thanks for any help
As far as I know, the way you mentioned is the only way to disable an observer
public function execute(\Magento\Framework\Event\Observer $observer)
{
if($disabled) {return;}
//your code here
}
Related
I would like to create a plugin mechanism for my custom wrapper around AgGrid. Currently I am doing so with an interface like this:
export interface IGridPlugin {
onGridReady: (e: GridReadyEvent) => void;
}
Then, in my wrapper around AgGrid, I add an onGridReady event handler that goes through the supplied list of plugins and invokes their onGridReady method one at a time. They can then perform any initialization that they require - like adding event handlers via e.api.addEventListener
gridOptions.onGridReady = (e: GridReadyEvent) => {
plugins?.forEach(plugin => plugin.onGridReady(e));
}
Unfortunately, the onGridReady event appears to only fire after the grid initially loads and renders. One of the plugins I've created will restore the grid state from url params when onGridReady fires, and will subsequently keep the url params updated as the grid state changes.
Since onGridReady fires after the initial rendering, there's a small delay where the grid is displayed without the application of the preserved grid state in the url. I was hoping to find an earlier event that might make more sense for this.
Alternatively, I suppose I could allow the plugins to modify the gridOptions directly before the grid is even constructed... but using events feels a little safer - preventing one plugin from setting itself up as the sole event handler for something that other plugins may want to use and accidentally disable some/all of the functionality of other plugins.
Is there a better event or alternative mechanism for adding such plugins to AgGrid? I would like to make sure that the plugins have access to the api and columnApi instances in any approach taken.
We are working on tracking a site which has components built using Shadow DOM concepts, when we are creating a rule in launch to add tagging to these components it’s not working.
Can you guide us with best practice on tagging components in Shadow DOM?
I found unanswered questions about google analytics Google analytics inside shadow DOM doesn't work is this true for adobe analytics also?
Best Practice
Firstly, the spirit of using Shadow DOM concepts is to provide scope/closure for web components, so that people can't just go poking at them and messing them up. In principle, it is similar to having a local scoped variable inside a function that a higher scope can't touch. In practice, it is possible to get around this "wall" and have your way with it, but it breaks the "spirit" of shadow DOM, which IMO is bad practice.
So, if I were to advise some best practice about any of this, my first advice is to as much as possible, respect the spirit of web components that utilize shadow DOM, and treat them like the black box they strive to be. Meaning, you should go to the web developers in charge of the web component and ask them to provide an interface for you to use.
For example, Adobe Launch has the ability to listen for custom events broadcast to the (light) DOM, so the site developers can add to their web component, create a custom event and broadcast it on click of the button.
Note: Launch's custom event listener will only listen for custom event broadcasts starting at document.body, not document, so make sure to create and broadcast custom events on document.body or deeper.
"But the devs won't do anything so I have to take matters into my own hands..."
Sadly, this is a reality more often than not, so you gotta do what you gotta do. If this is the case, well, Launch does not currently have any native features to really make life easier for you in this regard (for the "core" part of the below stuff, anyways), and as of this post, AFAIK there are no public extensions that offer anything for this, either. But that doesn't mean you're SoL.
But I want to state that I'm not sure I would be quick to call the rest of this answer "Best Practice" so much as "It's 'a' solution..". Mostly because this largely involves just dumping a lot of pure javascript into a custom code box and calling it a day, which is more of a "catch-all, last resort" solution.
Meanwhile, in general, it's best practice to avoid using custom code boxes when it comes to tag managers unless you have to. The whole point of tag managers is to abstract away the code.
I think the TL;DR here is basically me reiterating this something that should ideally be put on the site devs' plate to do. But if you still really need to do it all in Launch because ReasonsTM, keep on reading.
'A' Solution...
Note: This is a really basic example with a simple open-mode shadow DOM scenario - in reality your scenario is almost certainly a lot more complex. I expect you to know what you're doing with javascript if you're diving into this!
Let's say you have the following on the page. Simple example of a custom html element with a button added to its shadow DOM.
<script>
class MyComponent extends HTMLElement {
constructor() {
super();
this._shadowRoot = this.attachShadow({
mode: 'open'
});
var button = document.createElement('button');
button.id = 'myButton';
button.value = 'my button value';
button.innerText = 'My Button';
this._shadowRoot.appendChild(button);
}
}
customElements.define('my-component', MyComponent);
</script>
<my-component id='myComponentContainer'></my-component>
Let's say you want to trigger a rule when a visitor clicks on the button.
Quick Solution Example
At this point I should probably say that you can get away with doing a Launch click event rule with query selector my-component#myComponentContainer with a custom code condition along the lines of:
return event.nativeEvent.path[0].matches('button#myButton');
Something like this should work for this scenario because there are a lot of stars aligned here:
The shadow dom is open mode, so no hacks to overwrite things
There are easily identifiable unique css selectors for both light and shadow DOM levels
You just want to listen for the click event, which bubbles up and
acts like a click happened on the shadow root's light DOM root.
In practice though, your requirements probably aren't going to be this easy. Maybe you need to attach some other event listener, such as a video play event. Unfortunately, there is no "one size fits all" solution at this point; it just depends on what your actual tracking requirements are.
But in general, the goal is pretty much the same as what you would have asked the devs to do: create and broadcast a custom (light) DOM event within the context of the shadow DOM.
Better Solution Example
Using the same component example and requirement as above, you could for example create a rule to trigger on DOM Ready. Name it something like "My Component Tracking - Core" or whatever. No conditions, unless you want to do something like check if the web component's root light DOM element exists or whatever.
Overall, this is the core code for attaching the event listener to the button and dispatching a custom event for Launch to listen for. Note, this code is based on our example component and tracking requirements above. It is unique to this example. You will need to write similar code based on your own setup.
Add a custom js container with something along the lines of this:
// get the root (light dom) element of the component
var rootElement = document.querySelector('#myComponentContainer');
if (rootElement && rootElement.shadowRoot) {
// get a reference to the component's shadow dom
var rootElementDOM = rootElement.shadowRoot;
// try and look for the button
var elem = rootElementDOM.querySelector('button#myButton');
if (elem) {
// add a click event listener to the button
elem.addEventListener('click', function(e) {
// optional payload of data to send to the custom event, e.g. the button's value
var data = {
value: e.target.value
};
// create a custom event 'MyButtonClick' to broadcast
var ev = new CustomEvent('MyButtonClick', {
detail: data
});
// broadcast the event (remember, natively, Launch can only listen for custom events starting on document.body, not document!
document.body.dispatchEvent(ev);
}, false);
}
}
From here, you can create a new rule that listens for the custom event broadcast.
Custom Event Rule Example
Rule name: My Button clicks
Events
Extension: Core
Event Type: Custom Event
Name: MyButtonClick
Custom Event Type: MyButtonClick
Elements matching the CSS selector: body
Conditions
*None for this scenario*
From here, you can set whatever Actions you want (set Adobe Analytics variables, send beacon, etc.).
Note:
In this example, I sent a data payload to the custom event. You can reference the payload in any custom (javascript) code box with event.detail, e.g. event.detail.value. You can also reference them in Launch fields with the % syntax, e.g. %event.detail.value%.
I am using Angular2 with ionic, and I have this sort of component:
class MyComponent implements OnInit, OnDestroy {
ngOnInit() {
console.log("init");
socket.on("something", this.something.bind(this));
}
ngOnDestroy() {
console.log("destroy");
socket.remove("something", this.something.bind(this));
}
}
To open this page, I write this.nav.setRoot(MyComponent). Now my page is showing MyComponent, and the socket is listening for "something".
To refresh it, I write this.nav.setRoot(MyComponent) again. Now my page is showing MyComponet, and the socket is not listening for "something".
The console output is:
init
init
destroy
Why is it first run ngOnInit of the second component, and only then ngOnDestroy of the first one?
Is there a way to first call the destroy, and then the init?
Is there another way I should handle my socket?
As I understand this, you cannot really control, what should happen first in between ngOnInit of the second component and ngOnDestroy of the first one. These hooks, from 2 different components, at least, are not dependent on each other. Whether hooks on the same component are dependent on each other. Read this.
May be, this depends on whatever gets triggered faster at the moment.
In this case, what you can do is, you can move your code from ngOnDestroy before calling this.nav.setRoot(MyComponent).
Also, if the requirement does not suite this kind of code, you can try to refresh a particular type of UI component which needs to be refreshed than refreshing the entire component like this.nav.setRoot(MyComponent).
You can refresh a particular UI component using DOM using document.getElementByID('myDiv') type of operations.
Hope this helps. If not, please mention the specifics.
Apparentally, ionic has it's own lifecycle functions, as mentioned here.
Instead of ngOnInit use ionViewDidLoad
Instead of ngOnDestoy use ionViewWillLeave
Works like magic.
With routing/routes defined in the manifest.json and using Router.navTo() to change the hash and the content of the target App control, I noticed that the "old" views and controllers are still hanging around and listening to events (e.g. performing binding updates for controls that are no longer visible on the stage).
I (wrongly) assumed that the router would clean these views/controls up for me - what is the recommended way doing so?
You are correct. Before calling oRouter.navTo(...) you can call unbind. To give you an example you could check here. There you can find the following line of code inside the onNavBack handler:
this.getView().unbindElement();
unbindElement() is called because previously bindElement(...) was called in the same controller. So just make sure to use bind/unbind combination before oRouter.navTo()...
When you initialize a tinyMCE editor I have noticed two different ways to get called when the editor is created.
One way is using the setup callback that is part of tinyMCE.init:
tinyMCE.init({
...
setup : function(ed) {
// do things with editor ed
}
});
The other way is to hook up to the onAddEditor event:
tinyMCE.onAddEditor.add(function(mgr,ed) {
// do things with editor ed
});
What are the differences between using these two methods?
Is the editor in a different state in one versus the other? For example, are things not yet loaded if I try to access properties on the editor object.
What are reasons to use one over the other?
The difference here is that tinyMCE.onAddEditor adds code to be executed onthe AddEditor event and fires when a new editor instance is added to the tinymce collection
while the setup setting lets you add events to the editor. It gets executed before the editor instances gets rendered.
One other difference is that setup is to be set inside the tinymce initialization call (the configuration setting) while onAddEditor usually gets called inside a tinymce plugin (but you may also set it inside the setup function).
onAddEditor.add gives warning in the latest TinyMCE 4:
Deprecated TinyMCE API call: <target>.onAddEditor.add(..)
.on(nameofevent, function(){...} ) is the proper way to do this in MCE4 if you don't have the backward-compatibility plugin.