Define global array constant for using in view - cakephp

I want to define global array constant
code in bootstrap.php
$adv_types = array('top' => 'Верх', 'left' => 'Левое', 'right' => 'Правое', 'bottom' => 'Нижнее');
code in view file
echo $form->input('Adv.type', array('type' => 'select', 'option' => $adv_types, 'label' => 'Место рекламы'));
but cakephp gives error:
"Undefined variable: adv_types"

Unfortunately, the scope for bootstrap.php is bootstrap.php, so the $adv_types variable will be out-of-scope as soon as PHP completes parsing bootstrap.php.
There are several approaches you can take, depending on your actual requirements.
Solution 1: you need this variables in many of your views
If you need the variable to be available in all views, you should define and set it in AppController::beforeRender().
In app/app_controller.php:
class AppController extends Controller
{
function beforeRender()
{
parent::beforeRender();
$adv_types = array('top' => 'Верх', 'left' => 'Левое', 'right' => 'Правое', 'bottom' => 'Нижнее');
$this->set(compact('adv_types'));
}
}
This will allow any of your views to access the $adv_types array.
Solution 2: you might need access to this variable anywhere within your CakePHP app
If you must access the $adv_types variable elsewhere in your app, you can add it to the Configure collection. In bootstrap.php:
Configure::write('NameOfYourAppAsNamespace.adv_types', array('top' => 'Верх', 'left' => 'Левое', 'right' => 'Правое', 'bottom' => 'Нижнее'));
I recommend using the name of your project as a pseudo namespace; adv_types is unlikely to conflict with other identifiers, but if you start using this approach more often, your chances of creating conflicts increases. Additionally, this allows you to group the data you're storing in the Configure collection under one namespace, which can be handy for debugging.
Anyway, this approach will allow you to access the variable in any scope under the CakePHP umbrella, by invoking Configure::read(). Thus:
$adv_types = Configure::read('NameOfYourAppAsNamespace.adv_types');
Solution 3: you absolutely must have this variable available as a global variable
If you absolutely must have this as a standard PHP global variable, you could do the following:
$GLOBALS['adv_types'] = array('top' => 'Верх', 'left' => 'Левое', 'right' => 'Правое', 'bottom' => 'Нижнее');
Before doing this, please consider whether this is strictly necessary. Global variables are a really messy business, and you should have a clear and present need to justify it.
Edit/Update!
Thirty seconds in Google Translate has led me to discover that your array contains translations corresponding to the keys. You might want to investigate using CakePHP's Internationalization & Localization features to abstract away the distinction between English and Russian terms for top/left/right/bottom (and everything else).

These need to set in your app_controller.php, and then passed to your views
// app_controller.php
class AppController extends Controller {
var $adv_types = array('top' => 'Верх', 'left' => 'Левое', 'right' => 'Правое', 'bottom' => 'Нижнее');
function beforeFilter() {
$this->set('adv_types', $this->adv_types);
}
}
To me, bootstrap.php is not the correct file for this constant

Related

React context not being up to date in a Timeout

I am using react app context to store an array of "alerts objects" which is basically any errors that might occur and I would want to show in the top right corner of the website. The issue I am having is that the context is not being up to date inside a timeout. What I have done for testing is gotten a button to add an alert object to the context when clicked and another component maps through that array in the context and renders them. I want them to disappear after 5 seconds so I have added a timeout which filters the item that got just added and removes it. The issue is that inside the timeout the context.alerts array seems to have the same value as 5 seconds ago instead of using the latest value leading to issues and elements not being filtered out. I am not sure if there's something wrong with my logic here or am I using the context for the wrong thing?
onClick={() => {
const errorPopup = getPopup(); // Get's the alert object I need
context.setAlerts([errorPopup, ...context.alerts]);
setTimeout(() => {
context.setAlerts([
...context.alerts.filter(
(element) => element.id !== errorPopup.id,
),
]);
}, 5000);
}}
onClick={() => {
const errorPopup = getPopup(); // Get's the alert object I need
context.setAlerts([errorPopup, ...context.alerts]);
setTimeout(() => {
context.setAlerts(alerts => [
...alerts.filter(
(element) => element.id !== errorPopup.id,
),
]);
}, 5000);
}}
This should fix it. Until react#17 the setStates in an event handler are batched ( in react#18 all setStates are batched even the async ones ), hence you need to use the most fresh state to make the update in second setAlerts.
To be safe it's a good practice using the cb syntax in the first setState as well.
I think the fix would be to move context.setAlerts(...) to a separate function (say removePopupFromContext(id:string)) and then call this function inside the setTimeout by passing the errorPopup.Id as parameter.
I'm not sure of your implementation of context.setAlerts, but if it's based on just setState function, then alternatively, you could do also something similar to how React let's you access prevState in setState using a function which will let you skip the creation of the extra function which may lightly translate to:
setContext(prevContextState =>({
...prevContextState,
alerts: prevContextState.alerts.filter(your condition)
)})

