christiansalazar avatar christiansalazar committed 23e8e32

demowidget commit 1

Comments (0)

Files changed (7)

+*.tmp
+.*
+*.swp
+<?php
+/**
+ 	esqueleto de un widget funcional.
+ 
+	puedes ver un ejemplo de uso en README.md
+
+	en su forma mas simple, un widget puede ser asi:
+
+		class MiWidget extends CWidget {
+			public function run(){
+				echo "hola";
+			}
+		}
+
+	pero como te has imaginado ya eso no sirve, para eso omites el widget
+	y simplemente dices: echo "hola"; y asunto resuelto..
+
+	un widget va mas alla, su funcion es unificar el funcionamiento de 
+	varias piezas de codigo:
+		assets
+		actions
+		html
+		css
+	con el objeto final de proveerle al usuario final y al programador
+	una herramienta -encapsulada-. (concepto de Encapsulamiento de la OOP).
+	
+	autor:
+		Christian Salazar H. <christiansalazarh@gmail.com>
+ */
+class DemoWidget extends CWidget {
+
+	// los atributos visibles del widget
+	//
+	public $id;					// un ID para el div mayor
+	public $etiquetaNombre;		// campo de ejemplo
+	public $etiquetaApellido;	// campo de ejemplo
+	public $action;				// usalo para que el widget llame un action
+	public $onSuccess;			// usalo para reportar situacion OK
+	public $onError;			// usalo para reportar un error
+
+	private $_baseUrl;			// usada internamente para los assets
+
+	public function init(){
+		parent::init();
+		// aqui se hace cualquier inicializacion extra
+	}
+
+	public function run(){
+		// este metodo sera ejecutado cuando el widget se inserta
+
+		// preparamos los assets.
+		// lo que se hace simplemente es obtener una entrada en el directorio
+		// de /tuapp/assets/ (ese directorio te lo da yii con el codigo que 
+		// veras dentro del metodo _prepararassets).
+		// luego, copiamos a ese directorio todos nuestros scrips o css
+		$this->_prepararAssets();
+
+		// renderizamos el contenido que el widget va a darnos
+		//
+		// toda la funcionalidad de JAVASCRIPT, CSS ha sido
+		// manejada dentro de componentes que estan en assets/
+		// por tanto el widget se limita a crear la estructura
+		// para usar todo eso junto y consistente.
+		echo 
+"
+<div id={$this->id} class='demowidget'>
+	<div class='row'>
+		<label>{$this->etiquetaNombre}</label>
+		<input type='text' name='nombre'>
+	</div>
+	<div class='row'>
+		<label>{$this->etiquetaApellido}</label>
+		<input type='text' name='apellido'>
+	</div>
+	<input type='button' value='OK' class='botonok'>
+</div>
+";
+
+		// preparamos algunas opciones para pasarselas al
+		// objeto javascript llamado DemoWidget que crearemos
+		// mas abajo.
+		$options = CJavaScript::encode(array(
+			'action'=>CHtml::normalizeUrl($this->action), // importante
+			'id'=>$this->id,
+			'onSuccess'=>new CJavaScriptExpression($this->onSuccess),
+			'onError'=>new CJavaScriptExpression($this->onError),
+			'botonokclassname'=>'botonok',
+		));
+
+		// insertamos el objeto Javascript DemoWidget, el cual reside
+		// en un archivo JS externo (en los assets).
+		// le pasamos las opciones a su constructor con el objeto de 
+		// comunicar las dos piezas.
+		Yii::app()->getClientScript()->registerScript("demowidget_corescript"
+				,"new DemoWidget({$options})");
+
+		// nada mas es requerido.
+	}// end run()
+
+	/*	este metodo tiene como proposito desplegar los assets
+		que estan en el directorio privado "maestro" del directorio
+		del widget, cuyo destino sera el directorio /assets de tu app.
+	*/
+	public function _prepararAssets(){
+		// queremos que el recurso CSS y JS que tenemos en extensions/demowidget/assets/ 
+		// pase a copiarse al directorio de assets/ de la aplicacion:
+		$localAssetsDir = dirname(__FILE__) . '/assets';
+		// baseUrl contendrá el directorio de assets de nuestra app,
+		// aparte de copiar todos los archivos alli presentes al nuevo destino.
+		$this->_baseUrl = Yii::app()->getAssetManager()->publish(
+				$localAssetsDir);
+		// requerimos jQuery ?
+		// pues solicitemos jQuery a YII, si ya estaba inserto 
+		// yii no lo duplicara.
+        $cs = Yii::app()->getClientScript();
+        $cs->registerCoreScript('jquery');
+		// aqui le indicamos a nuestro website en su parte HEAD
+		// que inserte los scripts y css que han sido publicados
+		// al directorio de assets: _baseUrl
+		foreach(scandir($localAssetsDir) as $f){
+			$_f = strtolower($f);
+			if(strstr($_f,".swp"))
+				continue;
+			if(strstr($_f,".js"))
+				$cs->registerScriptFile($this->_baseUrl."/".$_f);
+			if(strstr($_f,".css"))
+				$cs->registerCssFile($this->_baseUrl."/".$_f);
+		}
+		// en este punto deberia haber un directorio de assets/NNNNNN
+		// con una copia de todos los archivos dentro de ext/demowidget/assets/
+		// y this->_baseUrl tendrá la ruta de ese directorio de assets.
+	}
+}
+Un Widget de Demostración
+=========================
+
+por: Christian Salazar	<christiansalazarh@gmail.com>
+
+A continuación el ejemplo completo de cómo hacer un widget funcional que
+presente un formulario con dos campos, que maneje el javascript (jQuery) 
+necesario y que envie el resultado via ajax a un action de nuestro
+controller.
+
+El concepto principal tras un widget es EL ENCAPSULAMIENTO, es un concepto
+de la OOP (Programación Orientada a Objetos) el dicta que un objeto debe
+ocultar sus partes complicadas a quien lo utilice. En el caso del widget
+se trata de ocultar el "cómo se hace". Al programador no le interesa ver
+cómo las partes jquery, ajax, css, html se combinan para producir un 
+componente de UI, solo le interesa USAR el componente de UI.
+
+Aqui podrás conocer como conectar un widget a un Action, cómo notificar
+eventos onSuccess u onError. he sido lo suficientemente corto en el código
+para no opacar esta funcionalidad, minimizando el código a lo mas pequeño
+posible.
+
+Para usar este widget en cualquier parte solo debes hacer lo siguiente:
+
+	```
+	<?php
+		//	un simple widget de demostracion que
+		//  pide un nombre un apellido y los lanza
+		//  a un action via Ajax.
+		//
+		$this->widget('ext.demowidget.DemoWidget'
+		,array(
+			'id'=>'demowidget1',
+			'etiquetaNombre'=>'Nombre:',
+			'etiquetaApellido'=>'Apellido:',
+			'action'=>array('site/demowidgetpost'),
+			'onSuccess'=>"function(respuesta){ $('#logger').html(respuesta); }",
+			'onError'=>"function(texto){ $('#logger').html('error: '+texto);  }",
+		));
+	?>	
+	<div id='logger'>..un logger..</div>
+	```
+
+lo cual proveerá el siguiente widget:
+
+![Demo Widget](https://bitbucket.org/christiansalazarh/ejemplowidget/downloads/demowidget-screenshot.png "Demo Widget")

assets/demowidget.css

+div.demowidget {
+	border: 1px dotted #eee;
+	display: block;
+	box-shadow: 3px 3px 10px #aaa;
+}
+
+div.demowidget .row {
+	border: 1px dotted #eee;
+	padding: 3px;
+	display: block;
+}
+
+div.demowidget label {
+	color: magenta;
+}
+
+div.demowidget input {
+	color: blue;
+}

assets/demowidget.js

+// el codigo JS del widget
+//
+var DemoWidget = function(options){
+
+	var divMayor = $('#'+options.id);
+	
+	// esta funcion envia por ajax un comando POST asincrono
+	// 
+	var ajaxcmd = function(action,postdata,callback){
+		var result=false;
+		var nocache=function(){
+			var dateObject = new Date();
+			var uuid = dateObject.getTime();
+			return "&nocache="+uuid;
+		}
+		jQuery.ajax({
+			url: action+nocache(),
+			type: 'post',
+			async: false,
+			contentType: "application/json",
+			data: postdata,
+			success: function(data, textStatus, jqXHR){
+				result = data;
+				if(callback != null)
+					callback(true, data);
+			},
+			error: function(jqXHR, textStatus, errorThrown){
+				callback(false, jqXHR.responseText);
+				return false;
+			},
+		});
+		return result;
+	}
+
+
+
+	// codigo que se ejecuta
+	// cuando el widget inicia:
+	// preparamos los controles:
+	$('#'+options.id).find('input.'+options.botonokclassname).click(function(){
+		
+		// solo es un demo, tomamos los valores y los mandamos
+		// via ajax al action dado en options, 
+	
+		// divMayor es definido arriba en la clase JS 
+		//	representa al DIV que contiene todo
+		//
+		var nom = divMayor.find("[name|='nombre']").val();
+		var ape = divMayor.find("[name|='apellido']").val();
+		//alert(nom+' '+ape);
+
+		// la magia de Ajax, al pasarle un array con valores,
+		// este los convierte a tiras de datos que yii puede usar
+		// para validar, ejemplo, el action recibirá algo como:
+		//
+		//	"nombre=hola&apellido=pepe"
+		//	
+		var datosPost = { 'nombre': nom, 'apellido': ape };
+
+		// enviamos la cosa al action via ajax
+		ajaxcmd(options.action, datosPost, function(ok, respuesta){
+			if(ok == true){
+				options.onSuccess(respuesta);
+			}else{
+				options.onError(respuesta);
+			}	
+		});	
+	});
+
+};
Add a comment to this file

demowidget-screenshot.png

Added
New image
+<h1>Prueba de Widget</h1>
+
+<p>Este widget pedira dos textos, nombre y apellido, y al hacer click en OK los enviara via ajax a un action.</p>
+
+<?php
+	//	un simple widget de demostracion que
+	//  pide un nombre un apellido y los lanza
+	//  a un action via Ajax.
+	//
+	$this->widget('ext.demowidget.DemoWidget'
+	,array(
+		'id'=>'demowidget1',
+		'etiquetaNombre'=>'Nombre:',
+		'etiquetaApellido'=>'Apellido:',
+		'action'=>array('site/demowidgetpost'),
+		'onSuccess'=>"function(respuesta){ $('#logger').html(respuesta); }",
+		'onError'=>"function(texto){ $('#logger').html('error: '+texto);  }",
+	));
+?>
+
+<div id='logger'>..un logger..</div>
+
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.