/ Gists

Gists

On gists

CSS (how to write logical statement)

CSS CSS trick

solution.css #

/* https://stackoverflow.com/questions/2797091/css-and-and-or */

/* or || */
.registration_form_right input:not([type="radio"]):not([type="checkbox"]) {}

/* and && */
.registration_form_right input:not([type="radio"]), 
.registration_form_right input:not([type="checkbox"])

On gists

External links only CSS

CSS CSS trick

external-links.css #

/* https://davidwalsh.name/external-links-css */

/* long version */
a[href^="http://"]:not([href*="mysite.com"]),
a[href^="https://"]:not([href*="mysite.com"]), 
a[href^="//"]:not([href*="mysite.com"]), {
    
}
/* shorter version! */
a[href*="//"]:not([href*="mysite.com"]) {
    /* external link styles, use :before or :after if you want! */
}

On gists

Full width

CSS CSS trick

full.css #

.full-width {
	left: 50%;
	margin-left: -50vw;
	margin-right: -50vw;
	max-width: 100vw;
	position: relative;
	right: 50%;
	width: 100vw;
}


.full-width2 {
  width: 100vw;
  position: relative;
  left: 50%;
  transform: translateX(-50%);
  background: coral;
}

.full-width3 {
  /* max-width: 100vw; */
  width: 100vw;
  margin-left: 50%;
  transform: translateX(-50%);
  background: lime;
 
}

On gists

Store/Restore Request

Nette Nette-Tricks

Presenter.php #

<?php

	public function actionDefault($back = null) 
	{
		

		if ($back)
			$this->restoreRequest($back);

		if ($this->presenter->navigation->navItem['alias'] == 'allOpenCalls')
		{
			
			$parentId = $this->presenter->navigation->navItem['parent__navigation_id'];
			$nav = $this->navigation->getById($parentId);
			$children = $nav->getChildren();

			$this->config['navigation_id'] = array_keys($children);

		}


		$this->template->back = $this->storeRequest();

	}

On gists

Composition API - examples

Vue.js

composition.vue #

<template>
  <div>
    <h2 class="text-h4">{{ name }}</h2>
    <button @click="score++" class="btn">
      {{ score }} twice is {{ double }}
    </button>

    <hr />

    <button @click="score2++" class="btn">
      {{ score2 }} triple is {{ triple }}
    </button>

    <hr />
    <ul>
      <li v-for="data in dataList" :key="data.id">{{ data.name }}</li>
    </ul>

    <hr />

    <button v-if="!divVisible" @click="showDiv">Show div</button>
    <button v-if="divVisible" @click="hideDiv">Hide div</button>
    <div v-if="divVisible" class="bg-red-100">My super hidden div :)</div>

    <hr />
    <form @submit.prevent="submitForm">
      <input type="text" v-model="title" />
    </form>
  </div>
</template>

<script>
import { ref, computed, reactive, toRefs, onMounted } from 'vue'
import { data } from '@/frontapp/components/data.js'

export default {
  components: {},
  props: ['name'],

  // props, context nebo { emit } nebo { emit, slots }
  setup(props, { emit }) {
    console.log(props, 'our props')

    // data
    const score = ref(1)
    const dude = ref('Kcko')
    const dataList = ref(data)

    const state = reactive({
      score2: 12,
      triple: computed(() => state.score2 * 3),
      divVisible: false
    })

    // computed
    const double = computed(() => score.value * 2 + 7)
    const hello = computed(() => 'Hello, I am ' + dude.value)

    // methods
    const showDiv = () => {
      state.divVisible = true
    }
    const hideDiv = () => {
      state.divVisible = false
    }

    // v nadrazenem prvku / komponentne nebo  standardne -> view @new-list-coming="nejakaMetoda($event)"
    const title = ref('')
    const submitForm = () => {
      emit('new-list-coming', title.value)
    }

    onMounted(() => {
      console.log('Mounted yeah!')
    })

    return {
      score,
      double,
      hello,
      ...toRefs(state),
      dataList,
      showDiv,
      hideDiv,
      submitForm
    }
  }
}
</script>

On gists

ICal Event Parser

PHP

ICallParser.php #

<?php

