refactored user blocks using ajax loaded drupal's html as template for vue

This commit is contained in:
Bachir Soussi Chiadmi 2019-05-31 23:05:37 +02:00
parent a48b7262eb
commit d21bd5ef4e
22 changed files with 492 additions and 225 deletions

View File

@ -9,7 +9,7 @@ dependencies:
id: languageswitcher
theme: materiotheme
region: header_right
weight: 0
weight: -1
provider: null
plugin: 'language_block:language_interface'
settings:

View File

@ -1,20 +1,28 @@
uuid: f6ac7bc7-be23-4375-a237-1d9a36edb17e
uuid: 7d7e2454-1d8e-4e95-8110-985088634608
langcode: en
status: true
dependencies:
module:
- user_block
- materio_user
- user
theme:
- materiotheme
id: userblock
theme: materiotheme
region: header_right
weight: -1
weight: -2
provider: null
plugin: user_block
settings:
id: user_block
label: 'User block'
provider: user_block
provider: materio_user
label_display: visible
visibility: { }
visibility:
user_role:
id: user_role
roles:
authenticated: authenticated
negate: false
context_mapping:
user: '@user.current_user_context:current_user'

View File

@ -9,7 +9,7 @@ dependencies:
id: userlogin
theme: materiotheme
region: header_right
weight: -2
weight: -3
provider: null
plugin: user_login_block
settings:

View File

@ -96,6 +96,7 @@ module:
materio_migrate: 0
materio_samples: 0
materio_sapi: 0
materio_user: 0
matomo: 0
maxlength: 0
menu_block: 0
@ -146,7 +147,6 @@ module:
update: 0
url_to_video_filter: 0
user: 0
user_block: 0
video_embed_field: 0
video_embed_wysiwyg: 0
views_bulk_edit: 0

View File

@ -13,7 +13,7 @@ materio_sapi.search_form:
_format: json
requirements:
_permission: 'access materio search'
#
# materio_sapi.materio_sapi_search_form:
# path: '/materio_sapi/form/materio_sapi_search'
# defaults:
@ -21,4 +21,3 @@ materio_sapi.search_form:
# _title: 'MaterioSapiSearchForm'
# requirements:
# _access: 'TRUE'
#

View File

@ -0,0 +1,5 @@
name: 'materio_user'
type: module
description: ''
core: 8.x
package: 'Materio'

View File

@ -0,0 +1,57 @@
<?php
/**
* @file
* Contains materio_user.module.
*/
use \Drupal\Core\Form\FormStateInterface;
use \Drupal\Core\Block\BlockPluginInterface;
/**
* implements hook_form_FORM_ID_alter()
*
*/
function materio_user_form_user_login_form_alter(&$form, FormStateInterface $form_state, $form_id) {
// Drupal::logger('materio_user')->notice(print_r($form, true));
$form['name']['#attributes'] += array(
"v-model" => "mail",
"@keyup.enter" => "login"
);
$form['pass']['#attributes'] = array(
"v-model" => "password",
"@keyup.enter" => "login"
);
$form['actions']['submit']['#attributes'] = array(
"@click.prevent" => "login"
);
}
/**
* implements hook_block_view_BASE_BLOCK_ID_alter()
*
* https://www.drupal.org/project/drupal/issues/2626224
*/
function materio_user_block_view_user_login_block_alter(array &$build, BlockPluginInterface $block) {
$build['#pre_render'][] = '_materio_user_user_login_block_pre_render';
}
function _materio_user_user_login_block_pre_render(array $build){
$user_links = &$build['content']['user_links'];
$items = &$user_links['#items'];
// ksm($items);
$items['create_account']['#url']->mergeOptions(array(
"attributes" => array(
"@click.prevent" => "create_account"
)
));
$items['request_password']['#url']->mergeOptions(array(
'attributes' => array(
"@click.prevent" => "request_password"
)
));
return $build;
}

View File

@ -0,0 +1,15 @@
materio_user.login_form:
path: '/materio_user/login_form'
defaults:
_controller: '\Drupal\materio_user\Controller\AjaxLoginForm::getForm'
_format: json
requirements:
_access: 'TRUE'
materio_user.login_block:
path: '/materio_user/login_block'
defaults:
_controller: '\Drupal\materio_user\Controller\AjaxLoginBlock::getBlock'
_format: json
requirements:
_access: 'TRUE'

View File

