8 – Name completion using @xyz and email notification in comment form

Many web based applications now allow using @xyz to trigger an email notification to existing user. This is nice UX pattern to trigger notification which does not require explicit subscription to an item, provided such notification are enabled by user at site level.

Parsing the @NAME from comment and trigger email is relatively straightforward. However I am wondering how to go about implementing autocompletion when user types @xyz in CKEditor for comments.

We have implemented autocompletion on dedicated text field that are for inputing usernames, but it’s unclear how to implement this when a user adds free text via CKEditor on comment form. The user can include @name anywhere and multiple times.

Is there any reference module that does this or other ideas?
Thanks

Google forms – using as feedback form for multiple schools

I am sending a link for a Google form in an email, asking for course feedback.

I send the same link to multiple schools.

When I get responses from the schools, I like to share the student responses with the teacher/ school, using the ‘summary’ – so easy to read charts.

However, students from multiple schools respond at different times.

Is there anyway of separating School A from School B (and C !) and sending the summary report to the school ?

The answers are going to a spreadsheet, and I can sort there, but then I don’t know how to (or if possible) to produce the ‘summary’ report ?

I want to send Google Form response to an Email address entered on the form

When a user completes the form to attend a conference. I ask for another email address so their supervisor knows they have signed up and for what date.

How can I use the field on the form as the address to cc the Email to.
the field is “Supervisors Email address”

I would like it to send as soon as they submit the form.

magento2 – How to modify data of Custom Field before rendering to backend form?

I have created a simple crud module that renders form with a few basic fields and saves data into database. Module is working fine and saving data correctly in the database.

Issue is before rendering form value I want to add a prefix to URL, something like this.

public function getInstagramLink()
{
    return 'https:'.$this->getData('instagram_link');
}

I have added this method within Model but that doesn’t seem to be working

namespace VendorModuleModel;

class Instalocator extends MagentoFrameworkModelAbstractModel
{
    protected function _construct()
    {
        $this->_init(VendorModuleModelResourceModelInstalocator ::class);
    }

    public function getInstagramLink()
    {
        return 'https:'.$this->getData('instagram_link');
    }
}

This doesn’t seem to be working. How to do this correctly to set/get value before/after saving into database?

Pattern for a form state/content, after an update data fail

I’m wondering about something: usually when you have a form on your website, a user fills it in then saves it. If the saving operation fails, is considered as a good UX practice the fact of restoring the form with the data that was entered, as the user shouldn’t have to type it again before retry.

That’s the behavior I expect for edit areas, checkboxes and most of UI elements in which can be entered data.

But some days ago, I was using the following kind of element: a dropdown containing a tree, implemented as checkboxes (see picture). I checked all the checkboxes, hitted “save”, which failed. The dropdown remained in the same state, I mean all of the checkboxes that I selected were still selected. “Normal” behavior, as said before.
But it seemed weird to me! And I really don’t know why, I was expecting that the dropdown content would have been “reseted” to its actual state. Why was I thinking that behavior? Am I used to it? From where?

enter image description here

So for now I’m trying to find out some examples, some advices or any other information about: this kind of UI element, your feelings about that or even better your feedback if you have encoutered such a situation.
Thanks!

formularios – Abrir un Form con el patron Singleton (Duda Teorica)

Mi duda es la siguiente. Tengo 6 formularios que deseo abrir solo una vez. Tengo entonces que implemetar el codigo para cada uno de ellos, o existe la posibilidad de que lo pueda hacer generico para cualquier formulario para poder reutilizar codigo. Gracias. Este es mi codigo que funciona OK, para uno de mis formularios que se llama Curva.

public class AbrirForm
{
    private static Curva miformulario = null;
    private AbrirForm() { }

    public static Curva Formulario()
    {
       if(miformulario == null || miformulario.IsDisposed == true)
       {
          miformulario = new Curva();
       }

       return miformulario;   
    }
}

private void button1_Click(object sender, EventArgs e)
{
   Curva x = AbrirForm.Formulario();
   x.Show();
}

css – Como colocar um botão que está no form e na mesma linha em uma coluna de tabela?

Meu problema é o seguinte:
Estou usando bootstrap4 e gostaria de colocar os três botões na mesma linha.
como procedo?

inserir a descrição da imagem aqui

