Pierwsza aplikacja oparta o React JS z wykorzystaniem Drupala

React JS to biblioteka dla języka JavaScript służąca do tworzenia interaktywnych interfejsów użytkownika. W standardowym modelu MVC można powiedzieć, że zajmuje się tylko warstwą V, czyli widokiem. Jeśli zastanawiasz się czy da się możliwości tej biblioteki wykorzystać w drupalowym projekcie, to bez obaw – da się. W tym wpisie pokażę Ci prosty przykład jak połączyć Drupala z React JS.

Aplikacja w React JS dla Drupala

Największą zaletą tej biblioteki jest tworzenie wirtualnej struktury dokumentu. Oznacza to, że nie musimy przeładowywać całej strony, jeśli chcemy zaktualizować tylko jej część. React porównuje stan aplikacji i zmienia tylko te dane, które zostały przez nas zaktualizowane.

Tutorial

W tutorialu stworzymy prosty formularz kontaktowy pobierający dane z aplikacji opartej o CMS Drupal, renderujący je za pomocą ReactJS z wykorzystaniem komponentów Material-UI. Po wypełnieniu prześlemy dane z powrotem do naszej aplikacji.

Material-UI to zbiór komponentów dla biblioteki ReactJS. Warto z nich skorzystać, ponieważ są już ostylowane i mają wiele ciekawych opcji.

Moduł w Drupalu

Na początek tworzymy prosty moduł. Nazwałem go „react_demo”. W module korzystam z hook_menu, aby dodać podstronę:

function react_demo_menu() {
  $items['contact-form'] = array(
    'title' => 'Formularz kontaktowy',
    'page callback' => 'react_demo_create',
    'access callback' => TRUE,
    'type' => MENU_NORMAL_ITEM,
  );
}

oraz funkcję obsługującą:

function react_demo_create() {
  $tree = taxonomy_get_tree(2);
  drupal_add_js(array('react_demo' => 
    array(
      'tree' => $tree,
    )
  ), array('type' => 'setting'));
  drupal_add_js(drupal_get_path('module', 'react_demo') . '/dist/bundle.js', array(
    'group' => JS_THEME,
    'preprocess' => TRUE,
    'scope' => 'footer',
    'weight' => '999',
  ));
  return '<div id="root"></div>';
}

W funkcji tej najpierw pobieramy wartości z wcześniej utworzonego słownika o id 2. Słownik zawiera opcje, które wyświetlimy w jednym z pól formularza. Następnie dwukrotnie korzystamy z funkcji drupal_add_js. W pierwszym użyciu dodajemy zmienną $tree, dzięki temu w kodzie js będziemy mogli bezpośrednio odczytać dane. W drugim użyciu dodajemy plik bundle.js, który zawiera skompilowany kod jsx. Na końcu zwracamy kontener, który będzie zawierał nasz reactowy formularz.

Formularz w React JS

Teraz możemy już przejść do napisania naszego formularza. Potrzebnych będzie do tego kilka narzędzi:

  • biblioteka React JS,
  • komponenty Material-UI
  • kompilator, który zamieni kod JSX oraz ES6 na kod JS umożliwiający odczyt przez przeglądarki do naszego pliku bundle.js. (ja używam webpack + babel).

Wszystkie potrzebne narzędzia można zainstalować za pomocą Node Package Manager.

Na początek zajmiemy się naszą zmienną $tree. Aby odczytać ją w kodzie JS, należy skorzystać z obiektu Drupal oraz dodać ścieżkę podaną w module, czyli:

Drupal.settings.react_demo.tree

W zmiennej obecnie mamy obiekt. Aby zamienić ją na tablicę wykonujemy:

var treeObj = Drupal.settings.react_demo.tree
const tree = Object.keys(treeObj).map(key => treeObj[key]);

Na koniec exportuję stałą tree:

export default tree

Ten kod zapisałem w pliku tree.js, w folderze constants, w lokalizacji naszego modułu.

W następnym kroku stworzymy klasę App, która będzie renderowała nasz formularz. Na początek zaimportuję wszystkie potrzebne klasy i komponenty:

import React from 'react';
import getMuiTheme from 'material-ui/styles/getMuiTheme'
import { RadioButton, RadioButtonGroup } from 'material-ui/RadioButton'
import TextField from 'material-ui/TextField';
import RaisedButton from 'material-ui/RaisedButton'
import tree from '../constants/tree.js'

Oprócz samego Reacta oraz stałej tree, zaimportowałem komponenty Material-UI oraz funkcję getMuiTheme potrzebną do ostylowania tych komponentów.

Teraz utworzymy naszą klasę App wraz z formularzem. Formularz będzie posiadał cztery pola. Jedno typu radio oraz trzy typu textfield.