@ -0,0 +1,54 @@
<?php
namespace Drupal\materio_user\Controller;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\Request;
use Drupal\block\Entity\Block;
use Symfony\Component\HttpFoundation\JsonResponse;
// use Drupal\Core\Cache\CacheableJsonResponse;
// use Drupal\Core\Cache\CacheableMetadata;
// use Drupal\core\render\RenderContext;
/**
* Defines a route controller.
*/
class AjaxLoginBlock extends ControllerBase {
private function getBlockDefinition(){
// $language = \Drupal::languageManager()->getCurrentLanguage()->getId();
// \Drupal::logger('materio_user')->notice($language);
$this->bid = "userlogin";
$this->block = Block::load($this->bid);
$this->block_builded = \Drupal::entityManager()->getViewBuilder('block')->view($this->block);
}
/**
* Handler for getBlock request.
*/
public function getBlock(Request $request) {
$this->getBlockDefinition();
$rendered = \Drupal::service('renderer')->renderRoot($this->block_builded);
$data = [
'rendered' => $rendered,
// '#cache' => [
// 'max-age' => \Drupal\Core\Cache\Cache::PERMANENT,
// 'tags' => [
// 'materio_sapi-search_form-cache',
// ]
// ]
];
$response = new JsonResponse();
$response->setData($data);
// $response = new CacheableJsonResponse($data);
// $response->addCacheableDependency(CacheableMetadata::createFromRenderArray($data));
return $response;
}
}

View File

@ -0,0 +1,82 @@
<?php
// https://www.qed42.com/blog/autocomplete-drupal-8
// https://www.drupal.org/docs/8/modules/search-api/developer-documentation/executing-a-search-in-code
namespace Drupal\materio_user\Controller;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Form\FormBuilder;
use Drupal\Core\Form\FormState;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
// use Drupal\Core\Cache\CacheableJsonResponse;
// use Drupal\Core\Cache\CacheableMetadata;
use Drupal\core\render\RenderContext;
/**
* Defines a route controller for entity autocomplete form elements.
*/
class AjaxLoginForm extends ControllerBase {
/**
* The form builder.
*
* @var \Drupal\Core\Form\FormBuilder
*/
protected $formBuilder;
/**
* Construct.
*/
public function __construct(FormBuilder $formBuilder) {
$this->formBuilder = $formBuilder;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('form_builder')
);
}
private function getFormDefinition(){
// $language = \Drupal::languageManager()->getCurrentLanguage()->getId();
// \Drupal::logger('materio_user')->notice($language);
$this->form_builded = $this->formBuilder->getForm('Drupal\user\Form\UserLoginForm');
}
/**
* Handler for getform request.
*/
public function getForm(Request $request) {
$this->getFormDefinition();
$rendered = render($this->form_builded);
// $form_builded = $this->form_builded;
// $rendered = \Drupal::service('renderer')->executeInRenderContext(new RenderContext(), function () use ($form_builded) {
// return render($form_builded);
// });
$data = [
'rendered' => $rendered,
// '#cache' => [
// 'max-age' => \Drupal\Core\Cache\Cache::PERMANENT,
// 'tags' => [
// 'materio_sapi-search_form-cache',
// ]
// ]
];
$response = new JsonResponse();
$response->setData($data);
// $response = new CacheableJsonResponse($data);
// $response->addCacheableDependency(CacheableMetadata::createFromRenderArray($data));
return $response;
}
}

View File

@ -1,6 +1,6 @@
<?php
namespace Drupal\user_block\Plugin\Block;
namespace Drupal\materio_user\Plugin\Block;
use Drupal\Core\Session\AccountProxy;
use Drupal\Core\Session\AccountProxyInterface;

View File

@ -1,5 +0,0 @@
name: 'user_block'
type: module
description: 'Provide a block with user name/email and logout link'
core: 8.x
package: 'Custom'

View File

@ -1,24 +0,0 @@
<?php
/**
* @file
* Contains user_block.module.
*/
use Drupal\Core\Routing\RouteMatchInterface;
/**
* Implements hook_help().
*/
// function user_block_help($route_name, RouteMatchInterface $route_match) {
// switch ($route_name) {
// // Main module help for the user_block module.
// case 'help.page.user_block':
// $output = '';
// $output .= '<h3>' . t('About') . '</h3>';
// $output .= '<p>' . t('Provide a block with user name/email and logout link') . '</p>';
// return $output;
//
// default:
// }
// }

File diff suppressed because one or more lines are too long

View File

