Yii2: Attach behavior to every controller - yii2

How do we attach a behavior to all web controllers in the application?
I understand this is theoretically possible with dependency injection, so I assumed something like this would work:
Yii::$container->set('yii\web\Controller', [
'as myBehavior' => [
'class' => 'app\behaviors\MyBehavior',
],
]);
however DI requires an exact class match (attaching to a parent class will not work). There is no way to know all the controller names in advance, especially when most are coming from 3rd party modules.
Is there another way to accomplish this?
EDIT: The purpose of this is to dynamically add controller filters (which are just a special type of behaviors). Therefore attaching the behavior during the EVENT_BEFORE_ACTION event is not sufficient, because it is triggered long after request filtering.

The module's (and application's) beforeAction event is triggered before the controller's version of that event. You can take advantage of that and use it to attach behaviors to current controller.
For example in your web.php config:
return [
'on beforeAction' => function() {
Yii::$app->controller->attachBehavior(
'myBehavior',
\app\behaviors\MyBehavior::class
);
},
// ... other configurations
];
Of course the disadvantage is that the behavior is not attached from the start.
NOTE: If your goal is attaching a filter to each controller, you can simply attach it to application itself instead of controllers.

Interesting problem, I must say. I could not find the simple solution for this but I have this hacky idea. You could take advantage of Yii autoloader and load your version of yii\web\Controller instead of the original one.
To do that:
Copy the original file from vendor and place it in your app
Don't change the original namespace and name.
Add your behavior config (or just the behavior's code, whatever) inside.
Add this line below in a place that will be called every time the app runs (like entry point file or bootstrap file, it must be called after vendor/yiisoft/yii2/Yii.php file is required):
Yii::$classMap['yii\web\Controller'] = ''; // replace '' with the path to your version
// of yii\web\Controller
Now, every time autoloader tries to load yii\web\Controller it will load your version instead so it should work like you want it.
The obvious con of this is that you will have to check manually if the original file has not been updated when upgrading Yii to make it up-to-date.

Child controller behavior depends on AccesControler behavior
class WorkerimgController extends OfficeController{
public function behaviors()
{
return ArrayHelper::merge(parent::behaviors(), [
]);
}
}

Related

Cannot set properties of undefined (setting 'isRightSidebarExpanded')

I'm trying to convert a template for use with nuxt.js
This attribute gives me this error
Cannot set properties of undefined (setting 'isRightSidebarExpanded')
#click="$store.global.isRightSidebarExpanded = false"
Here is the JS file : https://lineone.piniastudio.com/js/app.js
I imported it into nuxt.js with this line in nuxt.config.js
script: [
{src: '/js/app.js', body: false, defer:true}
]
Then nuxt.js shows me this error because it seems to ignore the client side javascript file, while it works perfectly without nuxt.js but with only static HTML/JS files in local
You can see here that the site is working normally (Laravel) https://lineone.piniastudio.com/
Some HTML variables (such as "activeTab") are also not defined
Property or method "activeTab" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Propertie
But everything works when I just keep HTML/JS/CSS and make it work without nuxt.js in static mode
It is not recommended to change the store data directly.
Vuex use Mutations to commit changes to the store state.
add a Mutation that change the isRightSidebarExpanded property
mutations: {
isRightSidebarExpanded(state , payload) {
// mutate state
state.isRightSidebarExpanded = payload ;
}
}
now you can call your Mutation
#click="$store.global.commit('isRightSidebarExpanded',false)"
---------------------------------------------------------------------------------
For the undefined issue :
-check if the $store.global property exist
-check if you are using the store (main.js)
app.use(store);
I have a theory it may be because the javascript is being referenced before it has been loaded. I had a similar problem dealing with VR/AR frame javascript that I loaded in the same way. I resolved this by using the callback option as described here in this article (Read to the end!).
https://vueschool.io/articles/vuejs-tutorials/how-to-load-third-party-scripts-in-nuxt-js/
Hope that helps!
-Brian

Using #Polymer/app-storage with lit-element

I want to use indexedDB with lit-element. To do so, I imported #polymer/app-storage/app-indexeddb-mirror in my lit-element project.
A copy of my code is here.
The value in the data attribute is not saved to indexedDB. No error is thrown.
Is there any incompatibility between the #polymer webcomponents and lit-element ?
Since <app-indexeddb-mirror> is just a web component, you can use it's API in any DOM-friendly library, including in lit-html.
For example:
render() {
return html`
<app-indexeddb-mirror
key="indexKey"
data="${this.data}"
#persisted-data-changed="${this.persistedDataChanged}">
log
</app-indexeddb-mirror>
`;
}
Note that lit-html has a different syntax for binding event listeners to element.s whereas with Polymer templates, you might add an attribute like
static get template() {
return html`<input on-change="methodName">`
}
With lit-html, the syntax for binding an event listener uses # in place of on-, and does not automatically dash-case your event names, so you could use:
html`<my-el #eventName="${referenceToFunction}"></my-el>`
where referenceToFunction is a direct reference to the event handler.
Note also that you don't need to create a lambda expression to pass the event to the instance method, since lit-html will auto-bind that function for you.
That all being said, consider using something like KV-Storage, idb or idb-keyval for simpler cases, as you'll end up shipping much less JavaScript to your clients that way, since you won't have to shlep along the entire Polymer library with you.

Yii2: differences between init() and bootstrap() and best place to add dynamical URLs on a module

I dont understand exactly what is the difference between use init() and bootstrap() on a class.
My case:
I want to add dynamical urls from my module by using Yii::$app->urlManager->addRules(...) but NOT loading the module in order to improve the performance.
So, I thought if bootstraping the module from the main configuration file like: 'bootstrap' => ['mymodule'], the Module::bootstrap() function will be executed ONLY and exclusively. But actually always runs Module::init() function, and then Module::bootstrap().
On this documentation http://www.yiiframework.com/doc-2.0/guide-runtime-routing.html#adding-rules say:
'Note that you should also list these modules in yii\web\Application::bootstrap() so that they can participate the bootstrapping process.'
But Module::bootstrap() is not executed if the module is listed on yii\web\Application::bootstrap()
I want to set only the dynamic rules with no module loading. How is it possible? What is the best place to set dynamical URLs with no impact to performance?
i decide resolve this issue(adding dynamic rules for module) by watching working extensions.
for example https://github.com/dmstr/yii2-pages-module extension uses bootstrap interface.
don`t forget write in composer.json "type" attribute as "yii2-extension".

is it ok to change the route programatically inside an epic in redux-observable

I am using redux-observable and in my epics I am changing the route in some of them using browserHistory.push. Is this standard/good practice? my gut feeling tells no, however need your opinion. In addition to this after upgrading to react-router 4, I cannot get access to browserHistory how do I do this?
Imperatively calling browserHistory.push() is totally fine. If it works for your use case and you don't need to know about the route changes in your reducers, carry on!
That said, the "purist" way would be for your epic to emit an action that will cause the route change. This can be done in react-router v4 by using the new react-router-redux, which supersedes the old one with the same name.
Once added, you can use the provided actions creators:
import { push } from 'react-router-redux';
export const somethingEpic = action$ =>
action$.ofType(SOMETHING)
.switchMap(() => {
somethingLikeAjaxOrWhatever()
.mergeMap(response => Observable.of(
somethingFulfilled(response),
push('/success-page')
))
});
To be clear, push() replace() etc in react-router-redux are action creators--aka action factories. They don't actually cause the route change themselves, or perform any side effects at all. They return an action that needs to be dispatched somehow for the route to change.
The other benefit of using actions to signal route change intents is testability. You no longer have to mock a history API, you can just assert that the epic emits the expected action. This is known as "effects as data", since your code doesn't perform the actual side effect, you just generate the intent.

Global function in Ionic 3

I would like to create a global function called "translate". As i know, i can define global variables and their values in the app.module.ts file. So i tried following code:
export function translate(string) {
// i am not sure if it would make a difference if i would use var
let ts = new TranslateService();
return ts.get(string).subscribe(res=>{
return res;
});
}
So maybe i try to use the wrong class, maybe the mistake is somewhere else. I use the ngx-translate Module (and it works great). Instead of always declaring to use the "TranslateService" (in every class where a translation is needed), i want to have a global function, where i can access the translations via only a function (i do not want to call another class...). You can see the code that i do like to use in the global function at the very end of the link (ngx-translate)
Thanks in advance.
global means for me, that something is accessible everywhere in the project.
I think thats a very bad idea, even if you get it to work somehow by some messy hack thats not the way this is intended to work.
TranslateService is already a service you can inject in every class you need it. And injecting a service using Angulars dependency injection is the intended way to use it. If you are afraid that there will be multiple translate services in the end - don't worry, Angulars dependency injection system takes care of that.
I guess you want to do this because you always have to write public translate: TranslateService in your constructor. But if you export a function in your app.module you have to import it again in your class, so you will have to write import { translate } from 'path/to/app.module/translate'; instead every time.