/*
FORK from https://gist.github.com/edewaal97/344ec108931ac4c9e8ca7874b2db83cb
*/


class iCal
{
	/**
	 * @var string
	 */
	public $calendarIdent;

	/**
	 * @var string
	 */
	public $title;

	/**
	 * @var string
	 */
	public $description;

	/**
	 * @var array
	 */
	public $events = array();

	/**
	 * @var array
	 */
	protected $_eventsByDate;


	public function __construct($content = null, $calendarIdent = null)
	{

		$this->calendarIdent = $calendarIdent;
		if ($content) {
			$isUrl = strpos($content, 'http') === 0 && filter_var($content, FILTER_VALIDATE_URL);
			$isFile = strpos($content, "\n") === false && file_exists($content);
			if ($isUrl || $isFile) {
				$content = file_get_contents($content);
			}
			$this->parse($content);
		}

		
	}


	public function title()
	{
		return $this->summary;
	}

	public function description()
	{
		return $this->description;
	}

	public function events()
	{
		return $this->events;
	}

	public function eventsByDate()
	{
		if (! $this->_eventsByDate) {
			$this->_eventsByDate = array();
			$tmpEventsByDate = array();

			foreach ($this->events() as $event) {
				foreach ($event->occurrences() as $occurrence) {
					$date = date('Y-m-d', $occurrence);
					$newevent = clone $event;
					$newevent->fixOccurringDate($occurrence);
					// generate key for sorting
					$key = strtotime($newevent->dateStart);
					while(isset($tmpEventsByDate[$date][$key])) $key++;
					$tmpEventsByDate[$date][$key] = $newevent;
				}
			}

			// sort array
			ksort($tmpEventsByDate);
			foreach ($tmpEventsByDate as $date => $value) {
				ksort($value);
				$this->_eventsByDate[$date] = $value;
			}

			// prevent duplicates for edited dates in recurring events
			foreach ($this->_eventsByDate as $dateKey => $date) {
				foreach ($date as $event) {
					if(!empty($event->recurrenceId)) {
						$uid = $event->uid;

						foreach ($date as $eventKey => $eventValue) {
							if($eventValue->uid == $uid && (empty($eventValue->recurrenceId))) {
								unset($this->_eventsByDate[$dateKey][$eventKey]);
							}
						}

					}
				}
			}
		}

		return $this->_eventsByDate;
	}

	public function eventsByDateBetween($start, $end, int $limit=NULL)
	{
		if ((string) (int) $start !== (string) $start) {
			$start = strtotime($start);
		}
		$start = date('Y-m-d', $start);

		if ((string) (int) $end !== (string) $end) {
			$end = strtotime($end);
		}
		$end = date('Y-m-d', $end);

		$return = array();
		foreach ($this->eventsByDate() as $date => $events) {
			if ($start <= $date && $date < $end) {
				if(empty($limit) || count($return) <= $limit) {
					$return[$date] = $events;
				}
			}
			if(!empty($limit) && count($return) >= $limit){
				break;
			}
		}

		return $return;
	}

	public function eventsByDateSince($start, int $limit=NULL)
	{
		if ((string) (int) $start !== (string) $start) {
			$start = strtotime($start);
		}
		$start = date('Y-m-d', $start);

		$return = array();
		foreach ($this->eventsByDate() as $date => $events) {
			if ($start <= $date) {
				if(empty($limit) || count($return) <= $limit) {
					$return[$date] = $events;
				}
			}
			if(!empty($limit) && count($return) >= $limit){
				break;
			}
		}

		return $return;
	}

	public function eventsByDateUntil($end, int $limit=NULL)
	{
		if ((string) (int) $end !== (string) $end) {
			$end = strtotime($end);
		}

		$start = date('Y-m-d');
		$end = date('Y-m-d', $end);
		$return = array();
		foreach ($this->eventsByDate() as $date => $events) {
			if ($start <= $date && $end >= $date) {
				if(empty($limit) || count($return) <= $limit) {
					$return[$date] = $events;
				}
			}
			if(!empty($limit) && count($return) >= $limit){
				break;
			}
		}
		return $return;
	}

