Cannot render TWO INSTANCES of an HTML tag from a Class

Hi all,

I have created recently one extension http://www.yiiframework.com/extension/egmap/ and I do not know why I cannot re-render two maps using a function called renderMap:




/**

	 * 

	 * Lazy Programmer's function to register the javascript needed and display HTML

	 * map container

	 * @param array $afterInit -javascript code to be rendered after init call

  	 * @param string $language -preferred language setting for the results

  	 * @param string $region -top level geographic domain 

  	 * @param ClientScript::CONSTANT $position -where to render the script

	 */

  	public function renderMap( $afterInit=array(), $language = null, $region = null, $position = CClientScript::POS_LOAD ){

  		$this->registerMapScript( $afterInit, $language, $region, $position );

  		

  		ob_start();

                ob_implicit_flush(false);

        

  		echo $this->getContainer();

  		

               echo ob_get_clean();


  	}



here is the function getContainer




       /**

   	* returns the Html for the Google map container

   	* @param Array $options Style options of the HTML container

   	* @return string $container

   	* @since 2010-12-22 Yii modified Antonio Ramirez

   	*/

  	public function getContainer($styles=array(),$attributes=array())

  	{    	

    	$options = array_merge($this->_htmlOptions, array('id'=>$this->getContainerId()));

    	if(!isset($options['style'])) $options['style'] = '';

    	foreach($this->_styleOptions as $style=>$value)

    	{

    		$options['style'] .= $style.':'.$value.';';

    	}

    	

    	return CHtml::tag('div', $options);

  	}



The registration of the Javascript code works as expected, but when I try to render the container, it doesnt at all. In order to render two maps, I had to render them from the view and only use registerMapScript according to a map ID.

The object is not an extended Component nor a Widget. It is an object, that is all.

It just renders the first MAP, and if i put ob_clean() it renders the second.

What am I doing wrong?

Hey,

I’m not sure if this is the problem but:




ob_start();

ob_implicit_flush(false);

 

prevents any content to be echo’ed until you call


 ob_get_clean();

 

Also I didnt understand what you meant by

you mean if you use ob_clean instead of ob_get_clean ?

Yeah I know it prevents any code to be echo’ed, and I did that before the layer is rendered, then the first MAP I try to render works, but the second time I try to render another Map with a different object it doesnt.

If I use ob_clean(); at the end, it does return the second Map, so I know I am doing something really wrong in between.

Check the code for CBaseController:




  ob_start();

  ob_implicit_flush(false);

  $widget=$this->createWidget($className,$properties);

  $widget->run();

  return ob_get_clean();



Is it doing something different than me?

I cant see anything wrong with your code

Try the following :




$contents=ob_get_contents();

ob_end_clean();



Thank you very much Gustavo for your help, but still doesnt work… How should I echo? after the ob_end_clean() ??

yeah, and that should work … I can’t see why its not working, very weird …

ob_end_clean should be the same as ob_clean ob_end… try it, as you said ob_clean worked before




$contents=ob_get_contents();

ob_clean();

ob_end();

echo $contents;



Doesnt work… I am open for suggestions… Do I have to create a widget instead?? I had the same problem with other extension I had and same thing… How do I render contents from a class more than once?

I think you should pay some attention to the Google Maps code. I once tried to render it in a partial with no luck. (Static version IIRC.)

/Tommy

Thanks for helping Tri.

The problem it is not within the JS code as I do have different INIT functions for different Objects. The classes work great IF I WRITE HTML CODE on the view manually. Lets say

<div id="map1"></div>

<div id="map2"></div>

Works great… but why I cannot render those layers from the class? nevertheless, I will double check with js code.

suggestions?

No, not really. I had a look at my previous experiment. Actually the static html-only call works just fine if rendered via multiple calls to renderPartial().

It was the GMap extension widget I couldn’t place in a partial view (separate id’s).