Is there a way to save dynamic JSON into Mobx State Tree?

I was using Redux before and saving dynamic JSON into the store was quite easy. I was thinking, how can we do the same in the Mobx-State-Tree, without actually defining the model structures.
.model({
data:types.array(...)
)}
I was trying, but again I need to define the JSON array structure into it.
The whole idea for using mobx state tree is to define implicit data types into your application.
But if for some reason you want to save JSON without defining is to convert it into a string using JSON.stringify({...})
And the model will be actually a string
.model({
data:types.string
)}
Then you can use actions to save JSON into it
.actions((self) => ({
saveJSON(jsonData){
self.data = JSON.stringify(jsonData)
}
})
You can then use this JSON anywhere by calling store.getJson property with JSON.parse() using view
views((self) => ({
get getJson(){
return JSON.parse(self.data)
}
})
I needed to do this because I receive object of a well defined type, but that type contains a config field, that can be any json.
I managed to do it in MST by defining:
export const AnyJsonValue = types.union(
types.string,
types.number,
types.integer,
types.Date,
types.boolean,
types.map(types.late(() => JsonValue)),
types.array(types.late(() => JsonValue)),
types.undefined,
types.null
);
And then using it like this:
export const MyObject = types.model("MyObject", {
uuid: types.identifier,
name: types.string,
config: types.maybeNull(types.map(AnyJsonValue)),
...
});
If you have some restrictions, you can still set them.
For example, if you want config to be either null, or a dict with any json nested (so config can't be a literal or array, but can be a dict containing them), you can use the type: t.maybeNull(t.map(JsonValue)).

Menu sub-items not showing on mobile (understrap theme)

I'm building a website using understrap-child theme.
I've got a navbar with a lot of items and sub-items (up to three levels). I've set the navbar depht to 0 since, according to the documentation, it should allow all the sub items to show in the menu.
<!-- The WordPress Menu goes here -->
<?php wp_nav_menu(
array(
'theme_location' => 'primary',
'container_class' => 'collapse navbar-collapse',
'container_id' => 'navbarNavDropdown',
'menu_class' => 'navbar-nav ml-auto',
'fallback_cb' => '',
'menu_id' => 'main-menu',
'depth' => 0,
'walker' => new Understrap_WP_Bootstrap_Navwalker(),
)
); ?>
It seems to be working fine on the desktop version, but once I switch to mobile, I am only able to see the first sub-items and not all those that come after that. E.g. ABOUT (first item) --> ABOUT US (sub item) --> OUR ALLIANCES (sub-sub-item). in the mobile version nothing shows after "ABOUT US". Is there a way to solve this problem?
Any suggestion would be really appreciated!
THANK YOU!
try to put depth => 2 instead of 0.
may be then your problem will be solved.

Setting State on react native alert's onPress error

I find myself in a situation where I am needed to set the state of app upon the onPress of alert in react native. I am implemented the code below
Alert.alert(
'Delete Program',
'Are you sure you want to delete this program?',
[
{text: 'OK', onPress: () => this.doRefreshState()},
],
{ cancelable: false }
)
doRefreshState = () =>{
console.warn("i made it here")
this.setState({})
}
The error response is:
"_this2.doRefreshState is not a function.(in '_this2.doRefreshState()','_this2,doRefreshState; is undefined).
Please note that the alert is in another function
First, as a comment in general I would strongly suggest giving a more complete example so we have more information (including your version of React Native and the target OS).
My guess is that your method is not actually bound to the class instance, or in the case of a pure function, is not accessible. Using ES6 syntax you might try something like this:
class YourComponent extends React.Component {
doRefreshState() {
console.warn('method called') //personally I would use an alert here to verify
this.setState({})
}
renderAlertContainer() {
const onPressHandler = () => { //Bind to "this" with the lambda
this.doRefreshState()
}
return (
<Button onPress={()=>{
Alert.alert(
'Delete Program',
'Are you sure you want to delete this program?',
[
//{text: 'OK', onPress: () => this.doRefreshState()},
{ text: 'OK', onPress: onPressHandler }
],
{ cancelable: false }
) }} />
)
}
render() {...}
}
The reason I would format it this way is for (1) readability, it shows what "this" I meant, and (2) because I'm not actually sure what "this" refers to when it is transpiled by babel/webpack/whatever (and "this" has been a problematic language feature in JavaScript for many developers). You could probably jump into the debugger and figure out what "this2" actually is, but my guess is that it isn't bound to the object you think it is.
Secondly, depending on how your class/object/function is constructed you might need to explicitly bind the function to the prototype rather than the instance. In that case the easiest solution (in my opinion) is to simply add it in the constructor:
class.....
{
constructor(props) {
super(props)
this.doRefreshState = this.doRefrestState.bind(this) //annoying but now this method is bound to the class and I can act on it no matter the lifecycle state.
}
}
Without a clear picture of how the React.Component is initialized I can't really say for sure but I suspect either the method needs to be bound to the instance (prototype) or that the this object in the alert method is, in fact, executing in the context of the alert and not your class and, thus, is undefined.
My preferred technique to fix that category of error is to pull the handler out into a variable and then assign it so that whenever it is called the correct context is explicit rather than implicit (implied).
I should note: I just pushed out an Android React Native App to a client and I ran into problems very similar to this more often than I should have. Pulling the handlers out of the methods solved the problem in my Third party calls almost every time. Frankly, I'm just guessing at the cause of this issue as I didn't crawl into the debugger and figure it out, but I suspect one of my two solutions here will fix it.
Try binding the function instead of calling it.
Example:
Alert.alert(
'Delete Program',
'Are you sure you want to delete this program?',
[
{text: 'OK', onPress: this.doRefreshState},
],
{ cancelable: false }
)
doRefreshState = () =>{
console.warn("i made it here")
this.setState({})
}
Hopefully this should work as you are using arrow functions. If this does not work try binding the function like.
Alert.alert(
'Delete Program',
'Are you sure you want to delete this program?',
[
{text: 'OK', onPress: this.doRefreshState.bind(this)},
],
{ cancelable: false }
)
doRefreshState() {
console.warn("i made it here")
this.setState({})
}
Hope that helped.
Would check your doRefreshState function is outside the react's lifecycle method? Let's say i am calling this from componentWillMount
componentWillMount(){
Alert.alert(
"Delete Program",
"Are you sure you want to delete this program?",
[{ text: "OK", onPress: () => this.doRefreshState() }],
{ cancelable: false }
);
}
doRefreshState = () => {
alert("i made it here");
this.setState({});
};
render(){}

Drupal webform programmatically creating composite components

I have successfully created a custom webform component for a specific type of data. The data comes in various sections so I need to create additional but existing component types which will be sub-components to my custom component.
Basically, I am trying to create a composite of webform components programmatically.
My problem is, the code all executes successfully - I get my custom component gets created and I get feedback saying my sub-components were also successfully created.
However, the sub-components do not show-up in my webform.
At the point when my custom component is created and inserted in to the DB, I am attempting to create all the necessary sub-components via the following code:
function my_module_webform_component_insert($component){
if($component['type'] == 'my_component'){
$node = node_load($component['nid']);
$address_fields = array("my_sub_component1", "my_sub_component2");
foreach ($address_fields as $key => $address_field) {
//fetch next available component ID
$next_id_query = db_select('webform_component')->condition('nid', $component['nid']);
$next_id_query->addExpression('MAX(cid) + 1', 'cid');
$next_cid = $next_id_query->execute()->fetchField();
_create_sub_component($component['nid'], $address_field, $next_cid, $component['cid']);
}
}
}
My _create_sub_component function is defined below:
function _create_sub_component($nid, $new_component, $cid, $pid){
$node = node_load($nid);
$processed_name = str_replace(' ', '_', strtolower($new_component));
// Create the webform components array. Not sure if we need all these
// values, but let's be sure.
$component = array(
'cid' => (int)$cid,
'pid' => 0,#(int)$pid,
'nid' => (int)$node->nid,
// I don't trust the admin to make a key based on input :)
'form_key' => $processed_name,
'name' => $new_component,
// I want all lines to be numeric type component.
'type' => 'textfield',
'value' => '',
'extra' => array(),
'mandatory' => '0',
'weight' => 0,
'page_num' => 1,
);
array_push($node->webform['components'], $component);
node_save($node);
drupal_set_message("{$new_component} component successfully created in {$node->title}");
}
My guess is the call to node_save is causing the problem but I don't know exactly how.
Got it!
Replace:
array_push($node->webform['components'], $component);
node_save($node)
With:
webform_component_insert($component);

Resources