	public function parse($content)
	{
		$content = str_replace("\r\n ", '', $content);

		// Title
		preg_match('`^X-WR-CALNAME:(.*)$`m', $content, $m);
		$this->title = $m ? trim($m[1]) : null;

		// Description
		preg_match('`^X-WR-CALDESC:(.*)$`m', $content, $m);
		$this->description = $m ? trim($m[1]) : null;

		// Events
		preg_match_all('`BEGIN:VEVENT(.+)END:VEVENT`Us', $content, $m);
		foreach ($m[0] as $c) {
			$this->events[] = new iCalEvent($c, $this->calendarIdent);
		}

		return $this;
	}
}



<?php

namespace App\ServiceModule\Model;


class iCalEvent
{

	/**
	 * @var string
	 */
	public $calendarIdent;

	/**
	 * @var string
	 */
	public $uid;

	/**
	 * @var string
	 */
	public $summary;

	/**
	 * @var string
	 */
	public $description;

	/**
	 * @var string
	 */
	public $dateStart;

	/**
	 * @var string
	 */
	public $dateEnd;

	/**
	 * @var string
	 */
	public $recurrenceId;

	/**
	 * @var array
	 */
	public $exdate = array();

	/**
	 * @var \stdClass
	 */
	public $recurrence;

	/**
	 * @var string
	 */
	public $location;

	/**
	 * @var string
	 */
	public $status;

	/**
	 * @var string
	 */
	public $created;

	/**
	 * @var string
	 */
	public $updated;

	/**
	 * @var integer
	 */
	protected $_timeStart;

	/**
	 * @var integer
	 */
	protected $_timeEnd;

	/**
	 * @var integer
	 */
	protected $_recurrenceId;

	/**
	 * @var array
	 */
	protected $_occurrences;


	public function __construct($content = null, $calendarIdent = null)
	{
		if ($content) {
			$this->parse($content);
		}

		$this->calendarIdent = $calendarIdent;
	}


	public function summary()
	{
		return $this->summary;
	}

	public function title()
	{
		return $this->summary;
	}

	public function getEventUrl()
	{
		$eid = explode('@', $this->uid);
		$eid = base64_encode($eid[0] . ' ' . $this->calendarIdent);
		$eid = str_replace('=', '', $eid);

		return $eid;
	}

	public function getEventFullUrl()
	{
		return 'https://www.google.com/calendar/event?eid=' . $this->getEventUrl();
	}

	public function description()
	{
		return $this->description;
	}

	public function occurrences()
	{
		if (empty($this->_occurrences)) {
			$this->_occurrences = $this->_calculateOccurrences();
		}
		return $this->_occurrences;
	}

	public function duration()
	{
		// if ($this->_timeEnd) {
			return $this->_timeEnd - $this->_timeStart;
		// }
	}

