# yii2-ledap

## Install

The preferred way to install this extension is through [composer](http://getcomposer.org/download/).

Either run:

```bash
composer require --prefer-dist ethercap/yii2-ledap "dev-master"
```

Or add to the require section of your `composer.json` file:

```
"ethercap/yii2-ledap": "dev-master"
```

## Usage

copy layout. you can edit the layout after copy.

```bash
cp vendor/ethercap/yii2-ledap/src/gii/layout.php xxx/views/layouts/main.php
```

### Code generator

the use of code generator is similar to gii/crud, let's begin:

```bash
php yii gii/ledapCrud --controllerClass="\frontend\controllers\TestController" --modelClass="common\models\Test" --searchModelClass="\frontend\forms\TestSearch"
```

After run, you can access the page. the page will be follow:

1. **xxController :** yii2 Controller to process http request.
2. **views/xx/\*.php :** the template file, which to rendering html.
3. **views/xx/\*.api :** the api file, which to rendering api.
4. **xx/web/xx/\*.js :** the js file of the view。it view be rendering in html with a hash.

the file structure as the following picture.

![](https://2137901372-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Llk_7DQsWPv43GT9ZZm%2F-Llo8Lgz9GtMP_g5wBLm%2F-LloBhiYAwYE-nKKFmOE%2FWX20190809-102539%402x.png?alt=media\&token=8cf6d972-c158-44d1-98f7-0a1bf16b2e15)

### List View

the javascript has a dataProvider(Note. it's not the php DataProvider) which  to control the page.&#x20;

#### 1.refresh on input

we can add an event on form-item to refresh on inputing. thus, when we input, the table will auto search and refresh.

```markup
<form-item :model="dp.searchModel" attr="name" @input="refresh('')"></form-item>
```

#### &#x20;2. Some DataProvider api

{% code title="index.js" %}

```javascript
// to get an DataProvider
this.dp = ledap.App.getWebDataProvider({

    httpOptions:{
        url:'/test/xxx',
        params:'ddd',
    }
    // can be ignore. the key to removal duplicate models 
    primaryKey: 'id',
    // can be ignore. to prevent a lot of http request, the min interval between two request
    // 0 represent no interval
    timeWait : 600,
});

// a dataprovider contains 4 sub object
// the search params of list page
console.log(this.dp.searchModel)
// the items show in list page
console.log(this.dp.models);
// the sort of list page
console.log(this.dp.sort);
// the pagination of list page
console.log(this.dp.pager);

//change any params and refresh
this.dp.searchModel.name = 'xxx';
this.dp.refresh();

// change params and refresh
this.dp.changeParams({name:"xxx"});

// changePage
this.dp.changePage(page)
this.dp.nextPage();
this.dp.prePage();

// we may want refresh as mobile
this.dp.refresh("header");
this.dp.refersh("footer");

// we may want sort table
this.dp.sort = "id, -name";
this.dp.refresh();
// we can also use function
this.dp.setSort("id,-name");

// we may want sort locally
this.dp.sortModels("name", asc=true);

// we can use isLoading to show loading status
this.dp.isLoading
// we can also use event to do something
this.dp.on(ledap.WebDataProvider.EVENT_BEFOREGETDATA, function(){
});
this.dp.on(ledap.WebDataProvider.EVENT_AFTERGETDATA, function(){
});
```

{% endcode %}

#### &#x20;3. grid

grid is a ledap component, which render by columns and dataProvider.

{% code title="index.js" %}

```javascript
columns : {
    'id',
    {
        'attribute' : 'id',
        'label' : 'ID',
        //if use sort, we can click header to sort table
        'userSort' : true, 
    },
    {
        'attribute' : 'name',
        'value' : function() {
            // vm refer to current vue component
            // value,model, index, attribute and dataProvider can be use in the template
            return '<a @click="vm.xxx(model)"></a>';
        },
        'format': 'html',
    }
}
```

{% endcode %}

if you're not satisfied with the default content. you can change the grid content by Vue scoped slot. the component will transfer 4 args to scoped slot:&#x20;

* **model**. current model, refer to the row of grid.
* **column**. current colomn, refer to the col of grid.
* **index**.  the index of dp.models. dp.model\[index] = model
* **value**. the result of the column calculate with the model.

{% code title="index.php" %}

```markup
<grid class="table" :data-provider="dp" :columns="columns">
    <template v-slot:label="p">
        <th class="xxx">{{p.value}}{{p.model}}{{p.index}}{{p.column}}</th>
    </template>

    <template v-slot:default="p">
        <td class="xxx">{{p.value}}{{p.model}}{{p.index}}{{p.column}}</td>
    </template>
</grid>
```

{% endcode %}

you can also use dataProvider without grid. like dataProvider with "list", or components all by self.

{% code title="index.php" %}

```markup
<input type="xxx" v-model="dp.searchModel.name" @input="dp.refresh('')" />
<div v-for="model in dp.models">
    <div class="">{{model.id}}</div>
    <div class="xxx">{{model.img}}</div>
</div>
<div class="pager">
    <div>total:{{dp.pager.currentPage}}|{{dp.pager.totalCount}}</div>
    <a @click="dp.prePage()">PrePage</a>
    <a @click="dp.nextPage()">NextPage</a>
</div>
```

{% endcode %}

### View\&Update\&Create

type is the switch of the page. it can be set to an item of \["view", "update", "create"].

#### 1.model

the model corresponding to the yii2  Form Model. let's begin:

{% code title="view\.js" %}

```javascript
//generate a model
let model = ledap.App.getModel(data);

// change model's value
model.name = "xxx";
// get label of attribute
model.getAttributeLabel("name");
// get hint of attribute
model.getAttributeHint("name");
// get error of attribute
model.getErrors("name");
// get first error of attribute
model.getFirstError("name");
// get All error of a model
model.getErrors();

// validate the value of model.if not correct, return false.
// we can use getErrors to show the error
model.validate();

```

{% endcode %}

#### 2.detail

detail is similar to grid. we can use columns to show a detail.the rule is  same to the grid.

{% code title="view\.js" %}

```javascript
columns : {
    'id',
    {
        'attribute' : 'id',
        'label' : 'ID',
        //if use sort, we can click header to sort table
        'userSort' : true, 
    },
    {
        'attribute' : 'name',
        'value' : function() {
            // vm refer to current vue component
            // value,model, index, attribute and dataProvider can be use in the template
            return '<a @click="vm.xxx(model)"></a>';
        },
        'format': 'html',
    }
}
```

{% endcode %}

&#x20;like grid, we can also use scoped slot to change the default view.

#### 3. form-item

when we want't to show form, the form-item is very import. the form-item contains 4 parts:

* label
* hint
* input
* error

we cant use like this:

{% code title="\_form.php" %}

```markup
<!-- 
    normal input
    validator: the event to validator,
 default is blur. 
    tag: the tag, default is div
-->
<form-item :model="model" attr="name" :validator="['input', 'blur', 'focus']" :tag="div">
</form-item>

<!-- we can also use scoped slot to change the default view-->
<form-item :model="model" attr="name">
    <template v-slot:label="p">
        <label> {{p.model.getAttributeLabel(p.attr)}}{{p.model.isRequired(p.attr) ? '*' : ''}}</label>
    </template>
    <template v-slot="p">
        <baseinput type="password" v-bind="p"></baseinput>
    </template>
    <template v-slot:error="p">
        <p v-show="p.showError">{{p.showError}}</p>
    </template>
</form-item>

<!-- some other input -->
<!-- 
    groupinput need DictValidator. you should add a rule in model like this:
    ['name', \ethercap\common\validators\DictValidator::className(), [0 => 'xxx', 1=> 'xxx', ...]],
    dropdown also need DictValidator.
    ajax select you should follow next chapter
-->
<form-item :model="model" attr="name">
    <template v-slot="p">
        <groupinput v-bind="p"></groupinput>
    </template>
</form-item>
<form-item :model="model" attr="name">
    <template v-slot="p">
        <dropdown v-bind="p"></dropdown>
    </template>
</form-item>
```

{% endcode %}

Sometimes, we may want use Other's Vue Component. So I did an example in this package.

I use an [vue2-datepickerr ](https://www.npmjs.com/package/vue2-datepicker) to show the code.

{% code title="\_form.php" %}

```php
<?php
// or we can use register
// $this->registerJs("http://xxxx.js", ['depends' => '\ethercap\ledap\assets\VueAsset'])
\ethercap\ledap\assets\DatePickerAsset::register($this);
?>

<form-item :model="model" attr="time">
    <template v-slot="p">
        <date-picker v-model="p.model[p.attr]" :value-type="'format'"></date-picker>
    </template>
</form-item>

```

{% endcode %}

we should register the Component in view\.js

{% code title="view\.js" %}

```javascript
Vue.Comonpoent("date-picker", DatePicker.default);
```

{% endcode %}

### Other

#### 1.select2 & SearchAction

sometimes we want use a ajax suggestion like select2. the package offer you follows:

* SearchAction: a php Action to process the request.

```php
<?php
namespace frontend\controllers;
use Yii;
class xxController extends Controller
{
    // ....
    
    public function actions()
    {
        return [
            'search' => [
                'class' => \ethercap\ledap\actions\SearchAction::className(),
                'processQuery' => function($model) {
                     $query = xxx::find()->select(['id', 'name as text'])->asArray();
                     if($model->id) {
                         return $query->where(['id' => $model->id]);
                     }
                     if($model->keyword){
                         $query->andWhere(['like', 'name', $model->keyword]);                         
                     }
                     return $query;
                },
                // you can ignore this config
                $dataConfig => [
                    'id',
                    'text'
                ]
            ],
        ];
    } 

}
```

after all this config, we have an api "/xx/search" to search data from database.

* select2 component: vue component to send http request.

{% code title="\_form.php" %}

```markup
<form-item :model="model" attr="search">
    <template v-slot="p">
        <select2 v-bind="p" :data-provider="dp"></select2>
    </template>
</form-item>
```

{% endcode %}

{% code title="view\.js" %}

```javascript
{
    data:{
        dp: App.ledap.getWebDp({
            httpOptions:{
                "url" : '/xx/search'
            }
        });
    }
}
```

{% endcode %}

#### 2.upload & UploadAction

to be continued.

### Tips

1. be aware of Vue.
2. do not use yii2 widget.
3. all component you use should register first.  App.register(\['xxx'], Vue);
4. you can use other component.&#x20;
5. you can change the request by replace AppAsset.
