Symfony2 : customize form labels in form collections - doctrine-orm

I'm trying to customize form labels that are generated in subforms.
I want to display soccer fixtures that are contained in a specific game week, like the following:
- Fixture 1 : Manchester United (0) - (1) Arsenal
- Fixture 2 : Chelsea (2) - (1) Liverpool
- ...
My form displays all fixtures and related scores but all labels contain the database column names (score1, score2). I want to put team names instead.
So, it currently shows:
- Fixture 1 : score1 (0) - (1) score2
- Fixture 2 : score1 (2) - (1) score2
- ...
In the controller, I generate the week form (WeekType). $week contains week data and fixtures data using $week->getFixtures().
Controller/DefaultController.php
$form = $this->createForm(new WeekType(), $week)->createView();
return array(
'form' => $form,
);
Form/WeekType.php
class WeekType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('fixtures', 'collection', array(
'type' => new FixtureType(),
));
}
}
The Fixture form adds 2 fields. I want to replace default labels into team names.
However I cannot access fixture data in this form. $options is NULL. I thought $options['data'] would contain fixtures data... but I was wrong.
Form/FixtureType.php
class FixtureType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('score1', 'text', array('label' => **WHAT TO PUT HERE**));
$builder->add('score2', 'text', array('label' => **WHAT TO PUT HERE**));
}
}
I display all fixtures using this code, and it works great.
index.html.twig
{% for fixture in week.form.fixtures %}
{{ form_widget(fixture) }}
{% endfor %}
Maybe I could customize my labels directly in index.html.twig but how can I get fixtures data?
Does somebody encounter this issue and resolve it?

I found a solution!
In "index.html.twig" template, I iterated over form elements.
It was a mistake. I just had to iterate over fixtures and get related form widget.
index.html.twig
{% for fixture in week.fixtures %}
fixture.HomeTeam.name
{{ form_widget(week.form.fixtures[loop.index0]) }}
fixture.AwayTeam.name
{% endfor %}
The trick is to retrieve form elements directly from form widgets array :
week.form.fixtures[loop.index0]

http://symfony.com/doc/current/book/forms.html#rendering-each-field-by-hand
use
{{ form_label(fixture) }} <- {{ team.name }}
{{ form_errors(fixture) }}
{{ form_widget(fixture) }}
instead of
{{ form_widget(fixture) }}

Related

Extends base.twig when it is with passed entity

I am making some small project with Symfony, but now I have a question about base.twig.html, and does it should be passed with some entity in it. I am using Symfony 4.
Lets say I have some entity MainCategory with:
id
name
info
slug
Then from my Route /, I use MainCategoryRepository to get all records for that entity, then render the base.twig with the passed records.
Then in navigation bar, I loop over all records and make links with the entity.name.
After that in another Route "/{slug}", i render another twig file which extends the base one. But here i am not using the repository, i use just the Entity - MainCategory, because the 'slug' is unique field and it will get only that record.
And here is the problem, i am just passing one record, but the navigation bar from the 'base.twig' loop over all records and here I get an error:
Variable "mainCategories" does not exist.
Here is my code:
Route "/"
/**
* #Route("/", name="app_homepage")
*/
public function homepage(MainCategoryRepository $mainCategoryRepository)
{
$mainCategories = $mainCategoryRepository->findAll();
return $this->render('base.html.twig', [
'mainCategories' => $mainCategories,
]);
}
"base.html.twig"
{% for mainCategory in mainCategories %}
<li>
<a href="{{ path('category_show', {'slug': mainCategory.slug}) }}">
{{ mainCategory.name }}</a>
</li>
{% endfor %}
Route "/{slug}"
/**
* #Route("/{slug}", name="category_show")
*/
public function categoryShow(MainCategory $mainCategory)
{
return $this->render('show.html.twig',
[
'mainCategory' => $mainCategory,
]
);
}
"show.html.twig"
{% extends 'base.html.twig' %}
{% block body %}
<p>{{ mainCategory.Info }}</p>
{% endblock %}
ERROR:
Variable "mainCategories" does not exist.
So it is good idea to pass any entity in base.twig? I can pass all records in the second twig file, but that means I have to do it in every template which extends the base.twig.
I found one solution, dont know is it perfect or the right solution, but it works.
I made one Service with a method in which I return all records from MainCategory Entity.
MainCategoryLoaderService
protected $mainCategoryRepository;
public function __construct(MainCategoryRepository $mainCategoryRepository)
{
$this->mainCategoryRepository = $mainCategoryRepository;
}
public function getAllMainCategories()
{
return $this->mainCategoryRepository->findAll();
}
Then in the file config/packages/twig.yaml I made one global variable:
Global Variable
globals:
globalMainCategory: '#App\Service\MainCategoryLoaderService'
After that in base.html.twig I made one variable in which I will have all main categories:
Twig Variable
{% set mainCategories = globalMainCategory.getAllMainCategories() %}
And then just loop over that variable.

