SuggestInput (autocomplete) pro Nette 2.3.3

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Croc
Člen | 270
+
0
-

Zdravím,
chtěl bych se prosím zeptat ohledně SuggestInputu neboli Autocomplete pro Nette formuláře. Který používáte a hlavně který by byl kompatibilní s Nette 2.3.3.

Díky

Croc
Člen | 270
+
0
-

Děkuju za odkazy.

Spíše bych se přiklonil k Nextras (již používám DatePicker). Akorát nikde nemohu najít jak se s tím typeahead pracuje. Je někde nějaká ukázka či alespoň stručný návod?

Croc
Člen | 270
+
0
-

Vypátral jsem o jaký Typeahaed se jedná (Twitter typeahead). Akorát stále nevím, jak dostat potřebná data do addTypeahead:

Container::extensionMethod('addTypeahead', function(Container $container, $name, $label = NULL, $callback = NULL) {
    return $container[$name] = new Controls\Typeahead($label, $callback);
});

Editoval Croc (22. 6. 2015 19:45)

hrach
Člen | 1844
+
0
-

@Croc data vraci ten callback, ktery jako parametr bere to, co zadal uzivatel.

Croc
Člen | 270
+
0
-

To jo, ale právě nemám představu jak by měl vypadat. Předpokládám, že v PHP být nemůže (načítání na serveru) a JS se mi moc nezdá (s každou změnou znaku dotaz do DB). Jak tedy?

Unlink
Člen | 298
+
0
-

No callback ktorý bude získavať tie dáta napíšeš v php

$form->addTypeahead('name', 'Titulok', function($q) {
	return $this->daco->getItemsBy($q); //v $q je to čo užívateľ zadal
});

O zvyšok by sa mal postarať javascript ktorý tam už je.

Čo sa týka toho dotazu do db, to je na tebe, ako ten daný callback implementuješ.

Editoval Unlink (22. 6. 2015 21:29)

hrach
Člen | 1844
+
0
-

to je na tobe, jak dany js implementujes, ale vetsinou se po znacich js pta, s tim, ze zacne treba az po dvou. muzes implementovat, ze se js zacne ptat az po par ms odmlce od psani, etc. vetsinou ty js knihovny toto umeji.

Croc
Člen | 270
+
0
-

Takže třeba takto?

// Formulář:
 $form->addTypeahead('item', 'Název:', function($q) {
        return $this->utilsManager->getItemBy($q); });

// Příkaz do DB:
  public function getItemBy($q)
    {
        return $this->database->table(self::TABLE_ITEM)->select(self::ITEM_NAME)->where("".self::ITEM_NAME." LIKE ?", '%$q%');
    }

// V šabloně formuláře:
var countries = new Bloodhound({
  datumTokenizer: Bloodhound.tokenizers.whitespace,
  queryTokenizer: Bloodhound.tokenizers.whitespace,
  // url points to a json file that contains an array of country names, see
  // https://github.com/twitter/typeahead.js/blob/gh-pages/data/countries.json
  prefetch: '../data/item.json'
});

// passing in `null` for the `options` arguments will result in the default
// options being used
$('.typeahead').typeahead(null, {
  name: 'item',
  source: item
});

Pořád moc nerozumím, jak se data dostanou do JS.

Editoval Croc (23. 6. 2015 12:05)

Croc
Člen | 270
+
0
-

Pokud využiji klasickou variantu s polem v JS, ze kterého se data načtou, s tím problém samozřejmě nemám. Pokud bych chtěl využít variantu Prefetch s .json, tak pokud tomu odhaduji dobře, musím načtená data z DB uložit do .json (napsat si sám – JS to nemumí???), ze kterého se budou načítat dle výše uvedeného JS.

Bohužel Nextras nemá žádnou dokumentaci pro implementaci typeahead, tak nemám žádnou představu jak to tedy je…

Croc
Člen | 270
+
0
-

Aktuálně to mám následovně:

    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
    <script src="{$basePath}/js/typeahead.js"></script> // ten co je použitý v Nextras Demo

//formulář:
          $form->addTypeahead('states', 'Plemeno:', function($q) {
          return $this->utilsManager->getItemBy($q); });