	public function parse($content)
	{
		$content = str_replace("\r\n ", '', $content);

		// UID
		if (preg_match('`^UID:(.*)$`m', $content, $m))
			$this->uid = trim($m[1]);

		// Summary
		if (preg_match('`^SUMMARY:(.*)$`m', $content, $m))
			$this->summary = trim($m[1]);

		// Description
		if (preg_match('`^DESCRIPTION:(.*)$`m', $content, $m))
			$this->description = trim($m[1]);

		// Date start
		if (preg_match('`^DTSTART(?:;.+)?:([0-9]+(T[0-9]+Z?)?)`m', $content, $m)) {
			$this->_timeStart = strtotime($m[1]);
			$this->dateStart = date('Y-m-d H:i:s', $this->_timeStart);
		}

		// Date end
		if (preg_match('`^DTEND(?:;.+)?:([0-9]+(T[0-9]+Z?)?)`m', $content, $m)) {
			$this->_timeEnd = strtotime($m[1]);
			$this->dateEnd = date('Y-m-d H:i:s', $this->_timeEnd);
		}

		// Recurrence-Id
		if (preg_match('`^RECURRENCE-ID(?:;.+)?:([0-9]+(T[0-9]+Z?)?)`m', $content, $m)) {
			$this->_recurrenceId = strtotime($m[1]);
			$this->recurrenceId = date('Y-m-d H:i:s', $this->_recurrenceId);
		}

		// Exdate
		if (preg_match_all('`^EXDATE(;.+)?:([0-9]+(T[0-9]+Z?)?)`m', $content, $m)) {
			foreach ($m[2] as $dates) {
				$dates = explode(',', $dates);
				foreach ($dates as $d) {
					$this->exdate[] = date('Y-m-d', strtotime($d));
				}
			}
		}

		// Recurrence
		if (preg_match('`^RRULE:(.*)`m', $content, $m)) {
			$rules = (object) array();
			$rule = trim($m[1]);

			$rule = explode(';', $rule);
			foreach ($rule as $r) {
				list($key, $value) = explode('=', $r);
				$rules->{ strtolower($key) } = $value;
			}

			if (isset($rules->until)) {
				$rules->until = date('Y-m-d H:i:s', strtotime($rules->until));
			}
			if (isset($rules->count)) {
				$rules->count = intval($rules->count);
			}
			if (isset($rules->interval)) {
				$rules->interval = intval($rules->interval);
			}
			if (isset($rules->byday)) {
				$rules->byday = explode(',', $rules->byday);
			}

			// Avoid infinite recurrences
			if (! isset($rules->until) && ! isset($rules->count)) {
				$rules->count = 500;
			}

			$this->recurrence = $rules;
		}


		// Location
		if (preg_match('`^LOCATION:(.*)$`m', $content, $m))
			$this->location = trim($m[1]);

		// Status
		if (preg_match('`^STATUS:(.*)$`m', $content, $m))
			$this->status = trim($m[1]);


		// Created
		if (preg_match('`^CREATED:(.*)`m', $content, $m))
			$this->created = date('Y-m-d H:i:s', strtotime(trim($m[1])));

		// Updated
		if (preg_match('`^LAST-MODIFIED:(.*)`m', $content, $m))
			$this->updated = date('Y-m-d H:i:s', strtotime(trim($m[1])));

		return $this;
	}

	public function isRecurrent()
	{
		return ! empty($this->recurrence);
	}

	public function fixOccurringDate($timestamp)
	{
		if($timestamp != $this->_timeStart) {
			// calculate correct start & end date if not a repeating event
			$duration = $this->duration();

			// get date from occurrences
			$timestampCalc = new \DateTime();
			$timestampCalc->setTimestamp($timestamp);

			// make new startdate and start timestamp
			$startCalc = new \DateTime();
			$startCalc->setTimestamp($this->_timeStart);
			$startCalc->setDate($timestampCalc->format('Y'), $timestampCalc->format('m'), $timestampCalc->format('d'));
			$this->_timeStart = $startCalc->getTimestamp();
			$this->dateStart = date('Y-m-d H:i:s', $this->_timeStart);

			// calculate end date and time with duration of original event.
			$this->_timeEnd += - $this->_timeStart + $duration;
			$this->dateEnd = date('Y-m-d H:i:s', $this->_timeEnd);
		}
	}

	protected function _isExdate($date)
	{
		if ((string) (int) $date != $date) {
			$date = strtotime($date);
		}
		$date = date('Y-m-d', $date);

		return in_array($date, $this->exdate);
	}

	protected function _calculateOccurrences()
	{
		$occurrences = array($this->_timeStart);

		if ($this->isRecurrent())
		{
			$freq = $this->recurrence->freq;
			$count = isset($this->recurrence->count) ? $this->recurrence->count : null;
			$until = isset($this->recurrence->until) ? strtotime($this->recurrence->until) : null;

			$callbacks = array(
				'YEARLY' => '_nextYearlyOccurrence',
				'MONTHLY' => '_nextMonthlyOccurrence',
				'WEEKLY' => '_nextWeeklyOccurrence',
				'DAILY' => '_nextDailyOccurrence'
			);
			$callback = $callbacks[$freq];

			$offset = $this->_timeStart;
			$continue = $until ? ($offset < $until) : ($count > 1);
			while ($continue) {
				if(isset($occurrence)) {
					if (! $this->_isExdate($occurrence)) {
						$occurrences[] = $occurrence;
						$count--;
					}

				}
				$occurrence = $this->{$callback}($offset);

				$offset = $occurrence;

				$continue = $until ? ($offset < $until) : ($count > 1);
			}
		}

		if ($this->_isExdate($occurrences[0])) {
			unset($occurrences[0]);
			$occurrences = array_values($occurrences);
		}

		return $occurrences;
	}

