A first VueJS component, get api datas, a simple button add person

* !! composer require serializer !!
* see: http://localhost:8001/fr/parcours/861/show
This commit is contained in:
Mathieu Jaumotte 2021-04-21 00:10:46 +02:00
parent 69ea88a4d5
commit e7df62b373
12 changed files with 193 additions and 88 deletions

View File

@ -52,7 +52,8 @@
"symfony/browser-kit": "^5.2", "symfony/browser-kit": "^5.2",
"symfony/css-selector": "^5.2", "symfony/css-selector": "^5.2",
"twig/markdown-extra": "^3.3", "twig/markdown-extra": "^3.3",
"erusev/parsedown": "^1.7" "erusev/parsedown": "^1.7",
"symfony/serializer": "^5.2"
}, },
"conflict": { "conflict": {
"symfony/symfony": "*" "symfony/symfony": "*"

View File

@ -35,7 +35,7 @@ require('./modules/download-report/index.js');
require('./modules/select_interactive_loading/index.js'); require('./modules/select_interactive_loading/index.js');
require('./modules/export-list/export-list.scss'); require('./modules/export-list/export-list.scss');
require('./modules/entity/index.js'); require('./modules/entity/index.js');
require('./modules/tabs/index.js'); //require('./modules/tabs/index.js');
/* /*
* load img * load img

View File

@ -0,0 +1,87 @@
<template>
<div class="vue-component">
<dl>
<dt>id</dt>
<dd>{{ accompanying_course.id }}</dd>
<dt>opening_date</dt>
<dd>{{ accompanying_course.opening_date }}</dd>
<dt>closing_date</dt>
<dd>{{ accompanying_course.closing_date }}</dd>
<dt>remark</dt>
<dd>{{ accompanying_course.remark }}</dd>
<dt>closing_motive</dt>
<dd>{{ accompanying_course.closing_motive }}</dd>
</dl>
<label>{{counter}} usagers concernés</label>
<table class="rounded">
<thead>
<tr>
<th class="chill-orange">firstname</th>
<th class="chill-orange">lastname</th>
<th class="chill-orange">startdate</th>
<th class="chill-orange">enddate</th>
<th class="chill-orange">actions</th>
</tr>
</thead>
<tbody>
<tr v-for="person in accompanying_course.persons">
<td>{{ person.firstname }}</td>
<td>{{ person.lastname }}</td>
<td>{{ person.startdate }}</td>
<td>{{ person.enddate }}</td>
<td>
<ul class="record_actions">
<li><a class="sc-button bt-show"></a></li>
<li><a class="sc-button bt-update"></a></li>
<li><a class="sc-button bt-delete"></a></li>
</ul>
</td>
</tr>
</tbody>
</table>
<ul class="record_actions">
<li>
<button class="sc-button bt-create" @click="addPerson">Add Person</button>
</li>
</ul>
</div>
</template>
<script>
import Vue from 'vue'
import VueResource from 'vue-resource'
Vue.use(VueResource)
export default{
data () {
return {
accompanying_course: []
}
},
http: {
root: 'http://localhost:8001/fr/api'
},
methods: {
addPerson(){
this.accompanying_course.persons.push({"firstname": "Lisa", "lastname": "Simpson", "startdate": "1975-09-15", "enddate": "2021-04-20" })
}
},
computed: {
counter() {
return this.accompanying_course.persons.length
}
},
mounted() {
this.$resource('parcours/861/show').get().then((response) => {
this.accompanying_course = response.data
}, (response) => {
console.log('erreur', response);
})
}
}
</script>
<style>
</style>

View File

@ -1,19 +1,19 @@
<template> <template>
<div id="app"> <div id="app">
<!-- la balise contacts corresponds au composant contacts passé ci-dessous --> <!-- la balise contacts corresponds au composant accompanying_courses passé ci-dessous -->
<contacts></contacts> <accompanying_course></accompanying_course>
</div> </div>
</template> </template>
<script> <script>
// importe dans contacts les datas d'une requête rest GET en json // importe dans accompanying_course les datas d'une requête rest GET en json
import contacts from './Contacts.vue' import accompanying_course from './AccompanyingCourse.vue'
export default { export default {
name: 'app', name: 'app',
components: { components: {
// passe l'objet contacts comme composant // passe l'objet accompanying_course comme composant
contacts, accompanying_course,
} }
} }
</script> </script>

View File

@ -1,59 +0,0 @@
<template>
<div>
<table>
<tr v-for="contact in contacts">
<td>{{contact.firstname}}</td>
<td>{{contact.lastname}}</td>
</tr>
</table>
<button @click="addContact">Add</button>
<label>#contacts {{counter}}</label>
</div>
</template>
<script>
// la balise script dans ce fichier vue, est un peu comme une classe Contacts
// namespace components/Contacts
import Vue from 'vue'
import VueResource from 'vue-resource'
// injection de dépendance pour dealer du REST json
Vue.use(VueResource)
export default{
// initialise les données
data () {
return {
// constructeur du tableau-objet vide 'contacts' qui est passé à App.vue
contacts: []
}
},
// Rest GET json
http: {
root: 'http://localhost:3000'
},
// méthodes
methods: {
addContact(){
this.contacts.push({"firstname":"Lisa", "lastname":"Simpson", "age":"10"})
}
},
// traitements
computed:{
counter(){
return this.contacts.length
}
},
// on dirait un setter
mounted() {
this.$resource('contacts').get().then((response) => {
this.contacts = response.data
}, (response) => {
console.log('erreur', response);
})
}
}
</script>
<style>
</style>

View File

@ -2,6 +2,6 @@ import Vue from 'vue'
import App from './components/App.vue' import App from './components/App.vue'
new Vue({ new Vue({
el: '#app', el: '#app',
render: h => h(App) render: h => h(App)
}) })

View File

@ -35,7 +35,23 @@ div#header-accompanying_course-details {
/* /!\ Contourne le positionnement problématique du div#content_conainter suivant, /* /!\ Contourne le positionnement problématique du div#content_conainter suivant,
* car sa position: relative le place au-dessus du bandeau et les liens sont incliquables */ * car sa position: relative le place au-dessus du bandeau et les liens sont incliquables */
div.subheader { div.subheader {
height: 130px; height: 130px;
} }
div.vue-component {
padding: 1.5em;
margin: 2em 0;
border: 2px dashed grey;
position: relative;
&:before {
content: "vuejs component";
position: absolute;
left: 1.5em;
top: -0.9em;
background-color: white;
color: grey;
padding: 0 0.3em;
}
dd { margin-left: 1em; }
}

View File

@ -3,10 +3,18 @@
namespace Chill\PersonBundle\Controller; namespace Chill\PersonBundle\Controller;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
use Chill\PersonBundle\Entity\Person;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\SerializerInterface;
/** /**
* Class AccompanyingCourseController * Class AccompanyingCourseController
@ -58,6 +66,53 @@ class AccompanyingCourseController extends Controller
'accompanyingCourse' => $accompanyingCourse 'accompanyingCourse' => $accompanyingCourse
]); ]);
} }
/**
* Sérialise temporairement quelques données pour donner à manger au composant vuejs
* @Route(
* "/{_locale}/api/parcours/{accompanying_period_id}/show",
* name="chill_person_accompanying_course_api_show")
* @ParamConverter("accompanyingCourse", options={"id": "accompanying_period_id"})
* @param SerializerInterface $serializer
*/
public function showAPI(AccompanyingPeriod $accompanyingCourse): Response
{
$persons = [];
foreach ($accompanyingCourse->getParticipations() as $k => $participation ) {
/**
* @var AccompanyingPeriodParticipation $participation
* @var Person $person
*/
$person = $participation->getPerson();
$persons[$k] = [
'firstname' => $person->getFirstName(),
'lastname' => $person->getLastName(),
'email' => $person->getEmail(),
'phone' => $person->getPhonenumber(),
'startdate' => ($participation->getStartDate()) ? $participation->getStartDate()->format('Y-m-d') : null,
'enddate' => ($participation->getEndDate()) ? $participation->getEndDate()->format('Y-m-d') : null
];
}
$data = [
'id' => $accompanyingCourse->getId(),
'remark' => $accompanyingCourse->getRemark(),
'closing_motive' => $accompanyingCourse->getClosingMotive()->getName()['fr'],
'opening_date' => ($accompanyingCourse->getOpeningDate()) ? $accompanyingCourse->getOpeningDate()->format('Y-m-d') : null,
'closing_date' => ($accompanyingCourse->getClosingDate()) ? $accompanyingCourse->getClosingDate()->format('Y-m-d') : null,
'persons' => $persons
];
$normalizer = [new ObjectNormalizer()];
$encoder = [new JsonEncoder()];
$serializer = new Serializer($normalizer, $encoder);
$serialized = $serializer->serialize($data,'json', []);
$response = new Response($serialized);
$response->headers->set('Content-Type', 'application/json');
return $response;
}
} }

