Yii2. Add attributes to GET request before submit - get

I use an ActiveForm in yii2. After click on submit button I have a following GET request. How can I add the custom attributes to 'ProjectSearch' array before submition?
$_GET = [
'r' => 'project/index',
'ProjectSearch' => [
'description_' => '',
'categories' => '',
'moneyrange' => '5,50',
],
'sort' => '-price',
];

You should look into yii.activeform.js in order to know more about the events which can help you to do the above mentioned task. Like here you can use before submit:
$('#contact-form').on('beforeSubmit', function (e) {
//Add your part of code!
}
return true;
});

Related

Is there a way to query entries and categories together in Craft 3?

'm working on a global site search ( autocomplete ), right now it have almost all entries ( that have uri defined ) for results, it uses js fetch and Craft element api in a json file that filter results based on a query string. Works great!
I was wondering if it's possible to add to the query categories too, i don't know if this is possible buy maybe it's a way to merge them, here is my json api code, thanks!
'search.json' => function() {
$qParam = Craft::$app->request->getParam('q');
return [
'elementType' => Entry::class,
'paginate' => false,
'criteria' => [
'uri'=> ':notempty:',
'search' => $qParam,
'orderBy' => 'score',
'limit' => 20,
],
'cache' => false,
'transformer' => function(Entry $item) {
return [
'title' => $item->title,
'url' => $item->url,
];
},
];
}

How to make chrome.contextMenus.onClicked.addListener distinguish for different context menu ids?

While developing contextmenu based extension for Chrome, i'm facing a situation where invoking chrome.contextMenus.onClicked.addListener in a loop adds action cumulatively for each subcontext menu item.
So, when subcontextmenu is clicked it triggers event listener for all subcontext menus, instead for the context menu which was clicked.
var parent;
chrome.runtime.onInstalled.addListener(() => {
parent = chrome.contextMenus.create({"title": "CCM", "id":"CCM", "contexts":["selection"]});
});
Object.keys(msgs).forEach(function(key) {
chrome.runtime.onInstalled.addListener(() => {
var createCM = chrome.contextMenus.create(
{"title": "subcontextmenu"+key, "id":"scm"+key, "parentId": parent, "contexts":["selection"]});
});
chrome.contextMenus.onClicked.addListener(function(info,tab){openAction(info,tab,JSON.stringify(msgs[key]['msg']));}
);
});
In the above example, msgs is a JSON object containing message to be displayed when each subcontextmenu is clicked. Also, the msgs JSON context is bound to change dynamically. So, we can't tweak openAction to hardcode the numbers and associate the messages.
Hope my question is clear. Any help for clearing this confusion will be a great time saver for me.
Use one listener for onInstalled and onClicked (it provides menuItemId in its parameter).
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({title: 'CCM', id: 'CCM', contexts: ['selection']});
Object.keys(msgs).forEach(key => {
chrome.contextMenus.create({
title: 'subcontextmenu' + key,
id: 'scm' + key,
parentId: 'CCM',
contexts: ['selection'],
});
});
});
chrome.contextMenus.onClicked.addListener((info, tab) => {
openAction(info, tab, JSON.stringify(msgs[info.menuItemId].msg));
});

Telegraf.js pass data from button to a function that handle a WizardScene

I need to pass some data retrieved from a Database.
When i click into a button i send a private message to the user who have cliecked. I need to pass some datas from that button, to the message sent. Because, into that message i have an other button that starts a WizardScene. SO how can i do to pass data? Thank you.
Here is my code.
This is into a function that post a photo with a description and with a callbackup button.
my function() {
...
let productId = //obtain from a db;
await bot.telegram.sendPhoto(
channel_id,
{ source: filepath },
{
caption: description.join("\n"),
parse_mode: 'MarkdownV2',
reply_markup: productButtons.reply_markup
}
)
return productId;
...}
and the button is:
const productButtons = Extra.markup((m) =>
m.inlineKeyboard([
[m.callbackButton('TEST', 'test_btn')],
])
)
when someone clicsk on it, it sends a message on a private user with this:
bot.action('testa_btn', (ctx) => {
console.log('testa')
let text = `My text about ${productId}`
ctx.telegram.sendMessage(ctx.from.id, o_functions.escapeReplace(text), {
parse_mode: 'MarkdownV2',
reply_markup: startBtn.reply_markup
})
});
this sends a text where i need to write my productid, and an other button where i start my wizard scene:
const startBtn = Extra.markup((m) =>
m.inlineKeyboard([
[m.callbackButton('START', 'start_btn_wizard')],
])
);
bot.action('start_btn_wizard', (ctx) => {
return ctx.scene.enter('super-wizard');
})
So how can i pass my productId variable, first to the button TEST, then to the wizard scene? I need to use it on the Wizard on the user dialogue.
THank you
You need to do a couple of things to pass data from a callback button to a handler and then towards a wizard scene.
Add the desired data to the button. Notice how I attached the product Id to the callback data.
const startBtn = Extra.markup((m) =>
m.inlineKeyboard([
[m.callbackButton('START', `start_btn_wizard_${product_id}`)],
])
);
Receive the button callback using a regex rather than using a literal string and extract the product ID from the callback data.
bot.action(/start_btn_wizard_+/, (ctx) => {
let product_id = ctx.match.input.substring(17);
// add all necessary validations for the product_id here
return ctx.scene.enter('super-wizard');
});
Pass the acquired id to the wizard scene and extract it again from inside the scene using ctx.scene.state.
bot.action(/start_btn_wizard_+/, (ctx) => {
let product_id = ctx.match.input.substring(17);
// add all necessary validations for the product_id here
return ctx.scene.enter('super-wizard', {product_id: product_id});
});
From inside the wizard, you can access the product_id using ctx.scene.state.product_id.
Hope this helps someone even though it's probably too late for the OP

