Skip to Content
Technical Articles
Author's profile photo Nabheet Madan

Simple issue of passing the context to the Formatter

Background

I was working on enhancing an existing Fiori application.  This application has some search fields and then a table displaying the results.  The requirement was simple, to add a button to download the table data. The standard table control provides us an example here (using 1.38.11)which I referenced and work was 99% done. This last one percent led to this blog post, read on!

The 1% issue

Before explaining the issue please check my project structure below along with sample formatter code. The formatter is added as dependency in controller as we normally does.

getStatus: function (value) {
  var resourceBundle = this.getView().getModel("i18n").getResourceBundle();                           
  switch (value) {                           
    case "A_1":                                 
      return resourceBundle.getText("A_1");                           
    case "A_2":                                 
      return resourceBundle.getText("A_2");                           
    case "A_3":                                 
      return resourceBundle.getText("A_3");                           
    case "A_4":                                 
      return resourceBundle.getText("A_4");                           
    default:                                 
      return value;                           
   }                    
}

As can be seen above my view table is using a custom formatter defined in a separate file. The data being read is from model and same formatting needs to be applied while downloading of data also. The current formatter when called via view can read my i18n resource model via below mentioned statement as accordingly replace or update the value.

this.getView().getModel("i18n").getResourceBundle();

The same formatter when called via controller method during export it failed. The this in this case refers to the control via which it is being called which is Column control inside ExportToCSV. So somehow in the sample code below for COL3 we need to pass the context of the controller so that it works.

var oExport = new Export({
				exportType: new  ExportTypeCSV({
				separatorChar: "\t" ,
				mimeType: "application/vnd.ms-excel" ,
				charset: "utf-8" ,
				fileExtension: "xls"
		           }),
				// Pass in the model created above
				models : this.getView().getModel("SGTIN"),
				// binding information for the rows aggregation
				rows : {
					path : "/"
				},
				// column definitions with column name and binding info for the content
				columns : [{
					name : "COL1",
					template : {
					 content: "{COL1}"
					}	
				},{
					name : "COL2",
					template : {
					content: {
                    				parts: [{path:'COL2'}],
                    				formatter : Formatter.getCol2
                				}		
					}	
				},{
					name : "COL3",
					template : {
					 content : {
						parts : [{path:'COL3'}],
						formatter :Formatter.getStatus ----> PROBLEM HERE
						}
					}		
				}]
			});
			// download exported file
			oExport.saveFile().catch(function(oError) {
				MessageBox.error("Error when downloading data. Browser might not be supported!\n\n" + oError);
			}).then(function() {
				oExport.destroy();
			});	

So how do we basically pass the current contexts to this inner function so that i18n is available, read on!

Resolution

One thing is clear we need to pass the current context while calling it from my Main controller, question that remains is how?

As always we googled and tried the solution mentioned at the link here where we add the current context to the method call as any array but it did not work

formatter :[Formatter.getStatus,this]

We tried using the parts also to pass the context but it expects a path to the current model?

parts : [{path:'COL3'},this],

So the worst case scenario for me was to copy paste the same code which is in the formatter to the controller but that will be off course stupid and breaking the DRY ( Do not repeat yourself) principle. I was kind of stuck and thought of discussing this with a very close friend Ashish Ahire about the situation I am in. As always he was back with a super quick solution as mentioned below

formatter :Formatter.getStatus.bind(this)

This worked like a charm, just using bind and passing the current context worked! I actually ended up reading in detail about Call(), Apply() and bind().  Apart from bind other two functions will also work. To summarize these methods basically execute the function with the passed on context as arguments, super powerful. Thanks Ashish!

What is next?

I documented this solution because I feel we might have many other developers facing the same issue. If we have any other alternative apart from this one feel free to pitch in, open to all ears. I think call, apply etc. needs a separate blog post to understand them in detail, hopefully will write it soon.

Assigned Tags

      3 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Mahesh Palavalli
      Mahesh Palavalli

      Nice one Nabheet Madan !

      If you want to do it in the xml view (latest ui5)

      <Button text="Press Me" press=".doSomething($controller)" />

      https://ui5.sap.com/#/topic/b0fb4de7364f4bcbb053a99aa645affe

      and there is one more way also using proxy, which is not really needed as we have the latest .bind()

      jQuery.proxy()

       

      BR,

      Mahesh

      Author's profile photo Nabheet Madan
      Nabheet Madan
      Blog Post Author

      Thanks Mahesh.. cool now we are covered from view as well as controller side:)

      Author's profile photo Joseph BERTHE
      Joseph BERTHE

      Hello,

      It is very interesting, thanks a lot.

      Just a question, why not simply put the view instance during the instantiation of the formatter object ? Like this, you store it in the whole formatter class...

      Regards,

      Joseph