View File

@ -251,7 +251,7 @@ class AccompanyingPeriod
* *
* @return \DateTime * @return \DateTime
*/ */
public function getClosingDate() public function getClosingDate(): ?\DateTime
{ {
return $this->closingDate; return $this->closingDate;
} }
@ -425,15 +425,17 @@ class AccompanyingPeriod
* *
* @return boolean * @return boolean
*/ */
public function isClosingAfterOpening() public function isClosingAfterOpening(): bool
{ {
if (null === $this->getClosingDate()) {
return false;
}
$diff = $this->getOpeningDate()->diff($this->getClosingDate()); $diff = $this->getOpeningDate()->diff($this->getClosingDate());
if ($diff->invert === 0) { if ($diff->invert === 0) {
return true; return true;
} else {
return false;
} }
return false;
} }
function getUser(): ?User function getUser(): ?User
@ -448,7 +450,7 @@ class AccompanyingPeriod
return $this; return $this;
} }
public function getOrigin(): Origin public function getOrigin(): ?Origin
{ {
return $this->origin; return $this->origin;
} }
@ -540,7 +542,7 @@ class AccompanyingPeriod
return $this; return $this;
} }
public function getStep(): string public function getStep(): ?string
{ {
return $this->step; return $this->step;
} }
@ -552,7 +554,7 @@ class AccompanyingPeriod
return $this; return $this;
} }
public function getIntensity(): string public function getIntensity(): ?string
{ {
return $this->intensity; return $this->intensity;
} }