quickreplies coding for node-wit example messenger.js

I cannot get quickreplies to work in node-wit messenger.js example.
I tried may things including:
1) Updated this line of code Our bot actions (messenger.js):
const actions = { send({sessionId}, {text,quickreplies})
2) And Updated this line of code Our bot actions (messenger.js):
return fbMessage(recipientId, text,quickreplies)
3) Updated my custom action in messenger.js :
getWelcome({context, entities})
{
context.welcome = "how are you. Please select from the options below";
context.welcome.quickreplies = [
{
title: 'Choice A',
content_type: 'text',
payload: 'empty'
},
{
title: 'Choice B',
content_type: 'text',
payload: 'empty'
},
]
return context;
},
4) I tried so many permutations. I cant get it to work with node-wit messenger.js example. The quick repliess are not displayed I searched and read all documentation
5) Can you help with this and also exactly how to retrieve the quick reply selected in messenger.js
Thanks
Sorry if it is late, but I guess you were looking for this:
send(request, response) {
const {sessionId, context, entities} = request;
let {text, quickreplies} = response;
if(text.substring(0,6) === IDENTIFY_PAYLOAD){
text = text.substring(6); // It is a payload, not raw text
} else {
text = {"text": text};
}
if(typeof quickreplies !== "undefined"){
text.quick_replies = response.quickreplies
.map(x => { return {
"title": x, "content_type": "text", "payload": "empty"}
});
}
}
What I did was sending a payload with a string that identifies it is not raw text (That's why I use an identifier for a payload), then in the send action I receive the two parameters request and response, and I get their attributes. Finally, I convert the payload params using a routine which I find on internet and that in fact works well to map the quick replies.

Yii2 Search model without using GridView

There is a serch form on the mainpage of a realestate agency. The data about objects is stored in the table "realty" that uses relations. For example, there are related tables category (residential, commercial, land plots), deal (buy, sell, rent), object_type (apartment, house, office).
Then different categories have different properties and and there are three bootstrap tabs in the search form: residential, commercial, land plots. Under each tab there are selects and input fields that are specific for the choosen tab.
In the most cases, the examples of using Search model are given within a gridview.
Is it possible to adapt Search model logic so that it could return the array of results from the table 'realty' based on the values indicated in the search form on the mainpage ?
Yes, of course you can. you have several options:
Solution 1 - worst solution but the answer to your question
modify the search function of the model (or create a new function). The search functions usually looks like this
public function search($params)
{
$query = Bookmark::find()->where('status <> "deleted"');
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => Yii::$app->session->get(get_parent_class($this) . 'Pagination'),
],
]);
if (!($this->load($params) && $this->validate())) {
return $dataProvider;
}
$query->andFilterWhere([
'status' => $this->status,
'id' => $this->id,
'reminder' => $this->reminder,
'order' => $this->order,
'update_time' => $this->update_time,
'update_by' => $this->update_by,
'create_time' => $this->create_time,
'create_by' => Yii::$app->user->id,
]);
$query->andFilterWhere(['like', 'name', $this->name])
->andFilterWhere(['like', 'url', $this->url]);
return $dataProvider;
}
You can change it to something like
public function search($params)
{
$query = Bookmark::find()->where('status <> "deleted"');
if (!($this->load($params) && $this->validate())) {
THROW AN ERROR SOMEHOW HERE
}
$query->andFilterWhere([
'status' => $this->status,
'id' => $this->id,
'reminder' => $this->reminder,
'order' => $this->order,
'update_time' => $this->update_time,
'update_by' => $this->update_by,
'create_time' => $this->create_time,
'create_by' => Yii::$app->user->id,
]);
$query->andFilterWhere(['like', 'name', $this->name])
->andFilterWhere(['like', 'url', $this->url]);
return $query->all();
}
however this will return to you all the records because ActiveDataProvider takes care of the pagination based on the query given.
Solution 2, a better solution
read the first example here http://www.yiiframework.com/doc-2.0/yii-data-activedataprovider.html . You can call ->getModels() on an ActiveDataProvider to get the actual records. No changes needed to the search function. Do whatever you want with the array.
Solution 3 and what I use all the time
Use ActiveDataProvider with a ListView. The list view allows you to create the list of records however you want, it does not have to be a table. I personally do this in many places and it works quite well. I sometimes transform arrays to an ArrayDataProvider just to use it. More about data providers here: http://www.yiiframework.com/doc-2.0/guide-output-data-providers.html

Resources