@ -30,45 +30,27 @@ import 'theme/assets/styles/main.scss'
}
function initVues(){
initUserVBlock()
initVUserBlock()
initVMainContent()
initVSearchBlock()
}
function initUserVBlock(){
let mount_point = drupalSettings.user.uid !== 0 ? '#block-userblock' : '#block-userlogin';
function initVUserBlock(){
let mount_point = drupalSettings.user.uid !== 0 ? 'block-userblock' : 'block-userlogin';
let props = {
title: "",
form:""
loginblock:""
};
switch (mount_point) {
case '#block-userlogin':
// let $form = document.getElementById('user-login-form');
// console.log('login form html', $form);
// props.form = $form.outerHTML
let $block = document.querySelector(mount_point);
props = {
title: $block.querySelector('h2').textContent,
form: {
ph_email: $block.querySelector('input#edit-name').getAttribute('placeholder'),
ph_pass: $block.querySelector('input#edit-pass').getAttribute('placeholder'),
btn_value: $block.querySelector('input#edit-submit').getAttribute('value'),
register: {
title: $block.querySelector('a.create-account-link').textContent,
href: $block.querySelector('a.create-account-link').getAttribute('href')
},
reset: {
title: $block.querySelector('a.request-password-link').textContent,
href: $block.querySelector('a.request-password-link').getAttribute('href')
}
}
}
case 'block-userlogin':
let $block = document.getElementById(mount_point);
console.log('initVUserBlock login form html', $block);
props.loginblock = $block.outerHTML.trim()
break;
case '#block-userblock':
case 'block-userblock':
default:
break;
}
// console.log(props);
_v_user_block = new Vue({
store,
@ -78,14 +60,15 @@ import 'theme/assets/styles/main.scss'
// })
// },
created () {
// if already loggedin, call store.user to get the user infos
if(drupalSettings.user.uid !== 0){
this.$store.commit('User/setUid', drupalSettings.user.uid)
this.$store.dispatch('User/getUser')
}
},
render: h => h(VUserBlock, {props:props})
}).$mount(mount_point)
// console.log('initUserVBlock', _v_user_block);
}).$mount('#'+mount_point)
// console.log('initVUserBlock', _v_user_block);
}
function initVMainContent(){

View File

@ -52,7 +52,7 @@ header[role="banner"]{
&>section{
background-color: #fff;
overflow: hidden;
width:14em;
width:11em;
height:1px;
padding:0.01em 1em;
// margin:0 0 0 -1em;
@ -63,6 +63,7 @@ header[role="banner"]{
position: absolute;
right:0;
top:1.7em;
box-sizing: content-box;
}
&:hover{
&>section{
@ -72,6 +73,48 @@ header[role="banner"]{
box-shadow: 0 0 10px #ccc;
}
}
.form-item{
margin:0;
position: relative;
width:100%;
&.form-item-name{
margin:2px 0 0.5em 0;
input{
width:90%;
padding:0;
box-sizing: content-box;
}
}
&.form-item-pass{
margin:0 0 0.5em 0;
input{
width:90%;
padding:0;
box-sizing: content-box;
}
}
&.form-item-persistent-login{
font-size: 0.756em;
label{
vertical-align: top;
}
}
}
#edit-actions{
margin:0;
}
.item-list{
ul{
margin:0;
li{
list-style: none;
margin:0;
a{
font-size: 0.756em;
}
}
}
}
}
#block-languageswitcher{

View File

@ -4,8 +4,8 @@ import axios from 'axios'
// console.log('drupalSettings', drupalSettings);
export const MSAPI = axios.create({
baseURL: window.location.origin + '/' + drupalSettings.path.pathPrefix + `materio_sapi`,
export const MA = axios.create({
baseURL: window.location.origin + '/' + drupalSettings.path.pathPrefix,
withCredentials: true,
headers: {
"Content-Type": "application/json"

View File

@ -0,0 +1,84 @@
<script>
import Vue from 'vue'
import { mapState, mapActions } from 'vuex'
export default {
props: ['title', 'block'],
data () {
return {
template: null,
mail: '',
password: ''
}
},
computed: {
...mapState(['User'])
},
methods: {
...mapActions({
userLogin: 'User/userLogin'
}),
login () {
this.userLogin({
mail: this.mail,
pass: this.password
})
},
request_password () {
console.log('request_password');
},
create_account () {
console.log('create_account');
}
},
beforeMount() {
console.log('LoginBlock beforeMount', this._props.block);
if(this._props.block){
// console.log('LoginBlock beforeMount if this._props.block ok');
this.template = Vue.compile(this._props.block)
this.$options.staticRenderFns = [];
this._staticTrees = [];
this.template.staticRenderFns.map(fn => (this.$options.staticRenderFns.push(fn)));
}
},
mounted(){
// console.log('LoginBlock mounted');
Drupal.attachBehaviors(this.$el);
},
render(h) {
// console.log('LoginBlock render');
if(!this.template){
// console.log('LoginBlock render NAN');
return h('span', 'Loading ...')
}else{
// console.log('LoginBlock render template');
return this.template.render.call(this)
}
}
}
</script>
<style lang="scss" scoped>
// section{
// max-width:300px;
// }
// input{
// display:block;
// max-width:100%;
// margin:0.5em 0 0 0;
// }
// button{
// margin:0.5em 0 0 0;
// }
// ul{
// margin:0; padding:0.5em 0 0 0;
// }
// li{
// list-style:none;
// margin:0.5em 0 0 0; padding:0;
// font-size:0.882em;
// }
// a{
// }
</style>

View File

@ -9,7 +9,7 @@
<script>
import SearchForm from 'vuejs/components/Form/SearchForm'
import { mapState, mapActions } from 'vuex'
import { MSAPI } from 'vuejs/api/msapi-axios'
import { MA } from 'vuejs/api/ma-axios'
export default {
props: ['blockid', 'formhtml'],
@ -23,17 +23,17 @@ export default {
canSearch: state => state.User.canSearch
}),
displayform(){
console.log('computed displayform');
// console.log('computed displayform');
return this.canSearch && this.form
}
},
beforeMount() {
console.log('SearchBlock beforeMount');
// console.log('SearchBlock beforeMount');
this.form = this.formhtml
},
watch: {
canSearch(new_value, old_value) {
console.log('canSearch changed, old: '+old_value+", new: "+new_value);
// console.log('canSearch changed, old: '+old_value+", new: "+new_value);
if(new_value && !this.form){
this.getSearchForm()
}
@ -44,9 +44,9 @@ export default {
},
methods: {
getSearchForm(){
MSAPI.get(`/search_form`)
MA.get(`/materio_sapi/search_form`)
.then(({data}) => {
console.log("getSearchForm");
// console.log("getSearchForm");
this.form = data.rendered
})
.catch(( error ) => {

View File

@ -1,23 +1,51 @@
<template lang="html">
<UserTools v-if="isloggedin" />
<Login v-bind:title="title" v-bind:form="form" v-else />
<LoginBlock v-bind:title="title" v-bind:block="block" v-else/>
</template>
<script>
import { mapState, mapActions } from 'vuex'
import Login from 'vuejs/components/User/Login'
import LoginBlock from 'vuejs/components/Block/LoginBlock'
import UserTools from 'vuejs/components/User/UserTools'
import { MA } from 'vuejs/api/ma-axios'
export default {
props: ['title', 'form'],
props: ['title', 'loginblock'],
data(){
return {
block: null
}
},
computed: {
...mapState({
isloggedin: state => state.User.isloggedin
})
},
beforeMount() {
console.log('UserBlock beforeMount');
if(this.loginblock){
this.block = this.loginblock
}else{
this.getLoginBlock()
}
},
methods: {
getLoginBlock(){
MA.get(`/materio_user/login_block`)
.then(({data}) => {
// console.log("getLoginBlock data", data);
this.block = data.rendered
})
.catch(( error ) => {
console.warn('Issue with getLoginBlock', error)
})
}
},
components: {
Login,
LoginBlock,
UserTools
}
}

View File

@ -1,16 +1,15 @@
<script>
import Vue from 'vue'
import { MSAPI } from 'vuejs/api/msapi-axios'
export default {
props: ['form'],
data() {
return {
template: null,
typed: ""
}
},
props: ['form'],
methods: {
keyup() {
console.log("search typed", this.typed);
@ -20,66 +19,29 @@ export default {
}
},
beforeMount() {
console.log('SearchForm beforeMount');
// console.log('SearchForm beforeMount');
if(this._props.form){
console.log('SearchForm beforeMount if this._props.form ok');
this.template = Vue.compile(this._props.form).render
}
},
beforeUpdate() {
console.log('SearchForm beforeUpdate');
if(this._props.form){
console.log('SearchForm beforeUpdate if this._props.form ok');
this.template = Vue.compile(this._props.form).render
// console.log('SearchForm beforeMount if this._props.form ok');
this.template = Vue.compile(this._props.form)
// https://github.com/vuejs/vue/issues/9911
this.$options.staticRenderFns = [];
this._staticTrees = [];
this.template.staticRenderFns.map(fn => (this.$options.staticRenderFns.push(fn)));
}
},
mounted(){
console.log('SearchForm mounted');
// Drupal.attachBehaviors(this.$el);
},
updated(){
console.log('SearchForm updated');
// Drupal.attachBehaviors(this.$el);
// console.log('SearchForm mounted');
Drupal.attachBehaviors(this.$el);
},
render(h) {
console.log('searchForm render');
// console.log('searchForm render');
if(!this.template){
return h('span', 'Loading ...')
}else{
return this.template()
return this.template.render.call(this)
}
}
}
// searchform: (resolve, reject) => (
// MSAPI.get(`/search_form`)
// .then(({data}) => {
// // console.log("materiosapisearchform", data);
// resolve({
// data() {
// return {
// typed:""
// }
// },
// methods: {
// keyup() {
// console.log("search typed", this.typed);
// },
// submit() {
// console.log("search clicked");
// }
// },
// mounted() {
// Drupal.attachBehaviors(this.$el);
// },
// template: data.rendered
// })
// })
// .catch(( error ) => {
// console.warn('Issue with get searchform', error)
// Promise.reject(error)
// })
//
// )
</script>
<style lang="scss" scoped>