	protected function _nextYearlyOccurrence($offset)
	{
		$interval = isset($this->recurrence->interval)
			? $this->recurrence->interval
			: 1;

		return strtotime("+{$interval} year", $offset);
	}

	protected function _nextMonthlyOccurrence($offset)
	{
		$dayname = array(
			'MO' => 'monday',
			'TU' => 'tuesday',
			'WE' => 'wednesday',
			'TH' => 'thursday',
			'FR' => 'friday',
			'SA' => 'saturday',
			'SU' => 'sunday'
		);

		$interval = isset($this->recurrence->interval)
			? $this->recurrence->interval
			: 1;

		// INTERVAL IS BY (COUNT)DAYNAME
		if(isset($this->recurrence->byday)){
			$dates = array();
			foreach ($this->recurrence->byday as $pattern) {
				$offsetDateTime = new \DateTime();
				$offsetDateTime->setTimestamp((int) $offset);

				preg_match('`([-]?\d+)?(MO|TU|WE|TH|FR|SA|SU)`m', $pattern, $m);
				$recurrenceOffset = (isset($m[1])) ? (int) $m[1] : 1;
				$recurrenceDay = strtr($m[2], $dayname);

				$forDateTime = clone $offsetDateTime;

				for (
					$month = (int) $offsetDateTime->format('Ym');
					$month <= date('Ym', strtotime('+' . $interval*12 . ' months'));
					$month = (int) $forDateTime->modify('+'.$interval.' months')->format('Ym')
				) {
					$yearMonth = $forDateTime->format('Y-m');
					$firstDay = new \DateTime('first '. $recurrenceDay . ' of ' . $yearMonth);
					$lastDay = new \DateTime('last '. $recurrenceDay . ' of ' . $yearMonth);

					$newDate = $firstDay;

					$daysInMonth = array();
					while ($newDate->getTimestamp() <= $lastDay->getTimestamp()) {
						$daysInMonth[] = $newDate->getTimestamp();
						$newDate->modify('next '. $recurrenceDay);
					}

					if($recurrenceOffset < 0) {
						$dates[] = $daysInMonth[count($daysInMonth) + $recurrenceOffset];
					} else {
						$dates[] = $daysInMonth[$recurrenceOffset - 1];
					}
				}
			}
			sort($dates);

			foreach ($dates as $date) {
				if ($date > $offset) {
					return $date;
				}
			}
		}

		// INTERVAL IS BY DAYNUMBER OF MONTH
		$bymonthday = isset($this->recurrence->bymonthday)
			? explode(',', $this->recurrence->bymonthday)
			: array(date('d', $offset));

		$start = strtotime(date('Y-m-01 H:i:s', $offset));

		$dates = array();
		foreach ($bymonthday as $day) {
			// this month
			$dates[] = strtotime(($day-1) . ' day', $start);

			// next 'interval' month
			$tmp = strtotime("+{$interval} month", $start);
			$time = strtotime(($day-1) . ' day', $tmp);
			if ((string) (int) date('d', $time) == (int) $day) {
				$dates[] = $time;
			}

			// 2x 'interval' month
			$interval *= 2;
			$tmp = strtotime("+{$interval} month", $start);
			$time = strtotime(($day-1) . ' day', $tmp);
			if ((string) (int) date('d', $time) === (int) $day) {
				$dates[] = $time;
			}
		}
		sort($dates);

		foreach ($dates as $date) {
			if ($date > $offset) {
				return $date;
			}
		}
	}