const App = React.createClass({
render: function() {
		return (
			<form>
				<div>
					<h3>Wybierz Rodzaj zapytania</h3>
					<RadioButtonGroup name="type" >
						{ tree.map((elem) =>(
							<RadioButton key={elem.tid} value={elem.name} label={elem.name} />
						))}
				    </RadioButtonGroup>
			    </div>
			    <div>
			    	<TextField name="e_mail" floatingLabelText="Adres e-mail"  />
			    	<TextField name="name" floatingLabelText="Imię i nazwisko" />
			    	<TextField name="phone" floatingLabelText="Numer telefonu" />
			    </div>
			    <RaisedButton type="submit" label="Wyślij" className="button-submit" />
			</form>
		)
	}
});

W powyższym przykładzie do stworzenia pól formularza użyłem wcześniej zaimportowanych komponentów. Więcej o ich możliwościach możesz poczytać w dokumentacji Material-UI. Na początek wyświetlamy opcje rodzaju zapytania z naszej stałej tree. Później trzy proste pola tekstowe i na końcu przycisk Wyślij. Aby nasze komponenty działały poprawnie, potrzebny jest jeszcze następujący kod:

getChildContext: function() {
	return {
		muiTheme: getMuiTheme()
	}
},

childContextTypes: {
	muiTheme: React.PropTypes.object
},

Wszystkie wartości będziemy przechowywać w stanie naszej aplikacji, a więc musimy ustawić wartości początkowe w metodzie getInitialState:

getInitialState: function() {
	return {
		allValues: {
			type: '',
			e_mail: '',
			name: '',
			phone: ''
		},
	}
},

Gdy mamy już wartości początkowe, możemy dodać metodę, która będzie nam aktualizowała stan po wprowadzeniu danych do pól:

valueChanged: function(e, v) {
	this.setState({
		allValues: {...this.state.allValues, [e.target.name]: v}
	})	
},

Metoda aktualizuje stan obiektu allValues w zależności od tego, które pola zostały zmienione. Teraz dodajmy odwołanie do pól w formularzu:

<form>
	<div>
		<h3>Wybierz Rodzaj zapytania</h3>
		<RadioButtonGroup name="type" onChange={this.valueChanged} >
			{ tree.map((elem) =>(
				<RadioButton key={elem.tid} value={elem.name} label={elem.name} />
			))}
	    </RadioButtonGroup>
    </div>
    <div>
    	<TextField name="e_mail" floatingLabelText="Adres e-mail" onChange={this.valueChanged} />
    	<TextField name="name" floatingLabelText="Imię i nazwisko" onChange={this.valueChanged} />
    	<TextField name="phone" floatingLabelText="Numer telefonu" onChange={this.valueChanged} />
    </div>
    <RaisedButton type="submit" label="Wyślij" className="button-submit" onClick={this.onSave} />
</form>

Wygląd formularza po kompilacji

react_demo

Przesłanie danych do Drupala

Dodałem również do naszego przycisku ,,Wyślij'' metodę onSave, która będzie wywoływana po zatwierdzeniu formularza. Zanim przejdziemy do napisania tej metody, musimy doinstalować jeszcze jedno narzędzie, mianowicie do przesyłania danych metodą POST. Ja korzystam z biblioteki SuperAgent, ale możesz skorzystać z preferowanej przez siebie.

Po zainstalowaniu biblioteki poprzez npm, importujemy ją do naszej aplikacji:

import superagent from 'superagent'

Teraz możemy już napisać naszą funkcję submitującą:

onSave: function(e) {
	e.preventDefault();
	let values = JSON.stringify(this.state.allValues)
	superagent
		.post('/react-demo-finish')
        .field('values', values)
        .end(function(err, res){
        	if (err || !res.ok) {
        		console.log(err); console.log(res);
		        alert('Bład podczas przesyłania danych');
		    } else {
	        	console.log(res)
		    }
        });
},

Na początek blokujemy standardowe przeładowanie oraz zapisujemy nasze dane w formacie JSON. Następnie za pomocą biblioteki superagent przesyłamy dane pod adres /react-demo-finish, gdzie zaraz napiszemy funkcję odbierającą dane w Drupalu. Jeśli coś pójdzie nie tak, wyświetlamy stosowny alert, a jeśli wszystko będzie dobrze, zapisujemy odpowiedź do konsoli.

Do naszego hook_menu dodajemy nowy url:

$items['react-demo-finish'] = array(
    'title' => 'Formularz kontaktowy',
    'page callback' => 'react_demo_finish',
    'access callback' => TRUE,
    'type' => MENU_NORMAL_ITEM,
  );

oraz funkcję obsługującą:

function react_demo_finish() {
  $values = json_decode($_POST['values']);
  drupal_json_output($values->name);
}

W funkcji odczytujemy dane z JSON-a i aby sprawdzić czy wszystko jest w porządku przesyłamy wartość pola name z powrotem do React JS. W konsoli powinno się pojawić imię wpisane podczas wypełniania formularza.

Podsumowanie

I to by było na tyle. Kilka prostych kroków i nasz serwis został przygotowany, aby wykorzystywać najnowsze technologie tworzenia stron i aplikacji internetowych. Mam nadzieję, że ten wpis przekona Cię do wykorzystania biblioteki React JS w swoim projekcie.

Potrzebujesz pomocy przy tworzeniu projektu?

Z chęcią pomożemy