Namespace’y javascript w widokach cakePHP

Czasem zdarza się tak, że w danym widoku potrzebuję bardzo specyficzną funkcję javascript. Na przykład w widoku invoices/add funkcja count_gros_value, która na podstawie wpisanej wartości w polu netto (net) i stawki var wyliczy wartość brutto (gros).

Załóżmy, że nie wiem jak napisać taką funkcję, żeby była elastyczna, po prostu we wnętrzu mam zaszyte id pól formularza:

function count_gros_value(){
   $("#Invoice[gros]").val( 
      parseFloat($("#Invoice[net]").val()) * 
      parseFloat($("#Invoice[vat]").val())
   );
}
// wywołanie
count_gros_value();

(nie sprawdzałem tego kodu, służy jako przykład i nie mam pojęcia czy dobrze działa).

Oprócz wielkiej nieelastyczności kodu (o czym teraz pisał nie będę) istnieje problem polegający na tym, że nie wiem czy jakiś inny programista nie zdefiniował funkcji count_gros_value gdzieś w layoucie. Oczywiście dostanę error jeśli tak jest, więc poprawię ją na my_count_gros_value() (sic!). Ok, wszystko działa. Jednak przychodzi inny programista i odczuwa potrzebę zdefiniowania funckji my_count_gros_value() w layoucie (no bo count_gros_value() już istnieje, a jego robi coś inaczej/lepiej więc trzeba ją zaimplementować). W ten sposób rozwala twój zaimplementowany fragment i nikt tego nie zauważa.

Jak tego uniknąć? Można zacząć nazywać takie “lokalne” funkcje invoices_index_count_gros_value() ale to zaczyna wyglądać nieestetycznie i aż się ciśnie na usta magiczne słowo “namespace”.

W Javascript nie ma przestrzeni nazw, ale możesz użyć obiektów:

var invoices = {
   index: {
      count_gros_value: function(){
         $("#Invoice[gros]").val( 
            parseFloat($("#Invoice[net]").val()) * 
            parseFloat($("#Invoice[vat]").val())
         );
      }
   }
}
// wywołanie
invoices.index.cunt_gros_value();

to jest już fajniejsze, ale ma dwa problemy:

  1. ciągłe definiowanie obiektów dla namespace’ów jest równie uciążliwe jak nazwa funkcji invoices_index_count…
  2. gdy ktoś przed nami zdefiniuje zmienną var invoices = “invoices”; to mu ją nadpiszemy

Dlatego proponuję taką funckję, która definiuje dowolnie głębokie obiekty udające namespace’y i do tego tworzy je wszytkie w zmiennej o pseudolosowej nazwie (w stylu: Namespace12883495048110.1425782083547854):

function Namespace(namespace_path){
	var w = window;

	window.namespaceFrameworkNamespaceName = 
		window.namespaceFrameworkNamespaceName || 
		"Namespace"+ new Date().getTime() + new Math.random();

	var arr  = (window.namespaceFrameworkNamespaceName + namespace_path).
						split(".");

	for(i in arr){
		w[ arr[i] ] = w[ arr[i] ] || {};
		w = w[ arr[i] ];
	}
	return w;
}

Ok, jakiś uparciuch mógłby się uprzeć, że ktoś może w aplikacji zmiennej ‘namespaceFrameworkNamespaceName’ – takiego problem nie rozwiązywałbym na poziomie kodu, ale personalnym (zwiałbym z projektu) ;)

tak wygląda definiowanie funkcji w namespace:

Namespace("app.users.index").check_form = function(){ 
	document.write("my_check_form()");
};

a tak jej wywołanie:

Namespace("app.users.index").check_form();
// lub gdy mamy zamiar wywołać wiele funkcji z danego namespace:
with(Namespace("app.users.index")){
 check_form();
}

Można by z tego zrobić tez fajne rozszerzenie jQuery,
$().nspc(“app.invoices.index”) (pisałem już o rozszerzaniu jQuery w tutorialu “longPolling”)

Share Button

Leave a Reply

Your email address will not be published. Required fields are marked *