	protected function _nextWeeklyOccurrence($offset)
	{
		$interval = isset($this->recurrence->interval)
			? $this->recurrence->interval
			: 1;

		$byday = isset($this->recurrence->byday)
			? $this->recurrence->byday
			: array( substr(strtoupper(date('D', $offset)), 0, 2) );

		$start = date('l', $offset) !== 'Monday'
			? strtotime('last monday', $offset)
			: $offset;

		$daysname = array(
			'MO' => 'monday',
			'TU' => 'tuesday',
			'WE' => 'wednesday',
			'TH' => 'thursday',
			'FR' => 'friday',
			'SA' => 'saturday',
			'SU' => 'sunday',
		);

		$dates = array();
		foreach ($byday as $day) {
			$dayname = $daysname[$day];

			// this week
			$dates[] = strtotime($dayname, $start);

			// next 'interval' week
			$tmp = strtotime("+{$interval} week", $start);
			$time = strtotime($dayname, $tmp);
			$dates[] = $time;
		}
		sort($dates);

		foreach ($dates as $date) {
			if ($date > $offset) {
				return $date;
			}
		}
	}

	protected function _nextDailyOccurrence($offset)
	{
		$interval = isset($this->recurrence->interval)
			? $this->recurrence->interval
			: 1;

		return strtotime("+{$interval} day", $offset);
	}
}

On gists

Component - default + merged options

Vue.js

BcVideo.vue #

<template>
  <video-player v-bind="mergedVideoSettings" />
</template>

<script>
import VideoPlayer from './VideoPlayer.local.vue'
export default {
  props: {
    videoSettings: Object
  },
  components: {
    VideoPlayer
  },
  computed: {
    mergedVideoSettings() {
      return {
        ...this.defaultVideoSettings,
        ...this.videoSettings
      }
    }
  },
  data() {
    return {
      defaultVideoSettings: {
        mask: false,
        colors: 'var(--primaryColor)'
      }
    }
  },
  mounted() {}
}
</script>

<style lang="scss" scoped></style>

On gists

Konfiguracni objekty - neon - Nette

Nette Nette-Neon

article.md #

Konfigurační objekty v Nette

Tomáš Jacík   30. 1. 2017

Jak se poprat s předáním konfigurace službě z config.neon? A jak k tomu využít Nette DI?

Jak se to běžně dělává?

Mějme hypotetickou třídu InvoiceForm. Formulář má vlastní šablonu, nemůžeme tedy napsat pouhou továrnu, potřebujeme komponentu. A k ní továrnu. Navíc ale chceme formuláři předat z config.neon nějaké výchozí hodnoty. Jak by takový kód vypadal?

Formulář

declare(strict_types = 1);

namespace App\Forms;

use Nette\Application\UI\Control;
use Nette\Application\UI\Form;

final class InvoiceForm extends Control
{
    /**
     * @var array
     */
    private $config;

    public function __construct(array $config)
    {
        $this->config = $config;
    }

    protected function createComponentInvoiceForm(): Form
    {
        $form = new Form;

        $form->addText('maturity', 'Splatnost')
            ->setDefaultValue($this->config['defaultMaturity']);

        return $form;
    }

    public function render()
    {
        $this->getTemplate()->render(__DIR__ . '/InvoiceForm.latte');
    }
}

Továrna

declare(strict_types = 1);

namespace App\Forms;

final class InvoiceFormFactory
{
    /**
     * @var array
     */
    private $config;

    public function __construct(array $config)
    {
        $this->config = $config;
    }

    public function create(): InvoiceForm
    {
        return new InvoiceForm($this->config);
    }
}

Šablona

<form n:name="invoiceForm">
    {* naše vlastní vykreslení formuláře *}
</form>

V config.neon pak musíme továrnu formuláře zaregistrovat a předat jí potřebné parametry.

parameters:
    invoicing:
        defaultMaturity: 7
        pdfDirectory: %appDir%/../invoices

services:
    - App\Forms\InvoiceFormFactory( %invoicing% )

Jak to udělat lépe?

Místo běžného pole si na konfiguraci vytvoříme objekt, který bude konfiguraci našemu formuláři zprostředkovávat.

declare(strict_types = 1);

namespace App\Config;

use Nette\Utils\ArrayHash;

abstract class AbstractConfig extends ArrayHash
{
    public function __construct(array $arr)
    {
        foreach ($arr as $key => $value) {
            if (is_array($value)) {
                $this->$key = ArrayHash::from($value, TRUE);
            } else {
                $this->$key = $value;
            }
        }
    }
}

