How to add common model to Twig and Slim4 - twig

I'm using Twig and Slim4 with DI container (the same as this tutorial: https://odan.github.io/2019/11/05/slim4-tutorial.html).
I would like to know how can I add a common model to all my twig views, for example user object, general options and something like this.
This is the container Twig initialization:
TwigMiddleware::class => function (ContainerInterface $container) {
return TwigMiddleware::createFromContainer($container->get(App::class), Twig::class);
},
// Twig templates
Twig::class => function (ContainerInterface $container) {
$config = $container->get(Configuration::class);
$twigSettings = $config->getArray('twig');
$twig = Twig::create($twigSettings['path'], $twigSettings['settings']);
return $twig;
},
The twig middleware is the Slim standard one: Slim\Views\TwigMiddleware

You can add global variables to Twig environment, so they are accessible in all template files:
(To be able to provide a sample code, I assumed you have defined a service like user-authentication-service which is capable of resolving current user)
// Twig templates
Twig::class => function (ContainerInterface $container) {
//...
$twig = Twig::create($twigSettings['path'], $twigSettings['settings']);
$twig->getEnvironment()->addGlobal(
'general_settings',
[
'site_name' => 'my personal website',
'contact_info' => 'me#example.com'
]);
$twig->getEnvironment()->addGlobal(
'current_user',
// assuming this returns current user
$container->get('user-authentication-service')->getCurrentUser()
);
return $twig;
},
Now you have access to general_settings and current_user in all of your template files.

Related

Redux Toolkit - Slice utility methods

I'm building a React app with redux-toolkit and I'm splitting my store into some slices with redux-toolkit's helper function createSlice.
Here it is a simple use case:
const sidebar = createSlice({
name: "sidebar",
initialState:
{
menus: {}, // Keep track of menus states (guid <-> open/close)
visible: true
},
reducers:
{
show(state, action)
{
state.visible = action.payload.visible;
},
setMenuOpen(state, action)
{
const { id, open } = action.payload;
state.menus[id] = open;
return state;
}
}
});
export default sidebar;
Everything works fine until I "add" actions (that change the store) to the slice but consider your team looking for an utility function "getMenuOpen": this method doesn't change the store (it's not an action and cannot be addeded to reducers object). You can of course read directly the data from the store (state.menus[<your_id>]) but consider a more complex example where manipulating the data requires some library imports, more complex code, etc...I want to modularize/hide each slice as much as possible.
Actually I'm using this workaround:
const sidebar = createSlice({ /* Same previous code... */ });
sidebar.methods =
{
getMenuOpen: (state, id) => state.menus[id]
};
export default sidebar;
The above code allows importing the slice from a component, mapStateToProps to the redux store, and invoke the utilty function getMenuOpen like this:
import sidebar from "./Sidebar.slice";
// Component declaration ...
const mapStateToProps = state => ({
sidebar: state.ui.layout.sidebar,
getMenuOpen(id)
{
return sidebar.methods.getMenuOpen(this.sidebar, id);
}
});
const mapDispatchToProps = dispatch => ({
setMenuOpen: (id, open) => dispatch(sidebar.actions.setMenuOpen({id, open}))
});
The ugly part is that I need to inject the slice node (this.sidebar) as fist param of getMenuOpen because it's not mapped (as for actions with reducers/actions) automatically from redux-toolkit.
So my question is: how can I clean my workaround in order to automatically map the store for utility functions? createSlice doesn't seem to support that but maybe some internal redux's api could help me in mapping my "slice.methods" automatically to the store.
Thanks

add mutiple columns using pnpjs core in SPFx App