//implementace getItemBy()

 public function getItemBy()
    {
return $this->database->table(self::TABLE_ITEM)->select(self::BREED_ITEM)->where("".self::ITEM_NAME." LIKE ?", "'%".$q."%'")->fetch();
    }

// JS init
jQuery(function($) {
    $('.typeahead').each(function() {
        $(this).typeahead({
            remote: {
                url: $(this).attr('data-typeahead-url'),
                wildcard: '__QUERY_PLACEHOLDER__'
            }
        });
    });
});

Stále nechápu (dle @hrach a @Unlink) jak z getItemBy() se můžou dostat data do JS při načtení stránky. Dle této impelentace to nefunguje a už si opravdu nevím rady :-( Prosím o pomoc

Editoval Croc (26. 6. 2015 20:10)

Unlink
Člen | 298
+
0
-

No máš pravdu, ten javascript akosi nefunguje ani v tom deme.

Možno by to chcelo skúsiť inú JS libku.

Dáta získaš jednoducho, každý takto pridaný field má atribút data-typeahead-url pomocou ktorého môžeš volať ten callback čo si tam predal, už len doriešiť tú js časť.

Croc
Člen | 270
+
0
-

Zkusím selectize jak psal @Oli. Koukal jsem na dema a pro moji potřebu vypadá použitelnější. Pak dám vědět jak se povedlo :)

Pavel Kravčík
Člen | 1205
+
0
-

@Croc: Teď jsem řešil něco podobného jako ty. Problém je ve verzích typeahead. Většina příkladů nefunguje v 0.10.5, ale jen v 0.11. Pokud však nahraješ 0.11 – často to „zprasí“ ostatní doplňky (mě např. Grido).

Místo toho jsem použil https://jqueryui.com/autocomplete a funguje to skvěle.

$('#dealer').autocomplete({
	lookup: [{ data: 1, value: 'Jednička' }],
	onSelect: function (suggestion) {
    	alert(suggestion.data);
    }
});
Croc
Člen | 270
+
0
-

Zdravím, zkouším rozchodit selectize dle @Oli, ale narazil jsem na problém. Asi dělám něco špatně. Chtěl bych použít variantu výběru jedné položky (Single Item Select).

Instalaci jsem provedl dle návodu

config.neon mám:

extensions:
    selectize: App\Form\Control\SelectizeExtension

selectize:
    mode: select # second mode is `select` for selection just 1 option
    valueField: id
    labelField: name
    searchField: name

Vytvořil jsem si zkušební pole a vytvořil formulář:

$arrayData = [
        0 => [
            "id" => 1,
            "name" => "Item One"],
        1 => [
            "id" => 2,
            "name" => "Item Two"],
        2 => [
            "id" => 3,
            "name" => "Item Three"],
        3 => [
            "id" => 4,
            "name" => "Item Four"]
    ];

        $form = new Form;
        $form->addSelectize('tags', 'Text:', $arrayData);
		//......

Výsledkem je toto

HTML kód daného objektu:

<select class="selectize form-control" data-options="{"mode":"select","create":true,"maxItems":null,"delimiter":"…,"valueField":"id","labelField":"name","searchField":"name"}" data-entity="[{"id":1,"name":"Item One"},{"id":2,"name":"Item Two"},{"id":3,"name":"Item Three"},{"id":4,"name":"Item Four"}]" name="tags">
    <optgroup label="0">
        ::before
        <option value="id">
            1
        </option>
        <option value="name">
            Item One
        </option>
    </optgroup>
    <optgroup label="1"></optgroup>
//......

</select>

Pokud použiju fullmode, zobrazí se pouze input bez nápovědy.

Nevíte kde by mohl být problém prosím?

Editoval Croc (10. 7. 2015 15:00)

Oli
Člen | 1215
+
0
-

Mam to asi zmatene popsany. Pro mod select pouzij normálně fetchPairs. Ten array v dokumentaci je pro mod full(i to by se mohlo pojmenovat nějak lip, ale zbytecnej bc).

Croc
Člen | 270
+
0
-

Taky me to napadlo, ale zatim jsem to nezkousel. Diky za tip. Ale stejne je tu problem, asi s js, nefunguje ten input. V mod full s vyse uvedeny polem nejde napoveda. S mod select semzobrazi pouze list bez moznosti psani…

