I extended "dektrium/yii2-user" controller's class as below, now I want to have $authItems in render view file of parent class, how should I pass this variable?
namespace app\controllers;
use app\models\AuthItem;
use dektrium\user\controllers\RegistrationController as BaseRegistrationController;
class RegistrationController extends BaseRegistrationController
{
public function actionRegister()
{
$authItems = AuthItem::find()->all();
return parent::actionRegister();
}
}
its is main class method
public function actionRegister()
{
if (!$this->module->enableRegistration) {
throw new NotFoundHttpException();
}
/** #var RegistrationForm $model */
$model = \Yii::createObject(RegistrationForm::className());
$event = $this->getFormEvent($model);
$this->trigger(self::EVENT_BEFORE_REGISTER, $event);
$this->performAjaxValidation($model);
if ($model->load(\Yii::$app->request->post()) && $model->register()) {
$this->trigger(self::EVENT_AFTER_REGISTER, $event);
return $this->render('/message', [
'title' => \Yii::t('user', 'Your account has been created'),
'module' => $this->module,
]);
}
return $this->render('register', [
'model' => $model,
'module' => $this->module,
]);
}
A solution could be
don't invoke the parent::actionRegister();
and add the code directly in your actionRegister
and last add the autItems to the render function array parameters
class RegistrationController extends BaseRegistrationController
{
public function actionRegister()
{
$authItems = AuthItem::find()->all();
// return parent::actionRegister();
if (!$this->module->enableRegistration) {
throw new NotFoundHttpException();
}
/** #var RegistrationForm $model */
$model = \Yii::createObject(RegistrationForm::className());
$event = $this->getFormEvent($model);
$this->trigger(self::EVENT_BEFORE_REGISTER, $event);
$this->performAjaxValidation($model);
if ($model->load(\Yii::$app->request->post()) && $model->register()) {
$this->trigger(self::EVENT_AFTER_REGISTER, $event);
return $this->render('/message', [
'title' => \Yii::t('user', 'Your account has been created'),
'module' => $this->module,
]);
}
return $this->render('register', [
'model' => $model,
'module' => $this->module,
'authItems' => $authItems;
]);
}
}
I would do something like this:
Add a member variable to your extended class, such as:
namespace app\controllers;
use app\models\AuthItem;
use dektrium\user\controllers\RegistrationController as BaseRegistrationController;
class RegistrationController extends BaseRegistrationController
{
public $authitems;
public function actionRegister()
{
$this->authitems = AuthItem::find()->all();
return parent::actionRegister();
}
}
Then in your parent class, do a test to see if that variable is set, such as:
public function actionRegister()
{
//.........
return $this->render('register', [
'model' => $model,
'module' => $this->module,
'authitems' => (isset($this->authitems)) ? $this->authitems : null;
]);
}
Related
I tried to use handleChange method to change the completed boolean who is responsible for checking the box but it does not change .. I can't find where it's missed up
class App extends React.Component {
constructor() {
super()
this.state = {
todos: todosData
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(id) {
this.setState((prevState) => {
const updatedTodos = prevState.todos.map(todo => {
if (todo.id === id) {
todo.completed = !todo.completed
}
return todo
})
return {
todos: updatedTodos
}
})
}
render() {
const todoItems = this.state.todos.map(item => <TodoItem key={item.id} item={item} handleChange={this.handleChange}/>)
return (
<div className="todo-list">
{todoItems}
</div>
)
}
}
export default App
and this is my ToDoItem component
function TodoItem(props) {
return (
<div className="todo-item">
<input
type="checkbox"
checked={props.item.completed}
onChange={() => props.handleChange(props.item.id)}
/>
<p>{props.item.text}</p>
</div>
)
}
export default TodoItem
Issue
You are mutating your state objects. When you don't return new object references React doesn't consider the value to be different and bails on rerendering with updated values.
if (todo.id === id) {
todo.completed = !todo.completed // <-- state mutation!
}
return todo // <-- same todo object reference
Solution
You need to also shallow copy any nested state you are updating.
handleChange(id) {
this.setState((prevState) => ({
todos: prevState.todos.map(todo => todo.id === id ? {
...todo, // <-- shallow copy todo
completed: !todo.completed, // <-- update completed property
} : todo)
});
}
I'm trying to access object inside an array of a JSONObject and print its values.
I'm able to print the array as JSONObject using console.log. But i fail to access the values inside the array which are again JSONObject format. Following is my my JSONObject
{
"id": 4,
"meta": {
"type": "pagetype",
"title": "Home"
}
},
"title": "Expose data to frontend",
"subtitle": "We will be exposing the content to the frontend",
"content": [
{
"type": "full_richtext",
"value": "<p><b>Bold body</b></p>"
},
{
"type": "button",
"value": {
"button_text": "Google",
"button_url": "https://google.com"
}
}
]
}
I need to access the values inside the array "content" and print values for
"value" -- Bold body --
"button_text"
"button_url"
I have tried it as follows
class App extends React.Component {
constructor() {
super();
this.state = {
'items': []
}
}
componentDidMount() {
fetch('http://localhost:8000/api/v2/pages/4/')
.then(results => results.json())
.then(results => this.setState({ 'items': results }));
}
render() {
var contents_from_wagtail = this.state.items;
var streamfield_content_array = contents_from_wagtail.content;
console.log(streamfield_content_array); //prints array of objects
return (
<React.Fragment>
<p>{this.state.items.subtitle}</p>
<p>{this.state.items.title}</p>
/* print the values for
"value" -- Bold body --
"button_text"
"button_url"
*/
</React.Fragment>
);
}
}
export default App;
When showing an array of items the .map method can be used to create multiple elements:
class App extends React.Component {
constructor() {
super();
this.state = {
'items': {}
}
}
componentDidMount() {
fetch('http://localhost:8000/api/v2/pages/4/')
.then(results => results.json())
.then(results => this.setState({ 'items': results }));
}
render() {
var contents_from_wagtail = this.state.items;
var streamfield_content_array = contents_from_wagtail.content || [];
console.log(streamfield_content_array); //prints array of objects
return (
<React.Fragment>
<p>{this.state.items.subtitle}</p>
<p>{this.state.items.title}</p>
{streamfield_content_array.map((item, index) => {
return <div key={index}>type: {item.type} <p>{item.value}</p></div>
})}
</React.Fragment>
);
}
}
export default App;
More .map examples: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
To access it within the render you have to access it conditional because it is not set for the first render until the fetch call is Executed
That is why you have to provide some fallback until the array is loaded.just check if the item is undefined and return null for example.
Only if the array is filled render the desires output and it should be fine.
Hope this helps. Happy coding.
You could use a combination of .map() and .filter() to iterate over the items within the content array. It looks like you only want to display items that have a type of button. So try something like this:
class App extends React.Component {
constructor() {
super();
this.state = {
'items': []
}
}
componentDidMount() {
fetch('http://localhost:8000/api/v2/pages/4/')
.then(results => results.json())
.then(results => this.setState({ 'items': results }));
}
render() {
var contents_from_wagtail = this.state.items;
var streamfield_content_array = contents_from_wagtail.content;
var buttonContent = this.state.items ? this.state.items.content.filter((item) => item.type == "button") : null
return (
<React.Fragment>
<p>{this.state.items.subtitle}</p>
<p>{this.state.items.title}</p>
{ buttonContent && buttonContent.map(item => (
<div>
<p>{item.button_text}</p>
<p>{item.button_url}</p>
</div>
))}
</React.Fragment>
);
}
}
export default App;
I have a function that displays the 'tarificationtaches' list i would like to add an avg to get the average 'techniciens' moyenne_avis from tables avis_intervention where "intervention.technicien_id" = "tarificationtaches.techncien_id" , in this is my schema .
taches_table
public function up()
{
Schema::create('taches', function (Blueprint $table) {
$table->increments('id');
$table->string('libelle_tache');
$table->float('Tarif', 8,2)->nullable();
$table->integer('metier_id')->unsigned();
$table->foreign('metier_id')->references('id')->on('metiers');
$table->datetime('deleted_at')->nullable();
$table->timestamps();
});
}
tarificationtaches_tables
Schema::create('tarificationtaches', function (Blueprint $table) {
$table->increments('id');
$table->float('tarif', 8,2);
$table->integer('tache_id')->unsigned();
$table->foreign('tache_id')->references('id')->on('taches');
$table->integer('technicien_id')->unsigned();
$table->foreign('technicien_id')->references('id')->on('techniciens');
$table->datetime('deleted_at')->nullable();
$table->timestamps();
});
techniciens_tables
Schema::create('techniciens', function (Blueprint $table) {
$table->increments('id');
$table->boolean('actif')->default(1);
$table->float('moyenne_avis')->nullable();
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users');
$table->datetime('deleted_at')->nullable();
$table->timestamps();
});
avis_interventions_tables
Schema::create('avis_interventions', function (Blueprint $table) {
$table->increments('id');
$table->string('qualité');
$table->integer('nbr_heure');
$table->string('service');
$table->float('note', 1,1);
$table->integer('client_id')->unsigned();
$table->foreign('client_id')->references('id')->on('clients');
$table->integer('intervention_id')->unsigned();
$table->foreign('intervention_id')->references('id')->on('interventions');
$table->timestamps();
});
interventions_tables
Schema::create('interventions', function (Blueprint $table) {
$table->increments('id');
$table->date('date_intervention')->nullable();
$table->string('description');
$table->dateTime('duree_prevu');
$table->boolean('statut');
$table->integer('technicien_id')->unsigned();
$table->foreign('technicien_id')->references('id')->on('techniciens');
$table->integer('tarification_id')->unsigned();
$table->foreign('tarification_id')->references('id')->on('tarificationtaches');
$table->integer('client_id')->unsigned();
$table->foreign('client_id')->references('id')->on('Clients');
$table->timestamps();
});
this is my function
public function getTar(){
$tarifications = tarificationtache::with('technicien')->get();
return $tarifications->map(function ($tarification) {
return [
'nom' => $tarification->technicien->user->nom,
'moyenne_avis' => $tarification->technicien->moyenne_avis,
'tache' => $tarification->tache->libelle_tache,
'tarif' => $tarification->tarif,
];
});
}
it shown like this
[{"nom":"tech 1","moyenne_avis":null,"tache":"tache 2","tarif":29.55},
{"nom":"tech
2","moyenne_avis":null,"tache":"tache 3","tarif":55.12},{"nom":"tech
1","moyenne_avis":null,"tache":"tache 3","tarif":253},{"nom":"tech
2","moyenne_avis":null,"tache":"tache 3","tarif":28.22}]
EDIT:
Below are the model files followed by model relationships eloquent query which I have used to generate the output:
technicien model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\User;
class technicien extends Model
{
protected $table = "techniciens";
public function user()
{
return $this->belongsTo('App\User', 'user_id', 'id');
}
}
tarificationtache model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\technicien;
use App\tache;
use App\interventions;
class tarificationtache extends Model
{
protected $table = "tarificationtaches";
public function technicien()
{
return $this->belongsTo('App\technicien', 'technicien_id', 'id');
}
public function tache()
{
return $this->belongsTo('App\tache', 'tache_id', 'id');
}
public function interventions()
{
return $this->hasMany('App\interventions', 'technicien_id', 'id');
}
}
tache model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class tache extends Model
{
protected $table = "taches";
}
interventions model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\avis_interventions;
class interventions extends Model
{
protected $table = "interventions";
public function avis_interventions()
{
return $this->hasMany('App\avis_interventions', 'intervention_id', 'id');
}
}
avis_interventions model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class avis_interventions extends Model
{
protected $table = "avis_interventions";
}
Model relationship query:
$tarifications = tarificationtache::with('technicien')->get();
$results = $tarifications->map(function ($tarification) {
return [
'nom' => $tarification->technicien->user->name,
'moyenne_avis' => $tarification->technicien->moyenne_avis,
'tache' => $tarification->tache->libelle_tache,
'tarif' => $tarification->tarif,
'avg_avis_interventions' => $tarification -> interventions -> count()
];
});
print_r($results -> toJson());
exit;
That returns below output to me:
[{
"nom": "Ravi-carpenter",
"moyenne_avis": 2,
"tache": "Task #1",
"tarif": 5.22,
"avg_avis_interventions": 2
}, {
"nom": "Ravi-carpenter",
"moyenne_avis": 3.5,
"tache": "Task #2",
"tarif": 6.52,
"avg_avis_interventions": 3
}]
public function getTar(){
$tarifications = tarificationtache::with('technicien')->get();
return $tarifications->map(function ($tarification) {
return [
'nom' => $tarification->technicien->user->nom,
'tache' => $tarification->tache_id,
'tarif' => $tarification->tarif,
'avg_avis_interventions' => $tarification->technicien-
>avisinterventions->avg('note')
];
});
print_r($results -> toJson());
exit;
}
I'm using 2amigos datepicker in my application and it's working good throughout. Today I tried to apply AdminLTE theme to the application. I've added all the css, js in the asset file and uploaded them in the respective folders. No error in the browser console so far.
The first thing I notice is that the datepicker is not working. When I click on the calendar icon on the datepicker, the datepicker doesn't open.
Asset file
<?php
namespace frontend\assets;
use yii\web\AssetBundle;
/**
* Main frontend application asset bundle.
*/
class DashboardAsset extends AssetBundle
{
public $basePath = '#webroot';
public $baseUrl = '#web';
public $css = [
'css/site.css',
'css/bootstrap.min.css',
'css/font-awesome.min.css',
'https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css',
'css/AdminLTE.min.css',
'css/skins/_all-skins.min.css',
'plugins/iCheck/flat/blue.css',
'plugins/morris/morris.css',
'plugins/jvectormap/jquery-jvectormap-1.2.2.css',
'plugins/datepicker/datepicker3.css',
'plugins/daterangepicker/daterangepicker.css',
'plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.min.css',
];
public $js = [
'js/bootstrap/bootstrap.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js',
'plugins/morris/morris.min.js',
'plugins/sparkline/jquery.sparkline.min.js',
'plugins/jvectormap/jquery-jvectormap-1.2.2.min.js',
'plugins/jvectormap/jquery-jvectormap-world-mill-en.js',
'plugins/knob/jquery.knob.js',
'https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.11.2/moment.min.js',
'plugins/daterangepicker/daterangepicker.js',
'plugins/datepicker/bootstrap-datepicker.js',
'plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.min.js',
'plugins/slimScroll/jquery.slimscroll.min.js',
'plugins/fastclick/fastclick.js',
'js/app.min.js',
'js/dashboard.js',
'js/demo.js',
];
public $depends = [
'yii\web\YiiAsset',
'yii\bootstrap\BootstrapAsset',
];
}
I'm using the datepicker like below -
use dosamigos\datepicker\DatePicker;
<?= $form->field($model1, 'startdate')->widget(
DatePicker::className(), [
'options' => ['placeholder' => 'Start Date ...', 'id' => 'startdate1'],
'inline' => false,
style="background-color: #fff; width:250px">{input}</div>',
'clientOptions' => [
'autoclose' => true,
'todayHighlight' => true,
'format' => 'yyyy-mm-dd'
]
]);?>
I have this code in my view
<div class="col-xs-6">
<?php echo $form->field($model, 'deskripsi_produk')->widget(Redactor::className(),['clientOptions'=>['autoresize'=>'true', 'limiter' => 20, 'plugins' => ['limiter'], 'buttons'=> ['html', 'formatting', 'bold', 'italic','underline','lists','horizontalrule'],]]);?>
</div>
it can limit 20 char but How I can limit text area to max string defined on model instead of limit with specific number?
Here is my model
class TbProduk extends \yii\db\ActiveRecord
{
/**
* #inheritdoc
*/
public $image;
public static function tableName()
{
return 'tb_produk';
}
public function rules()
{
return [
...
[['deskripsi_produk'], 'string', 'max' => 2000],
[['deskripsi_produk'], 'checkDesc'],
...
];
}
}
Trying to get the value via rules is a terrible idea. Instead you should create a constant in TbProduk that will hold the length:
class TbProduk extends \yii\db\ActiveRecord
{
const DESKRIPSI_PRODUK_LENGTH = 2000;
...
public function rules()
{
return [
...
[['deskripsi_produk'], 'string', 'max' => static::DESKRIPSI_PRODUK_LENGTH],
...
];
}
}
And in your view:
<div class="col-xs-6">
<?php echo $form->field($model, 'deskripsi_produk')->widget(Redactor::className(),['clientOptions'=>['autoresize'=>'true', 'limiter' => TbProduk::DESKRIPSI_PRODUK_LENGTH, 'plugins' => ['limiter'], 'buttons'=> ['html', 'formatting', 'bold', 'italic','underline','lists','horizontalrule'],]]);?>
</div>