Z tohoto objektu pak podědíme konfiguraci pro náš formulář. Můžeme také do třídy přidat metody, které budou s naší konfigurací pracovat. Např. metodu getPdfPath(), kterou později využijeme v třídě na generování PDF. Konfigurační třída tedy není jen jednorázová, předáme její pomocí fakturační konfiguraci více službám.

declare(strict_types = 1);

namespace App\Config;

/**
 * @property int    $defaultMaturity
 * @property string $pdfDirectory
 */
final class InvoicingConfig extends AbstractConfig
{
    public function getPdfPath(int $invoiceId): string
    {
        return vsprintf('%s/%s.pdf', [$this->pdfDirectory, $invoiceId]);
    }
}

Nyní můžeme třídu InvoiceForm upravit, aby nového konfiguračního objektu využila. Všimněte si také metody setDefaultValue(). Díky ArrayHash nyní můžeme ke konfiguraci přistupovat jako k objektu.

declare(strict_types = 1);

namespace App\Forms;

use App\Config\InvoicingConfig;
use Nette\Application\UI\Control;
use Nette\Application\UI\Form;

final class InvoiceFormNew extends Control
{
    /**
     * @var InvoicingConfig
     */
    private $config;

    public function __construct(InvoicingConfig $config)
    {
        $this->config = $config;
    }

    protected function createComponentInvoiceForm(): Form
    {
        $form = new Form;

        $form->addText('maturity', 'Splatnost')
            ->setDefaultValue($this->config->defaultMaturity);

        return $form;
    }

    public function render()
    {
        $this->getTemplate()->render(__DIR__ . '/InvoiceForm.latte');
    }
}

Můžeme se také zbavit vlastní implementace továrny a nahradit ji interfacem. Nette nám pak továrnu vygeneruje samo.

declare(strict_types = 1);

namespace App\Forms;

interface InvoiceFormFactoryInterface
{
    public function create(): InvoiceForm;
}

Nakonec vše zaregistrujeme v config.neon. Všimněte si také absence sekce parameters.

services:
    - App\Config\InvoicingConfig({
        defaultMaturity: 7
        pdfDirectory: %appDir%/../invoices
    })
    - App\Forms\InvoiceFormFactoryInterface

Co na závěr? Tohle jistě není definitivní řešení. Dalo by se lecjak rozšířit. Např. dodělat validace hodnot vstupujících do konfiguračních objektů. Tím by však byl článek již moc složitý. Jeho pointou je ukázat alternativní přístup předávání konfigurace službám. A jak to ve svých aplikacích děláte vy?


On gists

v-model, Parent <-> Child 2 way databinding

Vue.js

1.vue #

<!--Emit + props-->

<!--PARENT-->
<template>
  <div>
    <h1>A1 - parent</h1>
    <input
      :value="time"
      @input="time = $event.target.value"
      class="border border-gray-400"
    />
    <b1 @on-parent="this.time = $event" :time="time" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      time: null
    }
  },
  methods: {},
  computed: {},
  mounted() {}
}
</script>



<!--CHILD-->
<template>
  <div>
    <h1>B1 - child</h1>
    <input
      :value="time"
      @input="sendData($event.target.value)"
      class="border border-gray-400"
    />
  </div>
</template>

<script>
export default {
  emits: ['on-parent'],
  props: ['time'],
  data() {
    return {}
  },
  methods: {
    sendData(val) {
      this.$emit('on-parent', val)
    }
  },
  computed: {},
  mounted() {}
}
</script>


On gists

Use Global Utility Methods For DRYer Code

Vue.js

utils.js #

// import store from '../store' <-- To access your Vuex store
import Vue from 'vue' // <-- used for vue-toastification

class Utils {
  // Copy a string to user's clipboard
  copyToClipboard(text) {
    let copyText = document.createElement('input')
    document.body.appendChild(copyText)
    copyText.value = text
    copyText.select()
    document.execCommand('copy')
    document.body.removeChild(copyText)

    // Show toast on copy success
    // (using the vue-toastification package here)
    Vue.$toast.success('Copied address to clipboard: ' + text, {
      position: 'top-right',
      timeout: 3000
    })
  }
}

export default new Utils()