<td class="col-4">
    <a href="/FAQCategory/{{ $FAQCategory->id }}" class="btn btn-success btn-sm">Detalhar</a>
    <a href="/FAQCategory/{{ $FAQCategory->id }}/edit" class="btn btn-primary btn-sm">Editar</a>
    <form action="FAQCategory/{{ $FAQCategory->id }}" method="post">
        @csrf
        @method('delete')
        <a class="btn btn-danger btn-sm">Apagar</a>
    </form>
</td>

theming – Adding Drupal 8/9 Commerce Wishlist Share Mail Template Variables from Form

I am trying to add 2 additional form fields to the Wishlist Share form where the user input will be rendered in the email. I have been able to add the fields to the form, but I am not sure how to add the user’s input in the email twig template.

Here is how I have updated the form() function:

  public function form(array $form, FormStateInterface $form_state) {
    $form('#tree') = TRUE;
    $form('#attached')('library')() = 'core/drupal.dialog.ajax';
    // Workaround for core bug #2897377.
    $form('#id') = Html::getId($form_state->getBuildInfo()('form_id'));

    $form('to') = (
      '#type' => 'email',
      '#title' => $this->t('Recipient Email'),
      '#required' => TRUE,
    );

// COMBAK my edit

    $form('sender_name') = (
      '#type' => 'textfield',
      '#title' => $this->t('Your Name'),
      '#required' => FALSE,
    );

    $form('sender_message') = (
      '#type' => 'textarea',
      '#title' => $this->t('Your Message'),
      '#required' => FALSE,
    );

// COMBAK eo my edit

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  protected function actions(array $form, FormStateInterface $form_state) {
    $actions('submit') = (
      '#type' => 'submit',
      '#value' => $this->t('Send email'),
      '#submit' => ('::submitForm'),
    );
    if ($this->isAjax()) {
      $actions('submit')('#ajax')('callback') = '::ajaxSubmit';
    }

    return $actions;
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    /** @var Drupalcommerce_wishlistEntityWishlistInterface $wishlist */
    $wishlist = $this->entity;
    $to = $form_state->getValue('to');

    // COMBAK: my added vars
    $sender_name = $form_state->getValue('sender_name');
    $sender_message = $form_state->getValue('sender_message');

    $this->wishlistShareMail->send($wishlist, $to, $sender_name, $sender_message);

    $this->messenger()->addStatus($this->t('Shared the wishlist to @recipient.', (
      '@recipient' => $to,
    )));
    $form_state->setRedirectUrl($wishlist->toUrl('user-form'));
  }

This is the function that calls the mailHandler that I have updated:

public function send(WishlistInterface $wishlist, $to, $sender_name, $sender_message) {
    $owner = $wishlist->getOwner();

    $subject = $this->t('Check out my @site-name wishlist', (
      '@site-name' => $this->configFactory->get('system.site')->get('name'),
    ));
    $body = (
      '#theme' => 'commerce_wishlist_share_mail',
      '#wishlist_entity' => $wishlist,
      // COMBAK: my added vars
      '#sender_name' => $sender_name,
      '#sender_message' => $sender_message,
    );
    $params = (
      'id' => 'wishlist_share',
      'from' => $owner->getEmail(),
      'wishlist' => $wishlist,
    );

    return $this->mailHandler->sendMail($to, $subject, $body, $params);
  }

And this is the preprocees function provided by the commerce wishlist module:

function template_preprocess_commerce_wishlist_share_mail(array &$variables) {
  /** @var Drupalcommerce_wishlistEntityWishlistInterface $wishlist */
  $wishlist = $variables('wishlist_entity');
  $wishlist_url = $wishlist->toUrl('canonical', ('absolute' => TRUE));

  $variables('wishlist_url') = $wishlist_url->toString();

  // COMBAK: my added vars
  //$sender_name = $variables('sender_name');
}

And finally the twig template for the email itself:

{#
/**
 * @file
 * Template for the wishlist share email.
 *
 * Available variables:
 * - wishlist_entity: The wishlist entity.
 * - wishlist_url: The wishlist url.
 *
 * @ingroup themeable
 */
#}
<p>
  {% trans %}Check out my wishlist!{% endtrans %}
</p>

<p>
  {% trans %}I use my wishlist for keeping track of items I am interested in.{% endtrans %} <br>
  {% trans %}To see the list in the store and buy items from it, <a href="{{ wishlist_url }}">click here</a>.{% endtrans %}
</p>

<p>
  {% trans %}Thanks for having a look!{% endtrans %}
</p>

I haven’t been able to figure out how to access the variables I added to the body() array in the twig template.

Any help would be greatly appreciated.

Thanks!

typescript – Angular Material form issue: angular mat-form-field must contain MatFormFieldControl

I have created a new angular form, that includes both text input and now a select box. The problem is that when I run the code, I get the error:

mat-form-field must contain a MatFormFieldControl

This error only showed, up after I attempted to add the select box, but I am not sure what I am getting the error.
new.html:

<form (formGroup)='ResolutionFormGroup'>
    <mat-form-field>
        <mat-label>Title</mat-label>
        <input (formControl)='TitleControl' matInput placeholder="Title" required/>
    </mat-form-field>
    <mat-form-field>
        <mat-label>Description</mat-label>
        <input (formControl)='DescriptionControl' matInput placeholder="Description" required/>
    </mat-form-field>
    <mat-form-field>
        <select (formControl)='CategoryControl' matInput required>
            <option>This</option>
        </select>
    </mat-form-field>
</form>

If you comment out the last <mat-form-field> section, then the code will run as expected, and I get no errors.
new.ts

@Component({
  selector: 'app-new-resolution',
  templateUrl: './new-resolution.component.html',
  styleUrls: ('./new-resolution.component.scss')
})
export class NewResolutionComponent implements OnInit {
  uid: string
  category_id: string
  title: string
  description: string
  categoryList = ()

  TitleControl: AbstractControl
  DescriptionControl: AbstractControl
  UserIdControl: AbstractControl
  CategoryControl: AbstractControl
  ResolutionFormGroup: FormGroup

  constructor(public as: AuthService,
    public rs: ResolutionsService,
    public dialog: MatDialogRef<NewResolutionComponent>) { }

  ngOnInit(): void {
    this.ResInit()
    this.rs.GetCategories().subscribe(cat=>{
      for(let c of cat){
        this.categoryList.push({value:c.payload.doc.id,category:c.payload.doc.data()('category')})
        // console.log(c.payload.doc.id)
        // console.log(c.payload.doc.data().category)
      }
    })
    console.log(this.categoryList)
  }
  private ResInit() {
    this.ResolutionFormGroup = new FormGroup({})
    this.ResolutionFormGroup.registerControl('id',(this.UserIdControl = new FormControl('',(Validators.required))))
    this.ResolutionFormGroup.registerControl('category_id',(this.CategoryControl = new FormControl('',(Validators.required))))
    this.ResolutionFormGroup.registerControl('title',(this.TitleControl = new FormControl('',(Validators.required))))
    this.ResolutionFormGroup.registerControl('description',(this.DescriptionControl = new FormControl('',(Validators.required))))

  }
  onNoClick(): void{
    this.dialog.close()
  }

}

new.module.ts (imports only):

imports: (
    CommonModule,
    ResolutionRoutingModule,
    MatFormFieldModule,
    ReactiveFormsModule,
    MatDialogModule,
    FormsModule,
    MatInputModule,
    MatSelectModule
  )

As far as I can tell, I have everything imported correctly, and the formGroup and formControl setup correctly.

javascript – Why Google Sheets Crud/ search form is not working if has more than 5 columns?

I am working on a simple app that will display search window and the results based on the range in google sheets.
However I have a problem that I cannot understand at all.

The code is working perfect till I have 5 columns to display (search.html). If it is more than 5 it is not displaying anything….

I have no idea why. If anyone could help me with that I would be grateful.

CRUD.gs

function loadMainForm(){

   const htmlServ = HtmlService.createTemplateFromFile("main");
   const html = htmlServ.evaluate();
   html.setWidth(950).setHeight(600)
   
   const ui = SpreadsheetApp.getUi();
         ui.showModalDialog(html, "データ検索");   
}

function createMenu(){
   const ui = SpreadsheetApp.getUi();
   const menu = ui.createMenu("データ検索");
  
  menu.addItem("検索フォームを開く", "loadMainForm");
  menu.addToUi();
}

function onOpen(){
  createMenu();
}

main.html

<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
   
   <style>
   .nav-link {
   cursor: pointer;
   }
   </style>

  </head>
  <body>
    
    <div class="container">
    
    <ul class="nav nav-tabs">
  <li class="nav-item">
    <div class="nav-link active" id="home-link">Home</div>
  </li>
  <li class="nav-item">
    <div class="nav-link"id="search-link">検索</div>
  </li>

</ul>

     <div id="app"></div>    
  <!-- Content here -->
</div>

    <!-- Option 1: jQuery and Bootstrap Bundle (includes Popper) -->
    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ho+j7jyWK8fNQe+A12Hb8AhRq26LrZ/JpcUGGOn+Y7RsweNrtN/tE3MoK7ZeZDyx" crossorigin="anonymous"></script>

    
  <script>
  
  var data;
  
  function loadView(options){
var id = typeof options.id === "undefined" ? "app" : options.id;
var cb = typeof options.callback === "undefined" ? function(){} : options.callback;

google.script.run.withSuccessHandler(function(html){
document.getElementById("app").innerHTML = html;
typeof options.params === "undefined" ? cb() : cb(options.params);
})(options.func)();
}

function setDataForSearch(){
google.script.run.withSuccessHandler(function(dataReturned){
data = dataReturned.slice();
}).getDataForSearch();
}

function search(){

var searchinput = document.getElementById("searchinput").value;
var resultsArray = data.filter(function(r){
//return r(1).indexOf(searchinput) !== -1;
return r(0).toString().toLowerCase().indexOf(searchinput.toString().toLowerCase()) !== -1;
});
var searchResultsBox = document.getElementById("searchResults");
var templateBox = document.getElementById("rowTemplate");
var template = templateBox.content;
searchResultsBox.innerHTML = "";
resultsArray.forEach(function(r){

var tr = template.cloneNode(true);
var hinmokuColumn = tr.querySelector(".hinmoku");
var buhinCodeuColumn = tr.querySelector(".buhinCode");
var buhinNameColumn = tr.querySelector(".buhinName");
var hitsuyoColumn = tr.querySelector(".hitsuyo");
var genkaColumn = tr.querySelector(".genka");
var kobaiColumn = tr.querySelector(".kobai");
var sagakuColumn = tr.querySelector(".sagaku");
var kenshoColumn = tr.querySelector(".kensho");

hinmokuColumn.textContent = r(0);
buhinCodeuColumn.textContent = r(1);
buhinNameColumn.textContent = r(2);
hitsuyoColumn.textContent = r(3);
genkaColumn.textContent = r(4);
kobaiColumn.textContent = r(5);
sagakuColumn.textContent = r(6);
kenshoColumn.textContent = r(7);

searchResultsBox.appendChild(tr);

});
}

function loadSearchView(){
loadView({func:"loadSearchView", callback: setDataForSearch});
}


document.getElementById("search-link").addEventListener("click",loadSearchView);


function inputEventHandler(e){
if (e.target.matches("#searchinput")){
  search();
}
}
document.getElementById("app").addEventListener("input",inputEventHandler);
    
    </script>
    
  </body>
</html>

search.html

<h1>検索</h1>

<div class="form-group">  
    <input type="text" class="form-control" id="searchinput" placeholder="検索">
 </div>

<table class="table table-hover">
  <thead>
    <tr>
      
      <th scope="col">品目</th>
      <th scope="col">部品コード</th>
      <th scope="col">部品名</th>
      <th scope="col">必要数</th>
      <th scope="col">原価</th>
      <th scope="col">購買原価</th>
      <th scope="col">差額1ヶ</th>
      <th scope="col">検証状況</th>

    </tr>
  </thead>
  <tbody id="searchResults">
  
  </tbody>
</table>


<template id="rowTemplate">
 <tr>
      
      <td class="hinmoku"></td>
      <td class="buhinCode"></td>
      <td class="buhinName""></td>
      <td class="hitsuyo"></td>
      <td class="genka"></td>
      <td class="kobai"></td>
      <td class="sagaku"></td>
      <td class="kensho"></td>

  </tr>
</template>

loadPartial.gs

function loadPartialHTML_(partial) {
  const htmlServ = HtmlService.createTemplateFromFile(partial);
  return htmlServ.evaluate().getContent();
  
}

function loadSearchView(){
 
   return loadPartialHTML_("search");
}

serverSideFuncs.gs

function getDataForSearch(){
    
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const ws = ss.getSheetByName("Array");
  
  return ws.getRange(2, 1, ws.getLastRow(),8).getValues();

}