EDIT:

Ted ještě koukám, že ve FF v konzoli je zobrazena chyba: TypeError: MicroPlugin is undefined

řádek MicroPlugin.mixin(Selectize);:

		//......
		self.initializePlugins(self.settings.plugins);
		self.setupCallbacks();
		self.setupTemplates();
		self.setup();
	};

	// mixins
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	MicroEvent.mixin(Selectize);
	MicroPlugin.mixin(Selectize); //tento řádek

	// methods
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	$.extend(Selectize.prototype, {
		//......

Nemůže to být tím?

Editoval Croc (10. 7. 2015 17:15)

Oli
Člen | 1215
+
0
-

Tím to nebude. Včera jsem si toho taky všiml, ale selectize normálně funguje. Mas nekde vložený ten soubor z client-site a nekde pod nim selectize()? Zkus si do toho souboru například nekam vložit alert, jestli se zavolá.

Croc
Člen | 270
+
0
-

v @layout.latte mám:


	{include content}
</div>
	{block scripts}

    <script src="//nette.github.io/resources/js/netteForms.min.js"></script>
    <script src="{$basePath}/js/live-form-validation.js"></script>
    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
    <script src="{$basePath}/js/bootstrap.js"></script>
    <script src="{$basePath}/js/bootstrap-datepicker.js"></script>
    <script src="{$basePath}/js/locales/bootstrap-datepicker.cs.min.js"></script>
    <script src="{$basePath}/js/datepicker.init.js"></script>

    <script src="{$basePath}/js/selectize.js"></script> // samotný selectize
    <script src="{$basePath}/js/selectize.init.js"></script> // soubor z client-side
	{/block}
</body>
</html>

Jak máš na mysli „nekde pod nim selectize()?“ ? To se musí ještě někde ručně volat?

Croc
Člen | 270
+
0
-

Pokud do souboru selectize.init.js vložím na jeho konec selectize();, v debuggu se mi zobrazí zpráva:

"missing selectize!"

Ale nejsem si vědom, že by mi tam něco chybělo… Nevíte někdo čím by to mohlo být prosím?

vojty
Člen | 19
+
0
-

kzk_cz napsal(a):

@Croc: Teď jsem řešil něco podobného jako ty. Problém je ve verzích typeahead. Většina příkladů nefunguje v 0.10.5, ale jen v 0.11. Pokud však nahraješ 0.11 – často to „zprasí“ ostatní doplňky (mě např. Grido).

Místo toho jsem použil https://jqueryui.com/autocomplete a funguje to skvěle.

$('#dealer').autocomplete({
	lookup: [{ data: 1, value: 'Jednička' }],
	onSelect: function (suggestion) {
    	alert(suggestion.data);
    }
});

Ahoj, jak si to vyřešil s tim gridem? Napsal sis vlastní implementaci 0.11 pro Grido?

Croc
Člen | 270
+
0
-

Pavel Kravčík napsal(a):

@Croc: Teď jsem řešil něco podobného jako ty. Problém je ve verzích typeahead. Většina příkladů nefunguje v 0.10.5, ale jen v 0.11. Pokud však nahraješ 0.11 – často to „zprasí“ ostatní doplňky (mě např. Grido).

Místo toho jsem použil https://jqueryui.com/autocomplete a funguje to skvěle.

$('#dealer').autocomplete({
	lookup: [{ data: 1, value: 'Jednička' }],
	onSelect: function (suggestion) {
    	alert(suggestion.data);
    }
});

Tak jsem nerozchodil ani ten selectize… Jsem kopito asi… Poslední nadějí je tedy autocomplete. Povedlo se mi rozchodit ten příklad z odkazu co udáváš, ale budu mít problém dostat tam data z DB. Mohl by jsi mě prosím trochu nasměrovat? Je nějaká možnost přiřadit ID k danému záznamu? Abych pak zpětně nemusel vyhledávat zadaný text pro ID položky v DB.

Autocomplete jsem používal bez Nette, ale s Nette nevím jak tam dostat ty data spolu s ID…

Díky moc

EDIT:
Tak mi to nedalo a zkusil jsem ještě ten selectize. Vypadá to, že jsem ho částečně rozchodil :-) Problém akorát je, že zde mám předvyplněnou první hodnotu z pole při načtení formuláře.

Editoval Croc (7. 8. 2015 20:43)

Oli
Člen | 1215
+
0
-

@Croc Pokud jsi v modu select, tak to je normální. To by se mělo chovat uplně stejně jako addSelect z nette. Jestli tam jsou nějaký rozdíly, tak to je bug.

Pokud to máš v modu full, tak to je divný, protože se mě to nikdy nestalo a proto si i myslím, že to máš v tom select modu.

Řešení je jednoduché, prostě tomu nastav setPromt('-- Vyberte --');

Btw. Pokud by jsi potřeboval ty data tahat dynamicky z databáze, tak to selectize taky umí. Je to ta sekce Remote Source — Github. Ten nette doplněk to by default neumí, nikdy jsem to zatím nepotřeboval. Pokud tě ale napadne nějaký univerzální řešení, jak to zanést do toho doplňku, tak to bude super!

Croc
Člen | 270
+
0
-

Přesně tak, používám mod select. Načítání dat z DB už mám úspěšně implementováno a tu defaultní hodnotu jsem vyřešil přesně jak píšeš:

        $form->addSelectize('value', 'Name:', $this->utilsManager->loadName())
            ->setPrompt('   Vyberte   ');

Takže super :) Díky všem za tipy a rady :)