View File

@ -369,7 +369,7 @@ class Person implements HasCenterInterface
/** /**
* Return the opened accompanying period. * Return the opened accompanying period.
*/ */
public function getOpenedAccompanyingPeriod() : AccompanyingPeriod public function getOpenedAccompanyingPeriod() : ?AccompanyingPeriod
{ {
if ($this->isOpen() === false) { if ($this->isOpen() === false) {
return null; return null;

View File

@ -8,6 +8,7 @@
<h1>{{ block('title') }}</h1> <h1>{{ block('title') }}</h1>
{#
<pre> <pre>
{{ accompanyingCourse.id }} {{ accompanyingCourse.id }}
{{ accompanyingCourse.openingDate|format_date('short') }} {{ accompanyingCourse.openingDate|format_date('short') }}
@ -20,10 +21,9 @@ usagers:
{{ p.person.id }} | <a href="{{ path('chill_person_accompanying_period_list', { person_id: p.person.id }) }}">{{ p.person.fullnamecanonical }}</a> | {{ p.startdate|format_date('short') }} | {{ p.enddate|format_date('short') }} {{ p.person.id }} | <a href="{{ path('chill_person_accompanying_period_list', { person_id: p.person.id }) }}">{{ p.person.fullnamecanonical }}</a> | {{ p.startdate|format_date('short') }} | {{ p.enddate|format_date('short') }}
{% endfor %} {% endfor %}
</pre> </pre>
{{ dump() }} {{ dump() }}
#}
<h3>TESTS AREA vuejs:</h3>
{% verbatim %} {% verbatim %}
<div id="app" data-name="{{ app.user.username }}"></div> <div id="app" data-name="{{ app.user.username }}"></div>
{% endverbatim %} {% endverbatim %}

View File

@ -38,3 +38,6 @@ services:
$personMove: '@Chill\PersonBundle\Actions\Remove\PersonMove' $personMove: '@Chill\PersonBundle\Actions\Remove\PersonMove'
$eventDispatcher: '@Symfony\Component\EventDispatcher\EventDispatcherInterface' $eventDispatcher: '@Symfony\Component\EventDispatcher\EventDispatcherInterface'
tags: ['controller.service_arguments'] tags: ['controller.service_arguments']
Chill\PersonBundle\Controller\AccompanyingCourseController:
tags: ['controller.service_arguments']