(I went on and tried the GMapMultiplot extension but it didn’t work at all for me, in a quick test.)

Downloaded your EGMap extension from the Yii site. No problem to display two maps in a page using your example code. Will have to try the ‘lazy’ function later.

So no more clues at the moment.

Edit:

Just want to add that I’m not the creator of the extensions mentioned, just tried using them yesterday (and last year). Parenthesis added to slightly irrelevant text about GMapMultiplot. BTW it probably works with Yii 1.0.

/Tommy

Hi tri, thanks for your help,

It seems it is happening in most of the extensions that we create. Check the following comment from my blog:

thank you very much for this great widget and for share your modifications on it :)

I really would like to fix these issues and at least write a wiki. I believe this pitfall is happening to a lot of people. I try to contact somebody from development team, but no one has answered to this question yet, or at least clarify what is the reason in order to code a workaround.

In the meantime I will keep reading the code of Yii but it seems that I do what is correct. I was wondering that if as a widget will work but as you can see on the comment above isnt working either.

If anybody could help on this, will also help the community to develop better extensions.

Thanks!

Found a solution to the problem but has nothing to do with the use ob_… At the end I had to find a Javascript solution and integrate it with the maps. Now you will be able to render as many Maps on the page as wanted with just a simple call: renderMap.

I will update the extension asap

Thanks all for your help.

In the meantime I checked your extension and found something… Maybe you already figured this, but it’s not bad to mention it :)

The DIV container is rendered wrongly… like <div … /> instead of <div …></div>

So in the getContainer the line


return CHtml::tag('div', $options);

should be


return CHtml::tag('div', $options,'',true);

And note that first parameter when creating a new object is the name of the init function… if you want two maps or more… those names MUST be different…

so this works for me (but only with the DIV error (above) fixed)




Yii::import('ext.gmaps.*');

$gMap = new EGMap('x1');

$gMap->setCenter(51.245475,6.821373);

$gMap->addMarker(new EGMapMarker(51.245475,6.821373));

$gMap->addMarker(new EGMapMarker(46.262248,6.115969));

$gMap->setZoom(4);

$gMap->renderMap();


$gMap2 = new EGMap('x2');

$gMap2->setCenter(21.245475,3.821373);

$gMap2->addMarker(new EGMapMarker(21.245475,3.821373));

$gMap2->addMarker(new EGMapMarker(46.262248,6.115969));

$gMap2->setZoom(4);

$gMap2->renderMap();



Hi mdmomba,

Thanks! Damn man… I already rewrote! It was that easy? The closing of a tag? Even though I fixed it through JS I will try that my self… gotta see this with my own eyes.

My solution is through Javascript (again)

This is why Iike so much this framework… its support is superb!

About the names, they are all controlled by an autogenerated Id with an static counter. So conflict names only occur by wrongly using setContainerId




        /**

	 * 

	 * Static counter for autogenerated Ids

	 * @var integer

	 */

	private static $_counter = 0;



Thanks all again for your help, thumbs up for all of you and + votes for great attitude

The counter is used for the container ID… I’m talking about the JS init function… that by default is “gmaps_initialize”

If you call like this




$gMap = new EGMap();

$gMap2 = new EGMap();



then only one "gmaps_initialize" is rendered… because both has the same name !!!

That’s why you need to cal like




$gMap = new EGMap('init1');

$gMap2 = new EGMap('init2');



So to get two JS function init1() and init2()…

Edit: One idea would be to add the same counter you use for the div to the init function… so that you get gmaps_initializeXX where XX is a number…

I tried the old code with the closing of the tag and it works… nevertheless, the issue made me think about a better approach and functionality, and I have already updated the code. Thank you very much mdomba, that wasn’t easy to catch. I see if the other widgets have the same problem.

EDIT:

I already fixed the INIT JS_FUNCTION before the post. :) It is all automated as the ID.

Hehe… we posted at the same time… microseconds appart ;)