Croc
Člen | 270
+
0
-

Ahoj,
potřeboval bych se zeptat na jednu věc. Jak použít addSelectize například ve 2 formulářích. V jednom formuláři bude mode selecet a v druhém full.

Jde mi o to, že volba módu se provádí v configu a nebo přímo na daném poli, ale to mi nefunguje:

$form->addSelectize('id_category', 'label.category', $this->utilsManager->loadCategory($this->translator->getLocale(), $this->item_category), ['mode' => 'select'])
            ->setRequired('validate.field_required')
            ->setPrompt('input.choose_category');

Pokud tedy odstraním mode: select z configu a nastavím ho dle kódu výše, stane se z toho klasický list.

Nevíte kde by mohl být problém?

Editoval Croc (4. 3. 2016 10:40)

Oli
Člen | 1215
+
0
-

Zkus https://github.com/…electize.php#L50. Ale nemelo by to na to mit vliv. Taky to tak pouzivam. Mozna zkus jeste

[
    'mode' => 'select',
    'class' => 'another-class'
]

Podivej se taky jestli nemas v js error.

Croc
Člen | 270
+
0
-

Díky za tip, setMode jsem taky předtím zkoušel, ale výsledek stejný. Něco je špatně, protože mi teď nejde ani to co mi předtím fungovalo (mode pouze v konfigu). JS ale žádnou chybu nehlásí… Mazal jsem cache a taky nepomohlo… Zkusím se v tom pohrabat

BTW Tohle se mi jednou už stalo (nakonec jsem musel nakopčit původní soubory ze zálohy). Sranda je, že byly beze změny, ale pak to začalo fungovat.

Oli
Člen | 1215
+
0
-

Jeste me napadlo, mám tam dost nešťastně ten js obalenej v selectize(). Tak jestli to voláš někde v tom js. Zkus si tam dát nějakej alert, jestli se tam dostaneš. Vím, že mě tohle taky jednou vypeklo, že jsem tu funkci zapomněl zavolat. Přilinkovat soubor totiž nestačí…

Croc
Člen | 270
+
0
-

Hm, teď už mi nefunguje selectize vůbec ani s původními soubory…

@layout.latte mám toto:

{**
 * @param string   $basePath web base path
 * @param array    $flashes  flash messages
 *}

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">

		<title>{ifset title}{include title|striptags} | {/ifset}</title>
		<link rel="shortcut icon" href="{$basePath}/favicon.ico">

	    <link rel="stylesheet" href="{$basePath}/css/bootstrap.css">

		<link rel="stylesheet" href="{$basePath}/css/datepicker3.css">

		<link rel="stylesheet" href="{$basePath}/css/selectize.bootstrap3.css">

		<link rel="stylesheet" href="{$basePath}/css/basic.css">
		<link rel="stylesheet" href="{$basePath}/css/dropzone.css">

		<link rel="stylesheet" href="{$basePath}/css/style.css">

		<link rel="stylesheet" href="{$basePath}/js/pictures/magnific-popup.css">


		{block head}{/block}
	</head>

	<body>

		{control menu}

        <div class="container">

			<div n:foreach="$flashes as $flash" n:class="flash, $flash->type">
				<div class="alert alert-danger" role="alert">
					{$flash->message}
				</div>
			</div>

			<br/><br/>

			{include content}

		</div>

		<br/><br/>
	    <footer class="footer" role="navigation">
	      <div class="container">
	        <p class="navbar-text pull-left"><small>© 2011<small></p>
	      </div>
	    </footer>

		{block scripts}

			<script src="{$basePath}/js/live-form-validation.js"></script>

		<!--<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>-->
			<script src="{$basePath}/js/jquery-1.9.1.min.js"></script>
		    <script src="{$basePath}/js/nette.ajax.js"></script>


		    <script src="{$basePath}/js/bootstrap.js"></script>

		    <script src="{$basePath}/js/bootstrap-datepicker.js"></script>
		    <script src="{$basePath}/js/locales/bootstrap-datepicker.cs.min.js"></script>
		    <script src="{$basePath}/js/datepicker.init.js"></script>

		<!--<script src="http://brianreavis.github.io/selectize.js/js/selectize.js"></script>-->
		    <script src="{$basePath}/js/selectize.js"></script>
		    <script src="{$basePath}/js/selectize.init.js"></script>

		    <script src="{$basePath}/js/moment.js"></script>
		    <script src="{$basePath}/js/MyValidation.js"></script>

	        <script src="{$basePath}/js/jquery.picture.min.js"></script>

		    <script src="{$basePath}/js/pictures/jquery.magnific-popup.js"></script>
	        <script src="{$basePath}/js/pictures/jquery.magnific-popup.min.js"></script>

			<script>
				$(function () {
					$.nette.init();
				});

			    $('.zoom-gallery').each(function() {
			        $(this).magnificPopup({
			            delegate: 'a',
			            type: 'image',
			            closeOnContentClick: false,
			            closeBtnInside: false,
			            mainClass: 'mfp-with-zoom mfp-img-mobile',
			            image: {
			                verticalFit: true,
			                titleSrc: function(item) {
			                    return item.el.attr('title') + ' &middot; <a class="image-source-link" href="'+item.el.attr('data-source')+'" target="_blank">image source</a>';
			                }
			            },
			            gallery: {
			                enabled: true
			            },
			            zoom: {
			                enabled: true,
			                duration: 300, // don't foget to change the duration also in CSS
			                opener: function(element) {
			                    return element.find('img');
			                }
			            }

			        });
			    });

			</script>
		{/block}
	</body>
</html>

Definice pole pro formulář:

$form->addSelectize('id_category', 'label.category', $this->utilsManager->loadCategory($this->translator->getLocale(), $this->item_category), ['mode' => 'select'])
            ->setAttribute('class', 'selectize-input')
            ->setRequired('validate.field_required')
            ->setPrompt('input.choose_category');

selectize.js je odtud: Git

selectize.init.js mám: Git

Selectize se mi tedy už chytá. ale je zobrazen špatně a nefunguje (nezobrazí se list a nefunguje typeahead).
Problém bude v načtení nastavení pro selectize: https://lh3.googleusercontent.com/…T%25255D.png

Editoval Croc (4. 3. 2016 13:11)

Oli
Člen | 1215
+
0
-

Nevidím tam nikde to selectize() v javascriptu. Další věcí je, že nastavuješ třídu selectize-input. To znamená, že by jsi to měl odchytit v js. Já to mám třeba takhle:

$.nette.ext({
	load: function() {
		selectize();

		selectize(undefined, '.author-selectize', function (options) {
			options.onOptionAdd = function(value) {
				$('.authorEmail').parent().parent().show();
			};
			return options;
		});
	}
});

Uznávám, že pořadí parametrů není optimální, ale vznikalo to genericky… :-)

Croc
Člen | 270
+
0
-

selectize() jsem tam měl při některých pokusech, ale asi špatně, protože to stejně nešlo.

Super, tohle pomohlo. Díky moc :-)