I am using pnp js to create a list to be used by my sharepoint application. i tried provisioning it using the sharepoint framework schema but i am really have issues with it and have raised an issue for it (https://github.com/SharePoint/sp-dev-docs/issues/1253) . now i am trying to create a list using pnp js as a workaround. my code looks something like this:
pnp.sp.web.lists.ensure("listName").then((ler : ListEnsureResult) => {
listEnsureResults = ler;
if (!ler.created) {
resolve(ler.list);
return Promise.reject(LIST_EXISTS);
}
return ler.list.fields.addText("Field1");
})
i want to add multiple columns but i am always getting an error in adding multiple fields.
You can add multiple fields to list as below in SPFx:
public addFieldsToList(listname: string): Promise<any> {
return Promise.all([
pnp.sp.web.lists.getByTitle(listname).fields.addText("MyField1"),
pnp.sp.web.lists.getByTitle(listname).fields.addText("MyField2"),
pnp.sp.web.lists.getByTitle(listname).fields.addText("MyField3"),
pnp.sp.web.lists.getByTitle(listname).fields.addText("MyField4"),
]).then((response) => {
return response;
}, (error: any) => {
return error;
}).catch((error: any) => {
return error;
});
}
You just need to call this method and pass your list name.

Passing path parameters in axios

I am using Axios with NodeJs and trying to pass path parameters in axios.get() method. For example, if URL is url = '/fetch/{date}', I want to replace {date} with the actual date while calling axios.get(url).
I went through the source code on Github and StackOverflow, but couldn't find any method.
Is it possible to keep URLs with parameters as a placeholder and replace them while actually calling the get method of Axios?
Axios doesn't have this feature and it looks like the team don't want to add it.
With credit to previous responders for inspiration, to me this seems like the solution closest to what you (and me) are looking for:
1 - Where you want to store all your URLs and their parameters, define them as functions which use a template string to return the composed URL:
export var fetchDateUrl = (date) => `/fetch/${date}`;
If you need any type-specific formatting of the value being concatenated into the URL, this function is a good place to do it.
2 - Where you want to make the request, call the function with the correct parameters:
import { fetchDateUrl } from 'my-urls';
axios.get(fetchDateUrl(someDateVariable))...;
Another variation, if you really like the idea of naming the parameters at the call site, you can define the URL function to destructure an object like this:
var fetchDateUrl = ({date}) => `/fetch/${date}`;
which you'd then use like this:
axios.get(fetchDateUrl({date: someDateVariable}));
Use template strings
url = `/fetch/${date}`
Or just tag it on
url = '/fetch/'+ date
I think using axios interceptors is better to do this :
//create your instance
const instanceAxios = axios.create({
baseUrl: 'http://localhost:3001'
]);
instanceAxios.interceptors.request.use(config => {
if (!config.url) {
return config;
}
const currentUrl = new URL(config.url, config.baseURL);
// parse pathName to implement variables
Object.entries(config.urlParams || {}).forEach(([
k,
v,
]) => {
currentUrl.pathname = currentUrl.pathname.replace(`:${k}`, encodeURIComponent(v));
});
const authPart = currentUrl.username && currentUrl.password ? `${currentUrl.username}:${currentUrl.password}` : '';
return {
...config,
baseURL: `${currentUrl.protocol}//${authPart}${currentUrl.host}`,
url: currentUrl.pathname,
};
});
// use like :
instanceAxios.get('/issues/:uuid', {
urlParams : {
uuid: '123456789'
}
})
For typescript users, you will need to add this, in one of your .d.ts
declare module 'axios' {
interface AxiosRequestConfig {
urlParams?: Record<string, string>;
}
}
( this is a POC, not really tested, doesn't hesitate if you see something wrong )
You can use template strings ie:
let sellerId = 317737
function getSellerAnalyticsTotals() {
return axios.get(`http://localhost:8000/api/v1/seller/${sellerId}/analytics`);
}
Given some API /fetch/${date} you likely want to wrap your axios call in a function.
const fetchData = (date) => axios.get(`/fetch/${date}`);
fetchData(dateObject.toFormat('yyyy-mm-dd'))
.then(result => { ... });
This requires the calling code to format date correctly however. You can avoid this by using a DateTime library that handles date string parsing and do the format enforcement in the function.
const fetchData = (date) => axios.get(`/fetch/${date.toFormat('yyyy-mm-dd')}`);
fetchData(dateObject)
.then(result => { ... });
you can do like this:
getProduct = (id) => axios.get(`product/${id}`);
I always do it like this:
const res = await axios.get('https://localhost:3000/get', { params: { myParam: 123 } });
I find this to be much clearer than template strings.
More explanation here

Is there a way to create a dynamic partial resulting from server treatment?

I've started to use NodeJS for a couple of months now and I came across a little problem with partials rendering.
I'd like to include a partial view in some templates but I want this partial to be dynamically generated from the server (because it depends on data retrieved from DB and other stuff).
I tried to create a template helper to do that but as the processing needs to be done asynchronously I can't get an html return to write within my template.
Basically what would be the best for me would be something similar to (this code does not work obviously):
template_file.js
...
<div>
<%- generatePartial(data) %>
</div>
...
helper_middleware.js
module.exports = function registerAppHelpers(request, response, next)
{
var appHelpers = {};
appHelpers.generatePartial = function generatePartial(data)
{
if (request.isAuthenticated())
{
DB.findOne({ id: request.user.id }, function found(error, obj)
{
if (error)
...
if (obj)
{
return generatePartial1(data);
}
else
{
return generatePartial2(data);
}
});
}
else
{
return generatePartial3(data);
}
};
// Register the helpers as local variables to be accessed within a template.
for (var helper in appHelpers) {
response.locals[helper] = appHelpers[helper];
}
next();
};
Now I may be completely wrong about the way I want to deal with this problem, so if you have any solution/other suggestions about that do not hesitate.
PS : I use ExpressJS and EJS.
I think you are going completely in a wrong direction..
What ejs is for?
ejs is javascript embedded in html so you can create dynamic html.
so whatever logic you have just write it inside the ejs template and let it handle everything. you just need to pass the information to ejs engine.
So instead of
if (obj)
{
return generatePartial1(data);
}
else
{
return generatePartial2(data);
}
I would suggest to capture the whole data
if (obj)
{
array1.push(data);
}
else
{
array2.push(data);
}
and then pass this whole bunch of data to ejs, write the conditions and all logic in ejs file, and let it handle the html logic.
for ex.
res.render('template_file.js', {
array1: array1,
array2: array2
});

How to integrate pagination in Kohana?

I am trying to integrate pagination in kohana, but don't know how to integrate it. Following is the controller function
public function action_index() {
//setup the model and view
$_users = Model::factory('users');
$us = $_users->get_users();
$view = View::factory('users/index')->bind('user_list', $us);
$this->template->set('content',$view);
}
How can i add pagination in this function?
I found some code for pagination but couldn't integrate it. This is the function i found
$this->pagination = new Pagination(array(
'base_url' => 'users/index/',
'uri_segment' => 'page',
'total_items' => count($items->get_item_count())
Please help me
EDIT:
I tried something like
public function action_index(){
$query = DB::select()->from('user');
// count number of users
$total_users = count($query);;
// set-up the pagination
$pagination = Pagination::factory(array(
'total_items' => $total_users,
'items_per_page' => 10, // this will override the default set in your config
));
// select rows using pagination's limit&offset
$users = $query->offset($pagination->offset)->limit($pagination->items_per_page)->execute();
$view = View::factory('users/index')->bind('user_list', $users)->bind('pagination', $pagination);
$this->template->set('content',$view);
}
Now no error found but pagination not showing up. Used shadowhand's pagination module suggested by #DanielThompson
I use shadowhand's pagination module which supports Kohana 3+, just make sure you grab the same branch as your Kohana version, then add it to your modules directory.
Update your application/bootstrap.php file:
Kohana::modules(array(
// ...
'pagination' => MODPATH.'pagination'
));
Copy modules/pagination/config/pagination.php to application/config/pagination.php
In your controller action (e.g. users):
// count number of users
$total_users = ORM::factory('User')->count_all();
// set-up the pagination
$pagination = Pagination::factory(array(
'total_items' => $total_users,
'items_per_page' => 10, // this will override the default set in your config
));
// get users using the pagination limit/offset
$users = ORM::factory('User')->offset($pagination->offset)->limit($pagination->items_per_page)->find_all();
// pass the users & pagination to the view
$this->view->bind('pagination', $pagination);
$this->view->bind('users', $users);
In your view:
// loop over users
foreach($users as $user) {
// ...
}
// display pagination view using
echo $pagination;
The module comes with two views: basic or floating which is set in the config file. You could also create a custom one for your application.

Resources