How to use function with arguments in IF statement in the template

Currently Im trying to use a function that observes a field of a controller/component in ember template (handlebars).
index.hbs
<div>
{{ input value=model.field1 }}
{{if hasFieldError('field1')}}
<span>This field is required</span>
{{/if}}
</div>
<div>
{{ input value=model.field2 }}
{{if hasFieldError('field2')}}
<span>This field is required</span>
{{/if}}
</div>
index.js
hasFieldError: function(val) {
return true if val is found in an array
}.observes('field1', 'field2'),
But this of course returns a build error:
{#if hasFieldError('compa ----------------------^ Expecting
'CLOSE_RAW_BLOCK', 'CLOSE', 'CLOSE_UNESCAPED', 'OPEN_SEXPR',
'CLOSE_SEXPR', 'ID', 'OPEN_BLOCK_PARAMS', 'STRING', 'NUMBER',
'BOOLEAN', 'UNDEFINED', 'NULL', 'DATA', 'SEP', got 'INVALID'
Any idea how to achieve this?
You can't call a function from a template, except using an action. You can only reference properties, such as fields and computed properties.
Observers are generally not a good idea
Are you really trying to determine if the value of field1 is in an array? Let's assume the array is found at array1. Then you could write a helper named contains:
{{#if (contains array1 field1)}}
But someone has already written this. Welcome to the wonderful Ember community of addons! See https://github.com/DockYard/ember-composable-helpers#contains
Just replace your observer function with a computed property:
hasFieldError: Ember.computed('field1', 'field2', function(val) {
return true if val is found in an array
}),

Laravel Collective Checkbox return null value from form

I'm trying to figure out the collective's checkbox function.
Here are the code in the blade template:
{{ Form::model($record, ['route' => ['access_record.update', $record->id], 'method' => 'put']) }}
{{ Form::checkbox('IsApprove', 1, true) }}
{{ Form::submit() }}
{{ Form::close() }}
as you see, I set up the checkbox named: IsApprove and give it the default value. However, I can not receive any value from pages.
Here are the codes in the controller's update function:
public function update(Request $request, $id)
{
$record = Accessrecord::find($id);
print_r($request->toArray());
}
And there is nothing to print out.
How do I make the collective's checkbox retrieve value?
Thanks!
I guess I find out the answer. It seems my apache server got crush. After restart apache server, it fixed.

Trying to access Django REST API in Angular2

I am working on a project where we use Angular2 and a Django backend that has a rest API. Right now we are simply just trying to get the django backend to send JSON objects to Angular2. For some reason when we make get requests, it comes back as blank. Right now we just have dummy test functions, but even those don't work.
/_services/user.service.ts
tempFunc() {
return this.http.get('http://localhost:8000/chains/', this.jwt()).map((model: Response) => model.json());
}
/temptemp-page.component.ts
import { Router } from '#angular/router';
import { UserService } from '../_services/user.service';
export class Temp {
name : string;
description : string;
slogan : string;
founded_date : string;
website : string;
}
#Component({
selector: 'app-temp-page',
templateUrl: './temp-page.component.html',
styleUrls: ['./temp-page.component.css']
})
export class TempPageComponent implements OnInit {
model: Temp;
constructor(
private router: Router,
private userService: UserService) { }
ngOnInit() {
this.model = {
name: 'Wrong',
description: 'Wrong',
slogan: 'Wrong',
founded_date: 'Wrong',
website: 'Wrong',
}
this.userService.tempFunc().subscribe(model => this.model = model);
console.log(this.model);
}
}
The Wrong is there just to know that if we get nothing, it will print Wrong and we know the get request isn't succeeding.
temp-page-componenent.html
<div style="margin-left: 20px;">
name: {{ this.model.name }} <br/>
description: {{ this.model.description }} <br/>
slogan: {{ this.model.slogan }} <br/>
founded_date: {{ this.model.founded_date }} <br/>
website: {{ this.model.website }} <br/>
<hr/>
</div>
The django backend has a model with field of the type above in this html file, in a table called Chains. at that URL specified. For some reason, every attempt to call it works. Except for Angular2, and I am asking this to figure out if there is just bad syntax, or something else related to the problem. I know it works because when I do
curl -g localhost:8000/chains/
It works fine and returns
[{"name":"Cafe Amazing","description":"Founded to serve the best sandwiches.","slogan":"The best cafe in the Mississippi!","founded_date":"2014-12-04T20:55:17Z","website":"http://www.thecafeamazing.com"}]
with a sucess code on the django server of 200 204.
However when I try the angular2 code above it returns the same codes but nothing gets displayed. What am I doing wrong here?
don't use this in your template use only the property name, like this :
<div style="margin-left: 20px;">
name: {{ model.name }} <br/>
description: {{ model.description }} <br/>
slogan: {{ model.slogan }} <br/>
founded_date: {{ model.founded_date }} <br/>
website: {{ model.website }} <br/>
<hr/>
</div>
you tempFunc is asynchronous so console.log(this.model) will be executed first :
this.userService.tempFunc().subscribe(model => this.model = model);
console.log(this.model);
do this :
this.userService
.tempFunc()
.subscribe(model =>{
this.model = model
console.log(this.model);
});

Django - Javascript dynamic inline FormSet with autocomplete

I'm trying to make a kind of scheduler event editor with the ability to attach participants.
Models
class Session(models.Model):
start_time = models.DateTimeField()
end_time = models.DateTimeField()
class Participation(models.Model):
session = models.ForeignKey(Session)
participant = models.ForeignKey(User)
status = models.CharField(max_length=1, choices=STATUSES)
In the editor I'd like to have an autocomplete search input from which I can find users to add to the session
Preview
Here I have typed "laurent" and I'm going to add a person by clicking on one of the resulting names
Participant colors depend on their status
I have a form for the Session object defined with start & end times
Now I think I should have an inline formset for Participations
Questions
Do you suggest that I use an inline formset for the participants ?
How can I dynamically add/delete participant rows ?
The question seems very simple but a proper response would involve several answers.
I will give my solutions point by point, using jQuery.
Autocomplete
This is the simple part. You can use a plugin like select2 or jqueryui autocomplete and a view that finds users like
def search_users(request):
search = request.GET.get('term')
users = User.objects.filter(
Q(first_name__icontains=search)
| Q(last_name__icontains=search)
)
ulist = list({'id': u.id, 'value': u'%s %s' % (u.first_name, u.last_name)}
for u in users)
return JsonResponse(ulist)
This view is compatible with the default jQuery UI Autocomplete plugin
Dynamic Formset
This is the tricky one. The key is to take advantage of management_form and form.DELETE. Here is my solution:
Use an inline formset for the participants (with one extra form)
Print the management_form
Add form lines with jQuery after autocomplete selection by cloning a hidden empty form (the extra one) and incrementing id_form-TOTAL_FORMS
Delete form lines with jQuery by hiding them and checking a hidden delete checkbox
Template
<form method="post">{% csrf_token %}
{{ sessionform }}
<div>
{{ participant_formset.management_form }}
<label for="part_search">Search: </label><input id="part_search" />
<ul id="participation_set">
{% for tform in participant_formset %}
{{ tform.id }}
<li>
<span class="participant">
{{ tform.participant }}{{ tform.instance.participant.name }}
</span>
<span class="status">{{ tform.status }}</span>
<span class="delete ui-icon ui-icon-circle-minus">
{{ tform.DELETE }}
</span>
</li>
{% endfor %}
</ul>
</div>
</form>
CSS
/* Delete button */
#participation_set .delete {
display: inline-block;
vertical-align: middle;
cursor: pointer;
}
/* Hidden delete checkbox */
#participation_set .delete input {
display: none;
}
/* Deleted form */
#participation_set li.deleted {
display: none;
}
/* Last hidden form to clone */
#participation_set li:last-child {
display: none;
}
jQuery
/*! This adds a form line
* Call it on autocomplete select
*/
function add_aform(inst, item) {
if ($(':input[name$="participant"][value=' + item.id + ']').length) {
return false;
}
var total = $('#id_' + inst + '-TOTAL_FORMS').val();
var sul = '#' + inst;
var li = $(sul + ' li:last-child');
var new_li = li.clone().appendTo(sul);
li.find('span.participant').append(item.label);
li.find(':input[name$="participant"]').val(item.id);
new_li.find(':input').each(function () {
var new_name = $(this).attr('name')
.replace('-' + (total - 1) + '-', '-' + total + '-');
$(this).attr('name', new_name);
});
new_li.find('label').each(function () {
var tmp = $(this).attr('for')
.replace('-' + (total - 1) + '-', '-' + total + '-');
$(this).attr('for', new_for);
});
new_li.find('.delete').click(del_aform);
$('#id_' + inst + '-TOTAL_FORMS').val(++total);
}
/*! This removes a form line
* Call it on click from delete buttons (placed inside each li)
*/
function del_aform() {
$(this).parents('li').addClass('deleted');
$(this).find(':checkbox').attr('checked', true);
}
I know I could also use an empty_form instance and use __prefix__ to replace the ids which simplifies the javascript for a better maintainability, but I didn't find a way to factorize the code between the true form and the empty one.
View
The view is pretty standard using inlineformset_factory with extra set to 1 (to get the only hidden form to clone). Also don't forget to use a HiddenInput widget for the field participant
This plugin would help you get the auto-complete functionality you're looking for:
https://github.com/millioner/django-ajax-select